(译)如何使用cocos2d和box2d来制作一个Breakout游戏:第一部分
免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!
程序截图:
box2d是一个非常强大的物理引擎库,同时它与cocos2d结合非常适合在iphone上面做游戏开发。著名的angry birds,tiny wings都是用box2d写的。你可以用它做好多事情,当然,最好的学习方法就是使用它来创建一个简单的游戏。
在这个教程中,我们将一步一步创建一个简单的breakout游戏,完成碰撞检测,篮球反弹物理效果,通过touch拖动paddle(就是上图的白色矩形),以及胜利/失败的场景。
如果你还不了解cocos2d和box2d,你可能先要读一读《如何使用cocos2d制作一个简单的iphone游戏》以及《在cocos2d里面如何使用box2d物理引擎:弹球》这些教程。
好了,是时候制作breakout了!
一个永远反弹的球
首先,打开Xcode,选择 cocos2d-0.99.1 Box2d Application template创建一个工程,命名为“ Box2DBreakout”. 删除掉模板代码,因此你会有一个空的工程来重新开始---具体步骤可以参照《在cocos2d里面如何使用box2d物理引擎:弹球》这个教程。
一旦你有了一个很好的干净的工程后,接下来,在HelloWorldScene.h中导入下面的头文件:
同时在HelloWorld类中添加以下成员变量:
b2Body*_groundBody;
b2Fixture*_bottomFixture;
b2Fixture*_ballFixture;
然后在HelloWorldScene.mm文件顶部定义比率:
这个比率我们在上一个教程中已经讨论过了,这里就不再啰嗦了。
然后,在init方法中加入下列代码:
//Create a world
b2Vec2 gravity=b2Vec2(0.0f,0.0f);
booldoSleep=true;
_world=newb2World(gravity, doSleep);
//Create edges around the entire screen
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
_groundBody=_world->CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
b2FixtureDef groundBoxDef;
groundBoxDef.shape=&groundBox;
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO,0));
_bottomFixture=_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO,
winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO),
b2Vec2(winSize.width/PTM_RATIO,0));
_groundBody->CreateFixture(&groundBoxDef);
好,这个代码和我们上一个教程中,为整个屏幕创建一个盒子边界差不多。然后,这一次,我们把重力设置为0,因为,在我们的breakout游戏中,我们并不需要重力!注意,我们存储了底部的fixture的一个指针,以方便后面使用(在后面的教程中,我们将用来追踪什么时候篮球与顶部相碰撞了)。
现在,下载我制作的篮球图片,并且拖到Resources文件夹中,确保 “Copy items into destination group’s folder (if needed)” 被复选中。
让我们往场景里面添加一个精灵吧。紧接着上面的代码,加入下面的代码片段:
CCSprite*ball=[CCSprite spriteWithFile:@"Ball.jpg"
rect:CGRectMake(0,0,52,52)];
ball.position=ccp(100,100);
ball.tag=1;
[self addChild:ball];
这里没什么疑问,我们已经做过好多次类似的事情了。注意,我们为篮球设置了一个tag标识,后面你会看到,这个tag标记有什么用。
接下来,为shape创建一个body:
b2BodyDef ballBodyDef;
ballBodyDef.type=b2_dynamicBody;
ballBodyDef.position.Set(100/PTM_RATIO,100/PTM_RATIO);
ballBodyDef.userData=ball;
b2Body*ballBody=_world->CreateBody(&ballBodyDef);
//Create circle shape
b2CircleShape circle;
circle.m_radius=26.0/PTM_RATIO;
//Create shape definition and add to body
b2FixtureDef ballShapeDef;
ballShapeDef.shape=&circle;
ballShapeDef.density=1.0f;
ballShapeDef.friction=0.f;
ballShapeDef.restitution=1.0f;
_ballFixture=ballBody->CreateFixture(&ballShapeDef);
这个看起来和上一篇教程中的也很像。再巩固一下吧,为了创建一个body对象,我们先要创建一个body定义结构,然后再创建body,接着是shape,再指定fixture结构,最后是创建fixture对象。
注意,我们设置这些参数有一点点不一样了:我们把回复力(restitution)设置为1.0,这意味着,我们的球在碰撞的时候,将会是完全弹性碰撞。
同时,我们也保存了球的fixture,原因和我们为什么保存屏幕底部的fixture是一样的,后面你就会看到了。
更新:注意,我们也把球的摩擦力设置为0.这样可以防止球在碰撞的时候,由于摩擦损失能量,导致来回碰撞的过程中会有一点点偏差。
好了,是时候做一些完全不同的事了!紧接上面的代码:
ballBody->ApplyLinearImpulse(force, ballBodyDef.position);
这里往球上面施加了一个冲力(impulse),这样可以让它初始化的时候朝一个特定的方向运动。
最后一件事情,就是在init方法中,增加一个tick调度方法:
下面是tick方法的实现:
_world->Step(dt,10,10);
for(b2Body*b=_world->GetBodyList(); b; b=b->GetNext()) {
if(b->GetUserData()!=NULL) {
CCSprite*sprite=(CCSprite*)b->GetUserData();
sprite.position=ccp(b->GetPosition().x*PTM_RATIO,
b->GetPosition().y*PTM_RATIO);
sprite.rotation=-1*CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
当然,这里也和上一个教程中的一样,没有什么特别的。
最后一件事我们永远不忘记!那就是清理:
delete _world;
_groundBody=NULL;
[super dealloc];
}
好了,让我们试一下吧。编译并运行工程,你将会看到一个球无限地在屏幕里面来回弹!----很酷吧!
增加 Paddle
如果没有一个paddle的话,那么就不可能称其为一个breakout游戏。下载我制作的paddle图片,然后把它拖到Resources文件夹中,同时确保 “Copy items into destination group’s folder (if needed)” 被复选上。
然后在HelloWorldScene.h文件中往HelloWorld类中添加下列成员变量:
b2Fixture*_paddleFixture;
然后,在init方法中构建paddle body:
CCSprite*paddle=[CCSprite spriteWithFile:@"Paddle.jpg"];
paddle.position=ccp(winSize.width/2,50);
[self addChild:paddle];
//Create paddle body
b2BodyDef paddleBodyDef;
paddleBodyDef.type=b2_dynamicBody;
paddleBodyDef.position.Set(winSize.width/2/PTM_RATIO,50/PTM_RATIO);
paddleBodyDef.userData=paddle;
_paddleBody=_world->CreateBody(&paddleBodyDef);
//Create paddle shape
b2PolygonShape paddleShape;
paddleShape.SetAsBox(paddle.contentSize.width/PTM_RATIO/2,
paddle.contentSize.height/PTM_RATIO/2);
//Create shape definition and add to body
b2FixtureDef paddleShapeDef;
paddleShapeDef.shape=&paddleShape;
paddleShapeDef.density=10.0f;
paddleShapeDef.friction=0.4f;
paddleShapeDef.restitution=0.1f;
_paddleFixture=_paddleBody->CreateFixture(&paddleShapeDef);
我不想花太多的时间解释上面的内容了。因为,和之前的创建篮球的body的过程差不太多。这里只给出不同的地方:
- 当你创建CCSprite的时候,你并不需要指定精灵的大小。如果你传递一个文件名给它,它会自动计算出大小。
- 注意,这里不是使用circle shape了。这一次,我们使用polygon shape。我们使用一个辅助方法来创建shape,当然,其形状是个盒子。
- 我们使用了SetAsBox方法来指定shape相对于body的位置,这个方法在构建复杂的对象的时候比较有用。这里,我们只是让shape在body中间。
- 我把paddle的密度设置得比球要大得多,同时调节了一下其它的参数。(这些参数要靠试,按照真实的高中物理知识去计算,可能得不到)
- 同时,我们存储paddleBody和paddleFixture的引用,为了方便后面使用。
如果你编译并运行的话,你将会看到屏幕中间有一个paddle,而且球碰到它将会反弹。
然后,这还不是很有趣,因为我们还不能移动paddle!
移动Paddle
移动paddle需要touch事件,所以先在init方法中允许touch事件:
然后,在HelloWorld类中添加下面的成员变量:
现在,让我们实现touch方法!首先是ccTouchesBegan:
if(_mouseJoint!=NULL)return;
UITouch*myTouch=[touches anyObject];
CGPoint location=[myTouch locationInView:[myTouch view]];
location=[[CCDirector sharedDirector] convertToGL:location];
b2Vec2 locationWorld=b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
if(_paddleFixture->TestPoint(locationWorld)) {
b2MouseJointDef md;
md.bodyA=_groundBody;
md.bodyB=_paddleBody;
md.target=locationWorld;
md.collideConnected=true;
md.maxForce=1000.0f*_paddleBody->GetMass();
_mouseJoint=(b2MouseJoint*)_world->CreateJoint(&md);
_paddleBody->SetAwake(true);
}
}
呃,好多新知识!让我们一点一点来讨论。
首先,我们把touch坐标转换成coocs2d坐标(convertToGL)然后,再转换成Box2d坐标(locationWorld)。
然后,我们使用paddle fixture的一个方法来测试这个touch点是否在fixture内部。
如果是的话,我们就创建一个所谓的”鼠标关节“。在Box2d里面,一个鼠标关节用来让一个body朝着一个指定的点移动---在这里个例子中,就是用户点的方向。
当你创建一个mouse joint后,你赋值给它两个body。第一个没有被使用,通常都是设置成ground body。第二个,就是你想让它移动的body,在这个例子中就是paddle。
接下来,你指定移动的终点---这个例子中就是用户点击的位置。
然后,你告诉box2d,但bodyA和bodyB碰撞的时候,把它当成是碰撞,而不是忽略它。这个很重要!因为,我之前没有设置它为ture,结果不行!因此,当我们用鼠标拖动这个paddle的时候,它并不会与屏幕的边界相碰撞,而且有时候,我的paddle直接就飞出屏幕之外了。这个非常非常奇怪,不过我现在知道是为什么了。因为没有设置bodyA和bodyB是可碰撞的。
你然后指定移动body的最大的力是多少。如果你减少这个数值的话,paddle body响应鼠标移动时就会慢一些。但是,我们想让paddle快速地响应鼠标的变化。
最后,我们把这个关节加入到world中,同时,保存这个指针,因为后面有用。同时,我们还要把body设置成苏醒的(awake)。之所以要这么做,是因为如果body在睡觉的话,那么它就不会响应鼠标的移动!
好了,接下来,让我们添加ccTouchesMoved方法:
if(_mouseJoint==NULL)return;
UITouch*myTouch=[touches anyObject];
CGPoint location=[myTouch locationInView:[myTouch view]];
location=[[CCDirector sharedDirector] convertToGL:location];
b2Vec2 locationWorld=b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
_mouseJoint->SetTarget(locationWorld);
}
这个方法的开头部分和ccTouchesBegan差不多---我们把touch坐标转换成Box2d坐标。唯一的区别就是,我们更新了鼠标关节的目标位置(也就是我们想让paddle移动的位置的)。
接下来,我们添加ccTouchesCacelled和ccTouchesEnded方法:
if(_mouseJoint) {
_world->DestroyJoint(_mouseJoint);
_mouseJoint=NULL;
}
}
-(void)ccTouchesEnded:(NSSet*)touches withEvent:(UIEvent*)event{
if(_mouseJoint) {
_world->DestroyJoint(_mouseJoint);
_mouseJoint=NULL;
}
}
我们在这些方法中做的只有一件事,就是在我们移动完paddle或者取消移动之后销毁mouse joint。
编译并运行,你现在可以用鼠标移动paddle了,同时可以让它与篮球相互碰撞了!
很好。。。不过,等一下,这还不是一个breakout!我们不可以把paddle移动到任何位置,我们只能在屏幕底部左右来回移动它!
限制Paddle的移动
我们可以很容易地限制paddle的移动,只需要添加另外一个关节,叫做prismatic joint。这个关节会限制一个body的移动沿着一根指定的轴。
因此,我们可以使用这种方法来限制paddle相对于地面移动,也就是说只能沿着x轴移动。
让我们看看相关代码。往init方法中加入下列代码:
b2PrismaticJointDef jointDef;
b2Vec2 worldAxis(1.0f,0.0f);
jointDef.collideConnected=true;
jointDef.Initialize(_paddleBody, _groundBody,
_paddleBody->GetWorldCenter(), worldAxis);
_world->CreateJoint(&jointDef);
第一件事情就是指定一个沿着x轴的向量。然后,我们需要指定collideConnected为true,因此,我们的球才能够正确的反弹,而不是飞到屏幕之外去。
然后,初始化关节,指定paddle和ground两个body,再使用world对象来创建关节!
编译并运行,你现在只能沿关x轴方向移动paddle了,这正是我们想要的,不是吧?
完成touch事件
现在,你玩一下,可能你会发现,有时候球反弹地特别快,有时候又比较慢。这取决于你是如何控制paddle与球相碰撞的。
更新:我第一次尝试去修正这个bug的时候,我通过直接调整球的速度,使用SetLinearVelocity方法。然后,Steve Oldmeadow也指出,这非常不好!它会破坏物理仿真,最好的方法是通过调用SetLinearDamping方法,间接影响速度。因此,现在这个教程就是这个做的。(damping就是阻尼的意思)
接下来,在tick方法中添加下列代码,具体位置是在获得user data之后:
staticintmaxSpeed=10;
b2Vec2 velocity=b->GetLinearVelocity();
float32 speed=velocity.Length();
if(speed>maxSpeed) {
b->SetLinearDamping(0.5);
}elseif(speed<maxSpeed) {
b->SetLinearDamping(0.0);
}
}
这里,我们判断sprite的tag,看是否是球的tag。如果是的话,我们就检查它的速度,如果太大的话,就设置它的阻尼为0.5,这样可以让它慢下来。
如果你编译并运行的话,你将会看到一个球以非常适中的速度在屏幕四周来回反弹。
给我源代码!
这里是本教程的完整源代码。这只是一部分,第二部分的教程会包含一个完整的breakout的源码。
接下来呢?
目前为止,我们已经有一个篮球在屏幕四周来回反弹了,同时还有一个paddle可以用鼠标来控制其移动。在下个教程中,我们将创建一些方块,当球碰到它们的时候,方块就会消失。当然,还有游戏胜利和失败的逻辑!
著作权声明:本文由http://www.cnblogs.com/andyque翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!
相关推荐
本教程将深入讲解如何利用Cocos2d和Box2D这两个强大的开源框架来制作一个类似的Breakout游戏。 Cocos2d是一个广泛应用于游戏开发的2D图形引擎,它提供了丰富的图形绘制、动画处理、物理模拟等功能。Box2D则是一个...
如何使用cocos2d和box2d来制作一个Breakout游戏:第一部分.pdf 如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分(完).pdf 如何使用cocos2d来制作一个打地鼠的游戏:第一部分.pdf 如何使用GameCenter...
如何使用cocos2d和box2d来制作一个Breakout游戏:第一部分.pdf 如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分(完).pdf 如何使用cocos2d来制作一个打地鼠的游戏:第一部分.pdf 如何使用GameCenter制作一...
如何使用cocos2d和box2d来制作一个Breakout游戏:第一部分.pdf 如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分(完).pdf 如何使用cocos2d来制作一个打地鼠的游戏:第一部分.pdf 如何使用GameCenter制作一...
下面我们将深入探讨如何使用cocos2d-x和Box2D来构建这样一个游戏。 首先,我们需要了解cocos2d-x中的Box2D整合。cocos2d-x提供了对Box2D的封装,使得开发者可以直接在cocos2d-x的场景中创建和操作Box2D的物理对象。...
### 如何使用Cocos2d来制作一个打地鼠的游戏:关键知识点解析 #### 引言 本教程旨在教导读者如何使用Cocos2d游戏引擎来构建一款经典的“打地鼠”游戏。Cocos2d是一款流行的游戏开发框架,特别适用于iOS平台。本教程...
《cocos2d-x与Box2D:构建基础物理引擎》 在游戏开发领域,物理引擎扮演着至关重要的角色,它赋予游戏中的对象以真实世界中的运动规律,从而增加游戏的真实感和趣味性。cocos2d-x是一个流行的开源2D游戏开发框架,...
Cocos2d-HTML5是一个强大的2D游戏开发框架,它提供了一系列工具和API,使开发者能够快速构建交互式游戏。该框架基于WebGL,因此游戏可以在现代浏览器上流畅运行,无需插件。 2. **TileMap概念**: 在2D游戏中,...
cocos2d是一个开源的2D游戏框架,广泛应用于iOS、Android以及Mac OS X等平台,提供丰富的图形绘制、动画制作和物理模拟等功能。而Box2D则是一款强大的2D物理引擎,它能够帮助开发者模拟现实世界的物理现象,如重力、...
《Box2dDemo》是基于Cocos2d-js框架实现的一个物理引擎示例项目,它展示了如何使用Box2d库来模拟2D物理世界。Box2d是一个强大的开源物理引擎,广泛应用于游戏开发,可以创建复杂的动态场景,如碰撞检测、重力影响等...
在这个"弹球"项目中,开发者利用了Cocos2d-x的图形功能和Box2D的物理模拟能力,创建了一个类似"打砖块"(Breakout)的游戏。"Breakout"是一款经典的街机游戏,玩家通过控制一个挡板反弹球来击碎屏幕上方的砖块。 **...
Box2D是一个开源的2D物理引擎,广泛用于游戏开发,尤其在Cocos2d这样的2D游戏引擎中。Cocos2d是一个流行的跨平台的游戏开发框架,它提供了丰富的功能和工具,使得开发者能够轻松创建各种2D游戏。在本案例中,我们...
【标题】"Breakout box2d学习代码"是基于Box2D物理引擎和Cocos2D 2.1框架创建的一款经典打砖块游戏(Breakout)的学习资源。这个项目旨在帮助初学者理解和掌握如何利用Box2D进行游戏开发,以及在Cocos2D 2.1环境下...
- **源码分析**:通过阅读`cocos2dx-mota-code`中的源码,可以学习到如何使用cocos2d-x框架构建一个完整的游戏,包括场景切换、游戏逻辑、UI布局等。 5. **调试与优化**: - **调试工具**:cocos2d-x提供了调试器...
在《Cocos2d-x 3.x制作2048》这本书中,作者详细介绍了使用Cocos2d-x 3.0版本,通过C++...通过学习这本书的知识点,开发者可以掌握使用Cocos2d-x框架来开发2D游戏的技能,同时了解一个完整游戏项目的开发步骤和细节。
【cocos2d 切水果游戏】是一款基于cocos2d和box2d游戏引擎开发的手机游戏,模仿了流行的“切西瓜”玩法。在这个游戏中,玩家需要通过滑动屏幕来切割屏幕上飞过的各种水果,尽可能多地得分,同时避免切割到炸弹等负面...
Cocos2d-x是一款强大的开源2D游戏引擎,而Box2D则是一个广泛使用的2D物理模拟库。将这两者结合使用,开发者可以轻松地创建出具有真实物理效果的游戏场景,让玩家体验到更加沉浸式的游戏环境。 Box2D,全称为"Box2D ...
在本文中,我们将深入探讨如何利用cocos2d-x游戏引擎和Box2D物理引擎来创建一款名为“弹球2”的Breakout风格游戏。这篇文章是基于子龙山人翻译的教程的第二部分,旨在为开发者提供详尽的实现步骤和关键知识点。 ...
资源名称:Cocos2d-x实战:JS卷——Cocos2d-JS开发内容简介:本书是介绍Cocos2d-x游戏编程和开发技术书籍,介绍了使用Cocos2d-JS中核心类、瓦片地图、物理引擎、音乐音效、数据持久化、网络通信、性能优化、多平台...
1. **场景(Scene)与层(Layer)管理**:Cocos2d-x使用场景和层的概念组织游戏画面。场景是一个完整的屏幕视图,而层则是场景中的一个可独立管理的部分,多个层可以组合成一个场景。 2. **精灵(Sprite)与动画**...