• 台球游戏的核心算法和AI(2)



    前言:
      最近研究了box2dweb, 觉得自己编写Html5版台球游戏的时机已然成熟. 这也算是圆自己的一个愿望, 一个梦想.
      承接该序列的相关博文:
      • 台球游戏核心算法和AI(1) 
      同时结合html5的学习笔记:
      • box2dweb 学习笔记--sample讲解 
      这篇文章, 具体讲解台球游戏的box2d模型抽象, 并给出一个初步版本.

    演示:
      台球游戏的雏形如下所示:
      
      该台球游戏, 改编自box2dweb的demo程序, 可用鼠标拖动球来移动.
      代码的下载链接: http://pan.baidu.com/s/1sjzCwqD

    分析:
      让我们对台球游戏做个简单的物理抽象, 然后"庖丁解牛", 对每个组件结合box2d进行剖析.
      
      如图所示, 其抽象为6个球袋和6个边框构成, 球袋是球落入的目标, 边框则限定了台球的活动范围.
      • 边框抽象
      台球边框相对简单, 其可视为静态物体. 其物理形状就是一条边.

    // 设置为静态物体类型
    wallBodyDef.type = b2Body.b2_staticBody;
    
    // 采用多边形形状,然后SetAsEdge设置为边
    wallFixDef.shape = new b2PolygonShape;
    wallFixDef.shape.SetAsEdge(new b2Vec2(x1, y1), new b2Vec2(x2, y2));

      注: 边框转为box2d对象还是简单的.
      • 球袋抽象
      球袋本身也是静态物体, 但不同于边框, 其的box2d抽象, 多了点复杂和技巧.
      1). 感应设置
      球袋区域应为感应区, 球可以进入该区域, 但并不与之发生碰撞反应.
      可以通过设定定制器(Fixture)的isSensor属性为true来实现, 如下面代码所示:

    var holeFixtureDef = new b2FixtureDef;
    holeFixtureDef.shape = new b2CircleShape(0.5);
    holeFixtureDef.isSensor = true;

      注: 其特性为能感知碰撞不发生碰撞反应
      2). 落袋有效区域变换
      球袋和球的区域相交时, 并不代表球就进洞. 如下图所示:
      
      注: 红球刚好和球袋区域相交, 但红球重心并没有落入球袋的有效范围内.
      为了完美解决球进洞的逻辑判断, 我们有两种思路去解决.
      一种思路为: 从产生的碰撞接触对象b2Contact中, 计算两者的距离, 若两者圆心距离小于球袋半径, 则算进洞, 否则不算.
      另一种思路, 是做一个trick的技巧, 构造一个半径 = 球袋半径 - 球半径, 圆心依旧是球袋中心的圆, 并代替作为球袋的box2d物理模型. 该圆若与球相交, 则可以认为球重心落入球袋区域. 这可以免去前者的计算.
      
      注: 绿色的内部圆即是构造的球袋核心圆, 其外部的圆是物理表象的圆. 该场景为球和球袋相交, 但球重心和内部圆没有相交, 即重心没有落入球袋区域.
      环绕球袋本身的3/4圆, 则采用多边形来逼近模拟(样例采用16边形), 这也是防止球出有效区域(实际上这个可以忽略).
      • 球体放置
      我们都知道, 台球模拟, 最困难的往往是开球的时候. 一堆球挤在一起, 每个瞬间, 都有好多球彼此互相接触.
      球体的堆放其实是有技巧的, 摆放的球体不需要每个都紧挨着的, 可以适当的留些空隙. 如下所示:
      
      • 整体模拟
      由于采用垂直视角看台球桌面, 重力方向是指向内部. 创建世界对象时, 可简单设置gravity为零向量.

    var world = new b2World(new b2Vec2(0, 0), true)

      而台球桌面本身的摩擦阻力, 由于台球游戏在box2世界, 没有存在相关物理物体, 因此我们需要设置球的线速度减震来模拟台球桌摩擦阻力.

    ballBodyDef.linearDamping = 0.25;

      最终台球游戏整体的box2d物理模型, 对转换为如下图:
      
      • 进球处理
      球进球袋后, 需要消失, 可以理解为该球从box2d的物理世界中消除.
      对于碰撞反应, box2d提供了两种方式去处理.
      1). 注册ContactListener方式
      2). 遍历ContactList列表
      样例代码采用第二种方式, 原因如下:
      1). ContactListener的回调处于step的模拟过程中, box2d明确规定step模拟过程中, 不允许修改物理属性.
      2). 由于台球游戏的物体个数并不多, 因此遍历ContactList列表其性能是可接受的.

            /* 清除落入袋中球 */
            var contactList = world.GetContactList();
            for ( var contact = contactList; contact; contact = contact.GetNext() ) {
                if ( !contact.IsTouching() ) {   /* 接触只代表AABB重合 但不代表形体碰撞 */
                    continue;
                }
                var b1 = contact.GetFixtureA().GetBody();
                var b2 = contact.GetFixtureB().GetBody();
    
                if (b1.GetUserData() && b2.GetUserData()) {
                    if (b1.GetUserData() === BALL_TYPE.BG_HOLE_TYPE && b2.GetUserData() === BALL_TYPE.BG_BALL_TYPE ) {
                        world.DestroyBody(b2);
                    }
                    if ( b2.GetUserData() === BALL_TYPE.BG_HOLE_TYPE && b1.GetUserData() === BALL_TYPE.BG_BALL_TYPE ) {
                        world.DestroyBody(b1);
                    }
                }
            }

      注: 该处理代码在world.Step调用之后进行.

    总结:
      这边的demo图形是借助box2d的DrawDebug来渲染的. 下一步计划用漂亮的素材替换, 并完善台球的游戏规则. 虽然水平有限, 但感觉向前迈出了坚实的一步, 这种感觉挺好的.

    写在最后:
      
    如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.

       

  • 相关阅读:
    idea设置全局ignore
    win 2012 安装mysql 5.7.20 及报错 This application requires Visual Studio 2013 Redistributable. Please ins
    win 2012 安装mysql 5.7.20 及报错 This application requires Visual Studio 2013 Redistr
    kafka 删除 topic
    java编译中出现了Exception in thread “main" java.lang.UnsupportedClassVersionError
    Centos中使用yum安装java时,没有jps的问题的解决
    Spring 整合Junit
    Spring纯注解配置
    Spring 基于注解的 IOC 配置
    打印java系统的信息
  • 原文地址:https://www.cnblogs.com/mumuxinfei/p/4502768.html
Copyright © 2020-2023  润新知