cocos2d简单弹球
今天我们介绍如何用cocos2d/Box2d实现一个简单的弹球程序:点击屏幕会新生成一个小球,在下落过程中碰到其他球或墙壁则会反弹。
1.
新建一个cocos2d/Box2d Application,输入名称Ball:
2.
修改HelloWorldLayer.h如下:
#import "cocos2d.h"
#import "Box2D.h"
// HelloWorldLayer
@interface HelloWorldLayer : CCLayer
{
CGSize _screenSize;
b2World * _world;
b2Body * _groundBody;
}
+(CCScene *)scene;
-(void)initWorld;
-(void)addBall:(CGPoint)p;
-(void)showLabel:(NSString *)str fontName:(NSString *)name fontSize:(int)size position:(CGPoint)pos rgb:(ccColor3B)color;
@end
函数功能不解释了,看名字就知道了。
3.
修改HelloWorldLayer.mm如下:
#import "HelloWorldLayer.h"
#define PTM_RATIO 32
// HelloWorldLayer implementation
@implementation HelloWorldLayer
+(CCScene *)scene
{
CCScene * scene = [CCScene node];
HelloWorldLayer * layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}
-(id)init
{
if( (self=[super init]))
{
// Enable touches
self.isTouchEnabled = YES;
_screenSize = [CCDirector sharedDirector].winSize;
CCLOG(@"Width: %0.2f, Height: %0.2f", _screenSize.width, _screenSize.height);
// Init the world
[self initWorld];
//Create the sprite
[self addBall:CGPointMake((int)(_screenSize.width / 2), (int)(_screenSize.height / 2))];
//Show label
[self showLabel:@"Tap screen" fontName:@"Marker Felt" fontSize:32 position:ccp(_screenSize.width/2, _screenSize.height-50) rgb:ccc3(0,0,255)];
[self schedule: @selector(tick:)];
}
return self;
}
-(void)initWorld
{
// Create the world
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
bool doSleep = true;
_world = new b2World(gravity, doSleep);
_world->SetContinuousPhysics(true);
// Create the ground
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
_groundBody = _world->CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
b2FixtureDef groundBoxDef;
groundBoxDef.shape = &groundBox;
groundBox.SetAsEdge(b2Vec2(0, 0), b2Vec2(_screenSize.width / PTM_RATIO, 0));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0, 0), b2Vec2(0, _screenSize.height / PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0, _screenSize.height / PTM_RATIO), b2Vec2(_screenSize.width / PTM_RATIO, _screenSize.height / PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(_screenSize.width / PTM_RATIO, _screenSize.height / PTM_RATIO), b2Vec2(_screenSize.width / PTM_RATIO, 0));
_groundBody->CreateFixture(&groundBoxDef);
}
// Init the world
-(void)addBall:(CGPoint)p
{
CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);
//Create the sprite
CCSprite * ballSprite = [CCSprite spriteWithFile:@"Ball.jpg" rect:CGRectMake(0, 0, 52, 52)];
ballSprite.position = p;
ballSprite.tag = 1;
[self addChild:ballSprite];
// Create ball body
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set(p.x / PTM_RATIO, p.y / PTM_RATIO);
ballBodyDef.userData = ballSprite;
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.0f;
ballShapeDef.restitution = 0.8f;
ballBody->CreateFixture(&ballShapeDef);
}
-(void)showLabel:(NSString *)str fontName:(NSString *)name fontSize:(int)size position:(CGPoint)pos rgb:(ccColor3B)color
{
CCLabelTTF * label = [CCLabelTTF labelWithString:str fontName:name fontSize:size];
[self addChild:label z:0];
[label setColor:color];
label.position = pos;
}
-(void)tick: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
_world->Step(dt, velocityIterations, positionIterations);
//Iterate over the bodies in the physics world
for (b2Body * b = _world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCLOG(@"X: %0.2f, Y: %02.f",b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
//Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite * myActor = (CCSprite *)b->GetUserData();
myActor.position = CGPointMake(b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for(UITouch * touch in touches)
{
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
[self addBall:location];
}
}
- (void)dealloc
{
delete _world;
_world = NULL;
// don't forget to call "super dealloc"
[super dealloc];
}
@end
在init函数中:
self.isTouchEnabled = YES;
表示启用触摸:在触摸开始时会自动调用
ccTouch(es)Began函数,在触摸结束时会自动调用ccTouch(es)Ended函数,在其中我们调用addBall函数创建一个球。
之后我们创建了一个重力为10,垂直向下的World,并在屏幕的四周创建了四面墙壁。
然后我们调用addBall创建一个球:
首先我们创建了一个sprite(精灵),之后创建了一个b2Body,然后用userData将它们绑定起来,方便我们之后获取它。
在init函数的最后我们调用了:
[self schedule: @selector(tick:)];
表示每一帧后都会调用tick函数,而在tick函数中,我们一个一个遍历World中的b2Body,一旦userData不空,我们就把它转为sprite,然后根据b2Body的坐标设置sprite的坐标。
因此可以总结如下:
我们创建了一个cocos2d中的sprite和一个Box2d中的b2Body。sprite通过spriteWithFile函数载入图片,因此可见;b2Body不可见,但它的运动遵循物理定律,因此在b2Body每运动一帧后我们都根据它的坐标设置sprite的坐标,这样看上去就像是这个sprite按物理定律运动一样。
好了,程序很简单,
结果如下:
最后我把代码也上传上来了:
http://download.csdn.net/detail/htttw/4464843
完成!