1

前言

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

看到这张图,不知道你会不会想到些什么?儿时的玩物,满满的童年的味道。那时候没有太多玩具,这些小玩意足以让我兴奋很久。有那么一段时光是“弹珠迷宫”陪我度过的,课后亦或放学的时候,总是捧着这么一个巴掌大小的塑料盒在掌心摇摇晃晃,小心翼翼的掌控着弹珠的轨迹。严肃认真,又夹杂着紧张与激动的心情,也许这就是童真吧。

好了,赶紧把思绪扯回来。偶然想起这么一个小玩具,又买不到实物,怎么办呢?果断自己写一个装到爪机里,走哪玩哪。

教程中将详细介绍如何使用cocos2d-x搭配box2d物理引擎以及重力感应来模拟弹珠迷宫的效果。

弹珠迷宫实现

创建项目

  • 硬件环境: MacOS X 10.9.1
  • 开发工具:Xcode5, VetexHelper
  • 引擎版本:Cocos2d-x-3.0beta

以上是我的开发环境,你大可根据你的开发环境做相应操作。直接将路径 /cocos2dx-master/tools/project-creator 中的 create_project.py 拖到终端,然后会跳出对话框如下(这是3.0之后才有的,使用之前版本的小伙伴请参考 cocos2d-x环境搭建(基于win7以及mac)

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

填写好相应的信息,项目名称、包名、项目存放路径以及开发语言后点击下方的 create 按钮。创建成功之后,去你的路径打开你的项目进行接下来的操作。

创建RollingBall类

个人的怪癖,不喜欢在项目自带的 HelloWorldScene 文件上进行修改,所以新添文件 “RollingBall.h” 和 “RollingBall.cpp”。当然了,你大可直接在HelloWorldScene.h和HelloWorldScene.cpp做相应修改。

头文件中声明了DebugDraw的开关控制方法btnDebugDrawCallback,物理世界的创建方法setPhyWorld,物理世界的布局方法demoLayout等等。完整的源码放在GitHub仓库中供参考。

    //turn on or off the DebugDraw
    void btnDebugDrawCallback(Object* pSender);
    //create a physics world
    void setPhyWorld(PhysicsWorld* world){_world = world;}
    //the layout of the physics world
    void demoLayout();

在“AppDelegate.cpp”中引入头文件,并在applicationDidFinishLaunching()方法中作如下修改:

    auto scene = HelloWorld::createScene();

改为

    auto scene = RollingBall::createScene();

创建物理世界

cocos2d-x-3.0中对物理系统进行了封装,开发过程中可不用再纠结与box2d和chipmunk的接口。Physics integration大大方便了物理系统的使用,有兴趣的话可以去看看这篇文章.

言归正传,使用物理效果必然得有一个物理世界。在box2d中需要我们做如下操作,先设定一个重力系数,然后根据这个重力系数创建一个世界:

    b2Vec2 gravity=b2Vec2(0.0f, -30.0f);
    b2World* m_world=new b2World(gravity);

但现在,我们可以通过createWithPhysics()方法创建一个带有物理效果的Scene,然后将需要添加物理效果的层加入其中:

    auto scene = Scene::createWithPhysics();
    auto layer = RollingBall::create();
    layer->setPhyWorld(scene->getPhysicsWorld());
    scene->addChild(layer);

DebugDraw

开启DebugDraw

DebugDraw对需要使用物理系统的我们来说是个很有用的方法。它可将碰撞体的形状、关节等等全部绘制出来,方便我们观察物体及整个场景的可碰撞区域。

    //choose whitch part need to draw, Joint, Shape, Contact, None or All
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);

你可以修改setDebugDrawMask(int mask)方法的参数mask,来开启相应的绘制,本示例中开启了绘制所有可碰撞体。

不必太惊讶,仅仅只需要这么一行代码就可以打开DebugDraw。

然而,之前在cocos2d-x-2.x版本中使用box2d的时候却不得不做大量的工作去实现它,例如导入相关源文件、初始化绘制参数、调用绘制方法等等很多很繁琐的步骤。同样有个小笔记《cocos2d-x中使用DebugDraw提高box2d开发效率》可供想要了解的朋友参考。

关闭DebugDraw

通过

    _world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_NONE)

即可关闭DebugDraw。

为了方便切换,我在程序中添加了一个按钮,参加代码中的toggleOfDebugDraw()方法,当按钮被触发即调用相应的回调方法btnDebugDrawCallback(),开启或关闭 DebugDraw。

    void RollingBall::btnDebugDrawCallback(Object* pSender)
    {
        if(_world->getDebugDrawMask() != PhysicsWorld::DEBUGDRAW_NONE) {
        _world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_NONE);
        } 
        else {
        _world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
        }
    }

添加刚体弹珠

现在为我们的物理世界添加一个圆形刚体:

首先创建一个精灵,并添加到场景中:

    auto spBall = Sprite::create("ball.png");
    spBall->setTag(0);
    spBall->setPosition(Point(50,visibleSize.height-50));
    this->addChild(spBall);

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

这是简单的创建一个精力,弹珠现在并没有被赋予刚体属性。

接下来为弹珠添加刚体属性:首先定义一个刚体body,并为其创建了一个半径为弹珠宽度1/2的圆形碰撞体。

    auto body = PhysicsBody::createCircle(spBall->getContentSize().width / 2);

然后我们将此 body 加在精灵 spspBall 上:

    spBall->setPhysicsBody(body);

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

运行项目,会发现小球在不断坠落,证明成功为其添加了刚体属性。同前一张图片进行对比,由于DebugDraw的作用,弹珠被绘制有红色边框。

创建物理边界

程序运行后,上面所创建的弹珠会受到重力的作用不断的下落,以至于会落置显示屏之外。但是这并非我们所需要的效果,大多时候我们都希望所创建的刚体能活动在屏幕的可见范围内。那么这个时候我们就需要为我们的场景添加一个 EdgeBox。

    Size visibleSize = Director::getInstance()->getVisibleSize();
    auto body = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);
    auto edgeNode = Node::create();
    edgeNode->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
    edgeNode->setPhysicsBody(body);
    scene->addChild(edgeNode);

上述代码通过createEdgeBox方法创建了一个使用默认材质,大小为visibleSize,且边框宽带为3的碰撞盒子。此时当弹珠下落时就会和盒子边缘产生碰撞,而不会落处屏幕之外了。

创建迷宫地图

在上面的代码中我们创建了圆形刚体弹珠和矩形的碰撞盒子,这些都是规则的形状,但是如下图所示的迷宫地图是不规则的。那么如何创建不规则的刚体呢?

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

那肯定就是创建多边形刚体了。但是问题又来了,我们怎么才能知道多边形的顶点数据呢?接下来将解答这个问题。

这里需要借助一个外部工具VertexHelper,网上很多提供源码下载的地方,下载后用Xcode自行编译成可执行文件,打开这个工具:

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

将事先准备好的迷宫地图(注意我的图片分辨率:480*320,图片是多大,分辨率就是多少)拖拽到深灰色区域:

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

注意:右边的红色边框里将显示我们勾选的顶点的坐标数据;左边的红色框中则是一些配置信息(我的配置同图中所示),分别是选择物理系统类型,还有顶点数据的显示方式,以及顶点数组的名称。

获取顶点数据:

利用Cocos2dx-3.0新物理特性模拟弹珠迷宫

首先点选右上方的Edit Mode,然后给地图绘制边框(我把地图分为了两个部分,上部即绿色区域)。
绘制完之后将会在右下方的数据区域显示顶点数组,将数据复制到代码中,并将默认生成的数组的数据类型CGPoint和顶点的数据类型cpv均改为2dx支持的Point类型。顶点数据少的话倒是修改方便,如果数据很多这样去修改确实就太麻烦了点,下面提供一个一劳永逸的方法—修改VertexHelper源码:

打开VertexDocument.m文件,将updateResultTextField中我注释部分改为下方的代码:

    case VHTYPE_CHIPMUNK:
    //itemString = [NSString stringWithFormat:@"cpv(%.1ff, %.1ff)", point.x, point.y];
    itemString = [NSString stringWithFormat:@"Point(%.1ff, %.1ff)", point.x, point.y];
    case VHSTYLE_INIT:
    if (p == 0) 
    {
        //result = [result stringByAppendingFormat:@"CGPoint %@[] = {\n", variableName];
        result = [result stringByAppendingFormat:@"Point %@[] = {\n", variableName];
    }<pre><code>修改好,重新编译。此时生成的数据就会变成一下格式:

![利用Cocos2dx-3.0新物理特性模拟弹珠迷宫][9]

将数据复制到程序中,此处以迷宫地图上半部分为例:
</code></pre>    Point verts1[] = {
    Point(-146.5f, 155.1f),
    Point(-146.5f, -87.6f),
    Point(-140.9f, -88.1f),
    Point(-140.8f, 155.5f),
    Point(162.8f, 154.6f),
    Point(162.9f, -27.7f),
    Point(12.0f, -29.0f),
    Point(12.0f, -33.9f),
    Point(167.6f, -34.6f),
    Point(168.7f, 154.4f),
    Point(235.0f, 155.1f),
    Point(235.3f, -91.6f),
    Point(238.8f, -93.2f),
    Point(239.8f, -91.5f),
    Point(239.1f, 159.2f),
    Point(-238.3f, 159.0f),
    Point(-238.7f, 155.0f),
    Point(-147.4f, 154.9f)
    };<pre><code>通过createEdgePolygon()创建多边形刚体,第一个参数为顶点数组名,第二个为数组元素个数,其余几句代码的理解可参考创建弹珠部分。
</code></pre>    auto spEdgePolygon1 = Sprite::create("bg1.png");
    spEdgePolygon1->setTag(1);
    auto borderUpper = PhysicsBody::createEdgePolygon(verts1,18);
    spEdgePolygon1->setPhysicsBody(borderUpper);
    spEdgePolygon1->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
    this->addChild(spEdgePolygon1);<pre><code>####开启重力感应
打开重力感应,当摇晃设备时动态刚体会根据重力左右偏移:
</code></pre>    //open the gravity sensor
    layer->setAccelerometerEnabled(true);

写在后面

至此,整个demo的创建已经完成。欢迎指正与交流。

感谢温暖的阳光,热气升腾的咖啡,最重要的是老爸的生日。感谢这一切带给我的好心情。


转载自:泰然 - Jack.Zha


JeOam
8.7k 声望166 粉丝

热爱互联网