目录

1.前言

 

2.弹珠迷宫实现

2.1. 创建项目

2.2. 创建RollingBall类

2.3. 创建物理世界

2.4. DebugDraw

2.5. 添加刚体弹珠

2.6. 创建物理边界

2.7. 创建迷宫地图

2.8. 开启重力感应

 

写在后面

 

前言

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

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

 

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

 

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

 

弹珠迷宫实现

创建项目

硬件环境: MacOS X 10.9.1

开发工具:Xcode5, VetexHelper

引擎版本:

 

以上是我的开发环境,你大可根据你的开发环境做相应操作。直接将路径 /cocos2dx-master/tools/project-creator 中的 create_project.py 拖到终端,然后会跳出对话框如下(这是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的时候却不得不做大量的工作去实现它,例如导入相关源文件、初始化绘制参数、调用绘制方法等等很多很繁琐的步骤。同样有个小笔记《》可供想要了解的朋友参考。

 

关闭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);     } }

添加刚体弹珠

 

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

 

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

 
  1. auto spBall = Sprite::create("ball.png"); 

  2. spBall->setTag(0); 

  3. spBall->setPosition(Point(50,visibleSize.height-50)); 

  4. this->addChild(spBall); 

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

 

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

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

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

spBall->setPhysicsBody(body);

 

运行项目,会发现小球在不断坠落,证明成功为其添加了刚体属性。同前一张图片进行对比,由于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的碰撞盒子。此时当弹珠下落时就会和盒子边缘产生碰撞,而不会落处屏幕之外了。

 

创建迷宫地图

 

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

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

 

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

 

 

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

 

 

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

 

获取顶点数据:

 

首先点选右上方的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]; }

 

修改好,重新编译。此时生成的数据就会变成一下格式:

 

将数据复制到程序中,此处以迷宫地图上半部分为例:

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)  };

 

通过createEdgePolygon()创建多边形刚体,第一个参数为顶点数组名,第二个为数组元素个数,其余几句代码的理解可参考创建弹珠部分。

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);

开启重力感应

打开重力感应,当摇晃设备时动态刚体会根据重力左右偏移:

//open the gravity sensor layer->setAccelerometerEnabled(true);

 

写在后面

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