怎样使用 Box2D 和 Cocos2D 制作类似fruitninja Ninja的游戏

下次自动登录
现在的位置:
& 综合 & 正文
在cocos2d-x里面使用BOX2D使用cocos2d-x和BOX2D来制作一个BreakOut(打砖块)游戏(一)
本教程基于子龙山人翻译的的教程,用引擎重写,加上我一些加工制作。教程中大多数文字图片都是原作者和翻译作者子龙山人,还有不少是我自己的理解和加工。感谢原作者的教程和子龙山人的翻译。本教程仅供学习交流之用,切勿进行商业传播。
子龙山人翻译的教程地址:
Iphone教程原文地址:
BOX2D这个引擎,是个很好的模拟真实物理世界的引擎。愤怒的小鸟就是基于这个引擎做的。想想愤怒的小鸟,是不是很有冲动来学习下使用这个引擎了呢。
本教程目的就是让你们熟悉在里面如何使用,所采用的例子就是制作一个简单的应用。里面有一个篮球,使篮球自己碰到墙壁反弹。
本教程假定你学过前面的教程,或者有同等的相关经验。
到最新的。然后解压。到其目录下的这里把这个这个文件复制出来。其实下载这么个文件我们就需要这个。然后估计有人会想,怎么到这里复制呢,那么不是有个工程么,其实,经过我实践,发现原来这个目录下的有些问题。所以到人家的示例工程里面把复制出来。
这里先介绍下世界的相关理论。(其实推荐看(英文)和(中文),不是很多,才页,看了就了解了的理论,再进行以下的理解就简单了
在我们开始之前,让我们先交待一下具体是如何运作的。
  你需要做的第一件事情就是,当使用来为创建一个对象的时候。这个对象管理物理仿真中的所有对象。
  一旦我们已经创建了这个对象,接下来需要往里面加入一些对象。对象可以随意移动,可以是怪物或者飞镖什么的,只要是参与碰撞的游戏对象都要为之创建一个相应的对象。当然,也可以创建一些静态的对象,用来表示游戏中的台阶或者墙壁等不可以移动的物体。
  为了创建一个对象,你需要做很多事情首先,创建一个定义结构,然后是对象,再指定一个,再是定义,然后再创建一个对象。下面会一个一个解释刚刚这些东西。
· 你首先创建一个定义结构体,用以指定的初始属性,比如位置或者速度。
· 一旦创建好结构体后,你就可以调用对象来创建一个对象了。
· 然后,你为对象定义一个,用以指定你想要仿真的物体的几何形状。
· 接着创建一个定义,同时设置之前创建好的为的一个属性,并且设置其它的属性,比如质量或者摩擦力。
· 最后,你可以使用对象来创建对象,通过传入一个的定义结构就可以了。
· 请注意,你可以往单个对象里面添加很多个对象。这个功能在你创建特别复杂的对象的时候非常有用。比如自行车,你可能要创建个轮子,车身等等,这些可以用关节连接起来。
  只要你把所有需要创建的对象都创建好之后,接下来就会接管工作,并且高效地进行物理仿真只要你周期性地调用对象的函数就可以了。
  但是,请注意,仅仅是更新它内部模型对象的位置如果你想让里面的的位置也更新,并且和物理仿真中的位置相同的话,那么你也需要周期性地更新精灵的位置
我们新建个的工程,命名为,同样的,的那个勾去掉。因为我们不需要这个服务。同样的,我们往工程里面新建一个的文件夹。然后把需要的添加到该工程的这个文件夹内。然后把带叹号的引用移除后并添加该工程文件夹内的相同的。并且在cocos2dBOX2DDemo工程中添加这个引用。
然后新建一个类添加到文件夹。命名为。并使之继承于。
下载并且添加到cocos2dBOX2DDemoContent工程的文件夹。
接下来,在类里面添加以下声明:
public static double PTM_RATIO = 32.0;
这里定义了一个像素米的比率。当你在里面指定一个在哪个位置时,你使用的单位要是米。但是,我们之前使用的都是像素作为单位,那样的话,位置就会不正确。根据,在处理大小在到个单元的对象的时候做了一些优化。这里的米大概就是一个杯子那么大,的话,大概就是一个箱子的大小。
  因此,我们并不直接传递像素,因为一个很小的对象很有个像素,那已经大大超过了优化时所限定的大小。因此,如果我们有一个像素的对象,我们可以把它除以,得到米这个长度,刚好可以很好地用来做物理仿真。
另外,为什么要用类型呢,这个有个精度的问题,如果用的话,我曾经尝试过,篮球直接嵌在墙壁不反弹。
  好了,现在来点有意思的东西。在类里面添加以下声明:
然后重载方法并且修改为:
public override bool init()
if (!base.init())
return false;
if (!base.init())
return false;
CCSize winSize = CCDirector.sharedDirector().getWinSize();
CCLabelTTF title = CCLabelTTF.labelWithString("Boxing", "Arial", 24);
//title.Color = new ccColor3B(0, 255, 255);
title.position = new CCPoint(winSize.width / 2, winSize.height - 50);
this.addChild(title, 1);
//Create sprite and add it to the layer
ball = CCSprite.spriteWithFile(@"images/Ball");
ball.position = new CCPoint(100, 300);
this.addChild(ball);
//Create the world
Vector2 gravity = new Vector2(0.0f, -30.0f);
bool doSleep = true;
world = new World(gravity, doSleep);
//Create edges around the entire screen
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position = new Vector2(0, 0);
Body groundBody = world.CreateBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
FixtureDef boxShapeDef = new FixtureDef();
boxShapeDef.shape = groundB
groundBox.SetAsEdge(new Vector2(0, 0), new Vector2((float)(winSize.width / PTM_RATIO), 0));
groundBody.CreateFixture(boxShapeDef);
groundBox.SetAsEdge(new Vector2(0, 0), new Vector2(0, (float)(winSize.height / PTM_RATIO)));
groundBody.CreateFixture(boxShapeDef);
groundBox.SetAsEdge(new Vector2(0, (float)(winSize.height / PTM_RATIO)),
new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)));
groundBody.CreateFixture(boxShapeDef);
groundBox.SetAsEdge(new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)),
new Vector2((float)(winSize.width / PTM_RATIO), 0));
groundBody.CreateFixture(boxShapeDef);
//Create ball body and shape
BodyDef ballBodyDef = new BodyDef();
ballBodyDef.type = BodyType.D
ballBodyDef.position = new Vector2((float)(100 / PTM_RATIO), (float)(300 / PTM_RATIO));
ballBodyDef.userData =
body = world.CreateBody(ballBodyDef);
CircleShape circle = new CircleShape();
circle._radius = (float)(26.0 / PTM_RATIO);
FixtureDef ballShapeDef = new FixtureDef();
ballShapeDef.shape =
ballShapeDef.density = 1.0f;
ballShapeDef.friction = 0.0f;
ballShapeDef.restitution = 1.0f;
body.CreateFixture(ballShapeDef);
this.schedule(tick);
return true;
呃,这里有很多陌生的。我们一点点来解释一下。下面,我会一段段地重复上面的代码,那样可以解释地更加清楚一些。
CCSize winSize = CCDirector.sharedDirector().getWinSize();
CCLabelTTF title = CCLabelTTF.labelWithString("Boxing", "Arial", 24);
//title.Color = new ccColor3B(0, 255, 255);
title.position = new CCPoint(winSize.width / 2, winSize.height - 50);
this.addChild(title, 1);
//Create sprite and add it to the layer
ball = CCSprite.spriteWithFile(@"images/Ball");
ball.position = new CCPoint(100, 300);
this.addChild(ball);
首先,我们往屏幕中间加入一个精灵。如果你看了前面的教程的话,这里应该没有什么问题。这个加入一个是为了让精灵能够更好的显示,这个是的一些问题,如果不添加一个,精灵的背景是黑的不是透明的。
//Create the world
Vector2 gravity = new Vector2(0.0f, -30.0f);
bool doSleep = true;
world = new World(gravity, doSleep);
接下来,我们创建了对象。当我们创建这个对象的时候,需要指定一个初始的重力向量。这里,我们设置轴方向为,因此,所有的都会往屏幕下面下落。同时,我们还指定了一个值,用以指明对象不参与碰撞时,是否可以休眠。一个休眠的对象将不会花费处理时间,直到它与其实对象发生碰撞的时候才会醒过来。
//Create edges around the entire screen
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position = new Vector2(0, 0);
Body groundBody = world.CreateBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
FixtureDef boxShapeDef = new FixtureDef();
boxShapeDef.shape = groundB
groundBox.SetAsEdge(new Vector2(0, 0), new Vector2((float)(winSize.width / PTM_RATIO), 0));
groundBody.CreateFixture(boxShapeDef);
groundBox.SetAsEdge(new Vector2(0, 0), new Vector2(0, (float)(winSize.height / PTM_RATIO)));
groundBody.CreateFixture(boxShapeDef);
groundBox.SetAsEdge(new Vector2(0, (float)(winSize.height / PTM_RATIO)),
new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)));
groundBody.CreateFixture(boxShapeDef);
groundBox.SetAsEdge(new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)),
new Vector2((float)(winSize.width / PTM_RATIO), 0));
groundBody.CreateFixture(boxShapeDef);
  接下来,我们为整个屏幕创建了一圈不可见的边。具体的步骤如下:
· 首先创建一个定义结构体,并且指定它应该放在左下角。
· 然后,使用对象来创建对象。(注意,这里一定要使用对象来创建,不能直接,因为对象会做一些内存管理操作。)
· 接着,为屏幕的每一个边界创建一个多边形。这些仅仅是一些线段。注意,我们把像素转换成了。通过除以之前定义的比率来实现的。
· 再创建一个定义,指定为。
· 再使用对象来为每一个创建一个对象。
· 注意:一个对象可以包含许许多多的对象。
//Create ball body and shape
BodyDef ballBodyDef = new BodyDef();
ballBodyDef.type = BodyType.D
ballBodyDef.position = new Vector2((float)(100 / PTM_RATIO), (float)(300 / PTM_RATIO));
ballBodyDef.userData =
body = world.CreateBody(ballBodyDef);
CircleShape circle = new CircleShape();
circle._radius = (float)(26.0 / PTM_RATIO);
FixtureDef ballShapeDef = new FixtureDef();
ballShapeDef.shape =
ballShapeDef.density = 1.0f;
ballShapeDef.friction = 0.0f;
ballShapeDef.restitution = 1.0f;
body.CreateFixture(ballShapeDef);
 接下来,我们创建篮球的。这个步骤和之前创建地面的差不多,但是有下面一些差别需要注意一下:
· 我们指定的类型为。默认值是,那意味着那个不能被移动也不会参与仿真。很明显,我们想让篮球参与仿真。
· 设置的属性为篮球精灵。你可以设置任何东西,但是,你设置成精灵会很方便,特别是当两个碰撞的时候,你可以通过这个参数把精灵对象取出来,然后做一些逻辑处理。
· 这里使用了一个不同的类型。
· 在这里,我们需要为这个指定一些参数,因此,我们没有使用便捷方法来创建。后面我们会讲到这些参数的具体意义。
this.schedule(tick);
最后一件事情就是调度一个方法。
实现方法:
void tick(float dt)
world.Step(dt, 10, 10);
for (Body b = world.GetBodyList(); b != null;b = b.GetNext() )
if (b.GetUserData() != null)
CCSprite ballData = (CCSprite)b.GetUserData();
ballData.position = new CCPoint((float)(b.GetPosition().X * PTM_RATIO),
(float)(b.GetPosition().Y * PTM_RATIO));
ballData.rotation = -1 * MathHelper.ToDegrees(b.GetAngle());
第一件事情就是调用对象的方法,这样它就可以进行物理仿真了。这里的两个参数分别是速度迭代次数和位置迭代次数你应该设置他们的范围在之间。(译者:这里的数字越小,精度越小,但是效率更高。数字越大,仿真越精确,但同时耗时更多。一般是个折中,如果学过数值分析,应该知道迭代步数的具体作用)。
  接下来,我们要使我们的精灵匹配物理仿真。因此,我们遍历对象里面的所有,然后看的属性是否为空,如果不为空,就可以强制转换成精灵对象。接下来,就可以根据的位置来更新精灵的位置了。
现在我们再添加一些代码作为层的初始化用。
public static new CCLayer node()
BOX2DLayer layer = new BOX2DLayer();
if (layer.init())
layer = null;
然后再修改里面的。在这个导演类里面修改初始场景。
修改如下:
// create a scene. it's an autorelease object
//CCScene pScene = cocos2dBOX2DDemoScene.scene();
CCScene pScene = CCScene.node();
pScene.addChild(Classes.BOX2DLayer.node());
pDirector.runWithScene(pScene);
return true;
编译并运行,你应该可以看到球会往下掉,并且会从屏幕底部往上面弹起来。
关于仿真的一些注意事项
  前面我们说后面会讨论,和参数的意义。
· Density就是单位体积的质量(密度)。因此,一个对象的密度越大,那么它就有更多的质量,当然就会越难以移动
· Friction 就是摩擦力。它的范围是, 意味着没有摩擦,代表最大摩擦,几乎移不动的摩擦。
· Restitution回复力。它的范围也是到意味着对象碰撞之后不会反弹,意味着是完全弹性碰撞,会以同样的速度反弹。
  建议多去改一改这些参数,看看具体会给小球带来什么影响。一定要去试哦!如果你想学习更多有关相关的内容,请继续关注。接下来会带来更好的教程。
本次工程下载:
深入学习:
&&&&推荐文章:
【上篇】【下篇】}

我要回帖

更多关于 fruit ninja hd 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信