如何使用cocos2d和box2d制作突围游戏:PART

你也可以在Ray的网站上查看这个教程的中文版。简单突围游戏截屏BBOX2D是一个非常强大的物理引擎库,结合cocos2d非常适合在iphone上进行游戏开发。著名的《愤怒的小鸟》和《微小的翅膀》都写在box2d里。你可以用它做很多事情。当然,最好的学习方法是用它来创建一个简单的游戏。在本教程中,我们将一步一步地创建一个简单的突围游戏,完成碰撞检测,篮球反弹的物理效果,通过触摸拖动球拍(上图中的白色矩形),以及胜利/失败场景。(跳到本系列的第二部分。)如果你不了解cocos2d和box2d,不妨阅读一下《cocos2d: Pinball》中的《如何用cocos2d制作简单的iphone游戏》和《如何使用box2d物理引擎》。好了,是时候突围了!永远弹跳的球。首先打开Xcode,选择cocos2d-0.99.1box2d应用模板,创建一个名为“Box2DBreakout”的项目。删除模板代码,这样你就会有一个空项目重新开始——具体步骤参考教程“如何在COCOS2D: Pinball中使用Box2D物理引擎”。一旦有了一个好的干净的项目,接下来,在HelloWorldScene.h中导入下面的头文件:# import“box2d . h”并在HelloWorld类中添加下面的成员变量:b2World * _ worldb2Body * _ groundBodyb2Fixture * _ bottomFixtureb2Fixture * _ ballFixture然后在HelloWorldScene.mm文件的顶部定义比值:#define PTM_RATIO 32这个比值在上一个教程里已经讨论过了,这里就不赘述了。然后,将以下代码添加到init方法中:cgsizewisize =[cc director shared director]。winsize//创建一个世界b2Vec2重力= b2Vec2(0.0f,0.0f);bool doSleep = true_world = new b2World(重力,doSleep);//在整个屏幕周围创建边缘b2BodyDef groundBodyDefgroundBodyDef.position.Set(0,0);_ ground body = _ world-& gt;创建主体(& ampgroundBodyDef);B2多边形接地箱;b2FixtureDef groundBoxDefgroundBoxDef.shape = & amp接地箱;接地箱。SetAsEdge(b2Vec2(0,0),b2Vec2(winSize.width/PTM_RATIO,0));_ bottom fixture = _ ground body-& gt;create fixture(& amp;groundbox def);接地箱。SetAsEdge(b2Vec2(0,0),b2Vec2(0,winsize . height/PTM _ RATIO));_ ground body-& gt;create fixture(& amp;groundbox def);接地箱。SetAsEdge(b2Vec2(0,winSize.height/PTM_RATIO),b2Vec2(winSize.width/PTM_RATIO,winsize . height/PTM _ RATIO));_ ground body-& gt;create fixture(& amp;groundbox def);接地箱。seta sedge(b2ve C2(winsize . width/PTM _比率,winsize . height/PTM _比率),b2ve C2(winsize . width/PTM _比率,0));_ ground body-& gt;create fixture(& amp;groundbox def);好了,这段代码类似于我们上一个教程中为整个屏幕创建一个方框边框。然后,这一次,我们将重力设置为0,因为我们的突围游戏不需要重力!请注意,我们已经存储了一个指向底部夹具的指针供以后使用(在后面的教程中,我们将使用它来跟踪篮球何时到达顶部)。现在,下载我制作的篮球图片,并把它们拖到资源文件夹中,并确保“将项目复制到目标组的文件夹中(如果需要)”被选中。让我们在场景中添加一个小精灵。紧接着上面的代码,添加下面的代码片段://create sprite并添加到图层cc sprite * ball =[cc sprite sprite with file:@ " ball . jpg " rect:cgrectmake(0,0,52,52)];ball.position = ccp(100,100);ball . tag = 1;【self addChild:ball】;这是毫无疑问的。类似的事情我们做过很多次了。请注意,我们为篮球设置了一个标签徽标,稍后您将看到这个标签徽标的作用。接下来,为shape创建一个body://createballbodybodydef ballbodydef;ballbodydef . type = B2 _ dynamic body;ballbodydef . position . set(100/PTM _比,100/PTM _比);ballBodyDef.userData = ballB2 body * ball body = _ world-& gt;创建主体(& ampballBodyDef);//创建圆形b2CircleShape圆形;circle . m _ radius = 26.0/PTM _比;//创建形状定义并添加到主体b2FixtureDef ballShapeDef= & amp圆形;ballshapedef . density = 1.0f;ballshapedef . friction = 0 . f;ballshapedef . restoration = 1.0f;_ ball fixture = ball body-& gt;create fixture(& amp;ballShapeDef);这看起来与上一个教程中的非常相似。为了巩固它,为了创建一个实体对象,我们必须首先创建一个实体定义结构,然后创建一个实体,然后是一个形状,然后指定一个夹具结构,最后创建一个夹具对象。更新:注意我们也把球的摩擦力设置为0。这样可以防止球在碰撞的时候因为摩擦而失去能量,导致来回碰撞过程中出现一点点偏差。好了,是时候做一些完全不同的事情了!紧接着上面的代码:b2vec2force = b2vec2 (10,10);球体->;ApplyLinearImpulse(force,ballbodydef . position);这里对球施加了一个冲量,这样球在初始化的时候就可以向一个特定的方向运动。最后就是在init方法中增加一个tick调度方法:【self schedule:@ selector(tick:)】;以下是tick方法的实现:-(void)tick:(cctime)dt { _ world--> Step(dt,10,10);for(B2 body * b = _ world-& gt;GetBodyList();b;b = b-& gt;get next()){ if(b-& gt;GetUserData()!= NULL){ cc sprite * sprite =(cc sprite *)b-& gt;get user data();sprite . position = CCP(b->;GetPosition()。x * PTM比率,b-& gt;GetPosition()。y * PTM _比率);sprite . rotation =-1 * CC _ RADIANS _ TO _ DEGREES(b-& gt;GetAngle());}}}当然和上一个教程一样,没什么特别的。我们永远不会忘记的最后一件事!那就是清理:-(void)dealloc { delete _ world;_ groundBody = NULL【超级dealloc】;好的,让我们试一试。编译并运行该项目,您将看到一个球在屏幕内部无限反弹!-是不是很酷?加桨不加桨,不可能称之为突围赛。下载我制作的桨图片,并将其拖到资源文件夹中,并确保选中“将项目复制到目标组的文件夹中(如果需要)”。然后将以下成员变量添加到HelloWorld文件中的HelloWorld类中:b2Body * _ paddleBodyb2Fixture * _ paddleFixture然后在init方法中构建paddle body://create paddle并添加到图层cc sprite * paddle =[cc sprite sprite with file:@ " paddle . jpg "];paddle . position = CCP(winsize . width/2,50);[self addChild:paddle];//创建桨体b2BodyDef paddleBodyDefpaddle bodydef . type = B2 _ dynamic body;paddle bodydef . position . set(winsize . width/2/PTM _比率,50/PTM _比率);paddle bodydef . user data = paddle;_ paddle body = _ world-& gt;创建主体(& amppaddle bodydef);//创建船桨形状b2PolygonShape paddleShape桨状。setas box(paddle . contentsize . width/PTM _比率/2,paddle . contentsize . height/PTM _比率/2);//创建形状定义并添加到主体b2FixtureDef paddleShapeDefpaddleShapeDef.shape = & amppaddleShapepaddle shapedef . density = 10.0 f;paddleShapeDef.friction = 0.4fpaddle shapedef . restoration = 0.1f;_ paddle fixture = _ paddle body-& gt;create fixture(& amp;paddleShapeDef);以上我不想花太多时间解释。因为,和之前的篮球造体过程没有太大区别。这里唯一的区别是当你创建CCSprite时,你不需要指定Sprite的大小。如果你给它一个文件名,它会自动计算大小。请注意,这里没有使用圆形。这次,我们使用多边形形状。我们使用一种辅助方法来创建一个形状。当然,它的形状是一个盒子。我们使用SetAsBox方法来指定形状相对于主体的位置,这在构建复杂对象时很有用。在这里,我们只是把形状放在身体的中间。我把桨的密度设置得比球大很多,同时调整了其他参数。(这些参数需要根据真实的高中物理知识进行试错计算,可能没有。)同时我们存储了paddleBody和paddleFixture的引用,方便以后使用。如果你编译运行它,你会看到屏幕中间有一个拨片,球碰到它就会反弹。然后,它不是很有趣,因为我们还不能移动桨!需要touch事件来移动Paddle,所以首先在init方法中允许touch事件:self.isTouchEnabled = YES然后,将以下成员变量添加到HelloWorldScene.h的HelloWorld类中:b2MouseJoint * _ mouseJoint现在,让我们实现触摸方法!第一个是cc touches begin:-(void)cc touches begin:(ns set *)touches with event:(ui event *)event { if(_ mouse joint!= NULL)返回;ui touch * my touch =[触摸任何对象];CG point location =[my touch location inview:[my touch view]];location =[[cc director shared director]convert togl:location];b2ve C2 location world = b2ve C2(location . x/PTM _比率,location . y/PTM _比率);if(_ paddle fixture-& gt;test point(location world)){ B2 mousejointdef MD;md.bodyA = _ groundBodyMD . body b = _ paddle body;md.target = locationWorldmd.collideConnected = trueMD . max force = 1000.0 f * _ paddle body-& gt;GetMass();_ mouse joint =(B2 mouse joint *)_ world-& gt;create joint(& amp;MD);_ paddle body-& gt;SetAwake(真);}}嗯,很多新知识!我们一点一点来讨论吧。首先,我们将触摸坐标转换为coocs2d坐标(convertToGL),然后转换为Box2d坐标(locationWorld)。然后,我们使用一种桨夹具的方法来测试接触点是否在夹具内部。如果是这样,我们将创建一个所谓的“鼠标关节”。在Box2d中,鼠标关节用于向指定的点移动身体——在本例中是用户点的方向。创建鼠标关节时,可以为其指定两个实体。第一个不用,一般设置为接地体。第二个是你想让它移动的身体,在这个例子中,是划水。接下来,指定移动的终点——在本例中,就是用户单击的地方。然后,你告诉box2d,但是当bodyA和bodyB发生碰撞时,把它当作一次碰撞,而不是忽略它。这一点很重要!因为,我之前没有设置为ture,没用!所以,当我们用鼠标拖动这个桨的时候,它并没有和屏幕的边界发生碰撞,有时候,我的桨只是飞出了屏幕。这很奇怪,但现在我知道为什么了。因为bodyA和bodyB不会发生碰撞。然后指定移动主体的最大力。如果您减少这个数字,桨体将会对鼠标移动反应较慢。但是,我们希望paddle能够快速响应鼠标的变化。最后,我们将这个关节添加到世界中,同时,保存这个指针,因为稍后它会有用。与此同时,我们必须让身体保持清醒。这样做的原因是,如果身体在睡觉,就不会对鼠标的移动做出反应!好,接下来我们添加cctouchemoved方法:-(void)cctouchemoved:(ns set *)touches with event:(ui event *)event { if(_ mouse joint = = null)return;ui touch * my touch =[触摸任何对象];CG point location =[my touch location inview:[my touch view]];location =[[cc director shared director]convert togl:location];b2ve C2 location world = b2ve C2(location . x/PTM _比率,location . y/PTM _比率);_ mouse joint-& gt;set target(location world);这个方法的开始类似于cctouchesbeangen——我们把触摸坐标转换成Box2d坐标。唯一不同的是,我们更新了鼠标关节的目标位置(也就是我们希望桨移动的位置)。接下来,我们添加ccTouchesCacelled和ccTouchesEnded方法:-(void)cctouchechancelled:(ns set *)touches with event:(ui event *)event { if(_ mouse joint){ _ world-->;destroy joint(_ mouse joint);_ mouseJoint = NULL} }-(void)ccTouchesEnded:(ns set *)touches withEvent:(ui event *)event { if(_ mouseJoint){ _ world-& gt;destroy joint(_ mouse joint);_ mouseJoint = NULL这些方法我们做的只有一件事,就是在我们移动桨或者取消移动后破坏鼠标关节。编译运行,现在可以用鼠标移动球拍,让它和篮球碰撞!挺好的。。。但是等一下,这不是越狱!我们不能在任何地方移动拨片,只能在屏幕底部来回移动!限制桨的运动我们可以很容易地通过添加另一个关节来限制桨的运动,这种关节称为棱柱形关节。该关节将限制物体沿指定轴的运动。所以我们可以用这个方法来限制桨相对于地面的运动,也就是只能沿着X轴运动。让我们看看相关的代码。将以下代码添加到init方法://RestrictPaddleAlong x轴B2棱柱形关节定义关节定义;b2Vec2 worldAxis(1.0f,0.0f);joint def . collided connected = true;jointDef。初始化(_paddleBody,_groundBody,_ paddle body-& gt;GetWorldCenter()、world axis);_ world-& gt;create joint(& amp;joint def);第一件事是指定一个沿x轴的向量。然后,我们需要将collideConnected指定为true,这样我们的球才能正确反弹,而不是飞出屏幕。然后,初始化关节,指定两个身体,桨和地面,然后使用世界对象创建关节!更新:当我第一次尝试修复这个bug时,我通过直接调整球的速度来使用SetLinearVelocity方法。然后,Steve Oldmeadow也指出,这样很不好!会破坏物理仿真,最好的办法是通过调用SetLinearDamping方法来间接影响速度。所以这个教程现在就由这个来做。(阻尼是阻尼的意思)接下来,在tick方法中添加下面的代码,在获取用户数据之后:if (sprite。tag = = 1){ static int maxspeed = 10;b2Vec2速度= b-& gt;GetLinearVelocity();float32速度=速度。长度();如果(速度& gtmaxSpeed){ b-& gt;setlinearminding(0.5);} else if(speed & lt;maxSpeed){ b-& gt;setlinearminding(0.0);这里我们判断一下sprite的标签,看看是不是球的标签。如果是,我们将检查它的速度。如果它太大,我们会将其阻尼设置为0.5,这会使它变慢。如果你编译并运行它,你会看到一个球以非常适中的速度在屏幕上来回弹跳。把源代码给我!这里是本教程的完整源代码。这只是一部分,教程的第二部分会包含完整的breakout源代码。下一步是什么?到目前为止,我们有一个篮球在屏幕上来回弹跳,还有一个可以用鼠标控制其移动的球拍。在下一个教程中,我们将创建一些正方形。当球碰到它们时,方块就会消失。