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 touchesself.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 worldb2Vec2 gravity;gravity.Set(0.0f, -10.0f);bool doSleep = true;_world = new b2World(gravity, doSleep);_world->SetContinuousPhysics(true);// Create the groundb2BodyDef 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 spriteCCSprite * 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 shapeb2CircleShape circle;circle.m_radius = 26.0 / PTM_RATIO;// Create shape definition and add to bodyb2FixtureDef 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 worldfor (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 bodyCCSprite * 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 locationfor(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
完成!