手把手教你制作一款Box2D小游戏(二)
2017-01-03 21:33
337 查看
我们继续来制作这款Box2D小游戏。
前一篇中我们介绍了StepBlock类的制作,本篇中我们首先来定义好游戏的场景(CCScene)和层(CCLayer)。
我们定义一个GameLayer类,继承自CCLayer,声明如下:
#import "CCLayer.h"
#import "cocos2d.h"
#import "Box2D.h"
#import "ParallaxBackground.h"
#import "Ball.h"
#import "TagDefinitions.h"
#import "ContactListener.h"
@interface GameLayer : CCLayer <CCTouchOneByOneDelegate>{
float screenWidth;
float screenHeight;
b2World* world;
ParallaxBackground* background;
NSMutableArray* steps;
Ball* ball;
float lowestStepY;
float stepDistance;
BOOL gameBegin;
ForceType appliedForceType;
CCLabelAtlas* scoreLb;
CCLabelAtlas* highScoreLb;
CCSprite* scoreIcon;
CCSprite* highScoreIcon;
int score;
int highScore;
ContactListener* myListener;
BOOL isTouchable;
}
@property int lastScoredStep;
+ (GameLayer*) gameLayer;
+ (id)scene;
- (b2World*) sharedWorld;
- (void)addScore;
@end
screenHeight和screenWidth为屏幕高宽,world为这款游戏的世界对象,background是游戏的滚动背景(需要自己制作),steps是游戏中需要用到的StepBlock的数组,主要是出于复用的考虑。ball是游戏中的小球对象,我们接下来会实现。lowestStepY前面介绍过了,是位置最低的Block的y值。stepDistance是相邻的上下两个StepBlock之间的间隔。gameBegin用来标记游戏是否已经开始,如果游戏没有开始,所有的物体都应静止不动。appliedForceType用来记录小球的受力情况(小球受点击屏幕控制)。下面的跟score相关的CCLabelAtlas,CCSprite以及int值都是跟当前分数和最高分有关的,myListener是重载b2ContactListener的类的实例,用来侦听碰撞事件并做相应的处理,我们后面来介绍。isTouchable用来控制屏幕点击,后面会用到。
GameLayer.mm的定义:
首先定义属性的实现:
@synthesize lastScoredStep;
然后是一个静态变量:
static GameLayer* instance;
静态方法,用来返回静态成员instance(instance在初始化的时候会被赋值为self):
+ (GameLayer*)gameLayer{
return instance;
}
静态的scene方法,返回一个包含GameLayer的场景对象供AppDelegate引用:
+ (id)scene{
CCScene* scene = [CCScene node];
GameLayer* gameLayer = [GameLayer node];
[scene addChild:gameLayer];
return scene;
}
下面列出了需要用到的图片素材:
highscore.png:
numbers.png:
score.png:
接着我们在GameLayer中添加下面的方法,初始化Score和HighScore(效果可以看上一篇最开始的那个截图):
- (void)initLabels{
int labelPos = 25;
scoreIcon = [CCSpritespriteWithFile:@"score.png"];
scoreIcon.anchorPoint = ccp(0, 1);
scoreIcon.position = ccp(17, screenHeight -labelPos);
[self addChild:scoreIcon];
scoreLb = [CCLabelAtlaslabelWithString:@"0" charMapFile:@"numbers.png"itemWidth:16 itemHeight:20 startCharMap:'0'];
scoreLb.position = ccp(100, screenHeight -labelPos);
scoreLb.anchorPoint = ccp(0, 1);
[self addChild:scoreLb];
highScoreIcon = [CCSpritespriteWithFile:@"highscore.png"];
highScoreIcon.anchorPoint = ccp(0, 1);
highScoreIcon.position = ccp(screenWidth * 0.5 +25, screenHeight - labelPos);
[self addChild:highScoreIcon];
highScoreLb = [CCLabelAtlaslabelWithString:[NSString stringWithFormat:@"%d", highScore]charMapFile:@"numbers.png" itemWidth:16 itemHeight:20startCharMap:'0'];
highScoreLb.position = ccp(screenWidth * 0.5 +59, screenHeight - labelPos);
highScoreLb.anchorPoint = ccp(0, 1);
[self addChild:highScoreLb];
}
然后添加下面的方法,都是跟score和highscore相关的方法,比较容易理解,不多做解释:
- (void)initScores{
score = 0;
[scoreLb setString:@"0"];
}
- (void)saveHighScore{
[[NSUserDefaults standardUserDefaults]setObject:[NSNumber numberWithInt:highScore] forKey:@"highScore"];
highScore = [[[NSUserDefaultsstandardUserDefaults] objectForKey:@"highScore"] intValue];
}
- (void)addScore{
score++;
[scoreLb setString:[NSStringstringWithFormat:@"%d", score]];
if (score > highScore) {
highScore = score;
[highScoreLbsetString:[NSString stringWithFormat:@"%d", highScore]];
}
}
接下来我们添加初始化StepBlock的方法:
- (void)initSteps{
steps = [[NSMutableArray array] retain];
lowestStepY = screenHeight * 0.5f;
[steps addObject:[[StepBlock alloc]initWithPos:ccp(screenWidth * 0.5f, lowestStepY) andType:StepBlockNormal]];
lowestStepY -= stepDistance;
[steps addObject:[[StepBlock alloc]initWithPos:ccp(0, lowestStepY) andType:StepBlockNormal]];
lowestStepY -= stepDistance;
[steps addObject:[[StepBlock alloc]initWithPos:ccp(1000, lowestStepY) andType:StepBlockNormal]];
for (int i = 0; i < 6; i++) {
lowestStepY -=stepDistance;
[stepsaddObject:[[StepBlock alloc] initWithY:lowestStepY]];
}
int tagBase = 1;
for (StepBlock* step in steps) {
step.tag = tagBase++;
[self addChild:step];
}
}
需要在StepBlock类中重载下面的方法:
- (void)setTag:(NSInteger)tag{
body->SetUserData([NSNumbernumberWithInteger:tag]);
if (tagSave < 1) {
tagSave = tag;
}
}
tagSave用来区分每一个Block。
接着我们定义GameLayer的初始化方法:
- (id)init{
if (self = [super init]) {
instance = self;
//screen dimensions
CGSize screenSize =[[CCDirector sharedDirector] winSize];
screenHeight =screenSize.height;
screenWidth =screenSize.width;
//init cache
[[GB2ShapeCachesharedShapeCache] addShapesWithFile:@"steps-elements.plist"];
highScore =[[[NSUserDefaults standardUserDefaults] objectForKey:@"highScore"]intValue];
//background
background =[ParallaxBackground background];
[selfaddChild:background];
//init physics
[self initPhysics];
//init random seed
srand(time(nil));
stepDistance = 90;
gameBegin = false;
isTouchable = true;
lastScoredStep = -1;
appliedForceType =ForceNone;
[self initSteps];
[self initLabels];
[self initScores];
}
return self;
}
初始化方法中的initPhysics用来初始化Box2D相关的对象:
- (void)initPhysics{
b2Vec2 gravity;
gravity.Set(0.0f, -8.0f);
world = new b2World(gravity);
world->SetAllowSleeping(true);
world->SetContinuousPhysics(false);
myListener = new ContactListener();
world->SetContactListener(myListener);
ball = [Ball ball];
[self addChild:ball];
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); //bottom-left corner
// The body is also added to theworld.
b2Body* groundBody =world->CreateBody(&groundBodyDef);
b2EdgeShape groundBox;
b2Filter filter;
filter.maskBits = 3;
filter.categoryBits = 2;
float leftX = 15.0/PTM_RATIO;
float rightX = (screenWidth-15)/PTM_RATIO;
float Y = 2*screenHeight/PTM_RATIO;
// left
groundBox.Set(b2Vec2(leftX, Y),b2Vec2(leftX,0));
b2Fixture* fixture2 =groundBody->CreateFixture(&groundBox,0);
fixture2->SetFilterData(filter);
fixture2->SetFriction(0);
// right
groundBox.Set(b2Vec2(rightX, Y),b2Vec2(rightX, 0));
b2Fixture* fixture3 =groundBody->CreateFixture(&groundBox,0);
fixture3->SetFilterData(filter);
fixture3->SetFriction(0);
}
我们在这个方法中初始化了世界对象,listener对象,ball对象以及左右的墙体,注意一下我们定义的墙体碰撞过滤标记。
接着我们定义reset方法,用来在一局游戏结束(小球掉下去了或者升到屏幕外边去了)的时候重置游戏:
- (void)reset{
[self initScores];
[ball reset];
lastScoredStep = -1;
lowestStepY = screenHeight * 0.5f;
[[steps objectAtIndex:0]resetWithPos:ccp(screenWidth * 0.5f, lowestStepY) andType:StepBlockNormal];
lowestStepY -= stepDistance;
[[steps objectAtIndex:1] resetWithPos:ccp(0,lowestStepY) andType:StepBlockNormal];
lowestStepY -= stepDistance;
[[steps objectAtIndex:2] resetWithPos:ccp(1000,lowestStepY) andType:StepBlockNormal];
for (int i = 0; i < 6; i++) {
lowestStepY -=stepDistance;
[[stepsobjectAtIndex:(3+i)] resetWithY:lowestStepY];
}
[self scheduleUpdate];
}
最后定义update方法:
- (void)update:(ccTime)delta{
int speed = min(80 + score/10, 150);
if (ball.position.y < -20 || ball.position.y> (screenHeight + 20)) {
[self showGameOver];
return;
}
if (appliedForceType == ForceLeft) {
[ball pushLeft];
} else if (appliedForceType == ForceRight) {
[ball pushRight];
}
float length = delta * speed;
lowestStepY += length;
for (StepBlock* step in steps) {
if ([step moveUp:lengthlowestStepY:(lowestStepY - stepDistance)] == NO) {
lowestStepY -= stepDistance;
}
}
int32 velocityIterations = 8;
int32 positionIterations = 4;
world->Step(delta, velocityIterations,positionIterations);
}
update方法中用world的Step方法来执行物理计算,同时也更新StepBlock的位置,并且根据小球的受力来使其移动,根据小球的y坐标来判断是否GameOver。
showGameOver方法定义如下:
- (void)showGameOver{
gameBegin = false;
[self unscheduleUpdate];
[background stopRolling];
[self saveHighScore];
isTouchable = false;
[self schedule:@selector(enableTouch)interval:1.0f repeat:1 delay:1.0f];
}
enableTouch方法:
- (void)enableTouch{
isTouchable = true;
[self unschedule:_cmd];
}
我们需要通过点击屏幕左右来控制小球的受力(移动),因此让GameLayer实现CCTouchOneByOneDelegate委托,重载下面的三个方法:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
if (!isTouchable) {
return NO;
}
if (gameBegin) {
CGPoint touchLocation =[touch locationInView:[[CCDirector sharedDirector] view]];
touchLocation =[[CCDirector sharedDirector] convertToGL:touchLocation];
if (touchLocation.x <0.5 * screenWidth) {
appliedForceType = ForceLeft;
} else {
appliedForceType = ForceRight;
}
} else {
gameBegin = true;
[backgroundstartRolling];
[self reset];
}
return YES;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchLocation = [touchlocationInView:[[CCDirector sharedDirector] view]];
touchLocation = [[CCDirector sharedDirector]convertToGL:touchLocation];
if (touchLocation.x < 0.5 * screenWidth) {
appliedForceType =ForceLeft;
} else {
appliedForceType =ForceRight;
}
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
appliedForceType = ForceNone;
}
别忘了注册代理:
- (void)onEnterTransitionDidFinish{
[[[CCDirector sharedDirector] touchDispatcher]addTargetedDelegate:self priority:1 swallowsTouches:YES];
}
- (void)onExit{
[[[CCDirector sharedDirector] touchDispatcher]removeDelegate:self];
}
这样GameLayer就只做完成了。
由于篇幅限制,我们在下一篇中完成ContactListener和Ball类的编写。
我们继续来制作这款Box2D小游戏。
前一篇中我们介绍了StepBlock类的制作,本篇中我们首先来定义好游戏的场景(CCScene)和层(CCLayer)。
我们定义一个GameLayer类,继承自CCLayer,声明如下:
#import "CCLayer.h"
#import "cocos2d.h"
#import "Box2D.h"
#import "ParallaxBackground.h"
#import "Ball.h"
#import "TagDefinitions.h"
#import "ContactListener.h"
@interface GameLayer : CCLayer <CCTouchOneByOneDelegate>{
float screenWidth;
float screenHeight;
b2World* world;
ParallaxBackground* background;
NSMutableArray* steps;
Ball* ball;
float lowestStepY;
float stepDistance;
BOOL gameBegin;
ForceType appliedForceType;
CCLabelAtlas* scoreLb;
CCLabelAtlas* highScoreLb;
CCSprite* scoreIcon;
CCSprite* highScoreIcon;
int score;
int highScore;
ContactListener* myListener;
BOOL isTouchable;
}
@property int lastScoredStep;
+ (GameLayer*) gameLayer;
+ (id)scene;
- (b2World*) sharedWorld;
- (void)addScore;
@end
screenHeight和screenWidth为屏幕高宽,world为这款游戏的世界对象,background是游戏的滚动背景(需要自己制作),steps是游戏中需要用到的StepBlock的数组,主要是出于复用的考虑。ball是游戏中的小球对象,我们接下来会实现。lowestStepY前面介绍过了,是位置最低的Block的y值。stepDistance是相邻的上下两个StepBlock之间的间隔。gameBegin用来标记游戏是否已经开始,如果游戏没有开始,所有的物体都应静止不动。appliedForceType用来记录小球的受力情况(小球受点击屏幕控制)。下面的跟score相关的CCLabelAtlas,CCSprite以及int值都是跟当前分数和最高分有关的,myListener是重载b2ContactListener的类的实例,用来侦听碰撞事件并做相应的处理,我们后面来介绍。isTouchable用来控制屏幕点击,后面会用到。
GameLayer.mm的定义:
首先定义属性的实现:
@synthesize lastScoredStep;
然后是一个静态变量:
static GameLayer* instance;
静态方法,用来返回静态成员instance(instance在初始化的时候会被赋值为self):
+ (GameLayer*)gameLayer{
return instance;
}
静态的scene方法,返回一个包含GameLayer的场景对象供AppDelegate引用:
+ (id)scene{
CCScene* scene = [CCScene node];
GameLayer* gameLayer = [GameLayer node];
[scene addChild:gameLayer];
return scene;
}
下面列出了需要用到的图片素材:
highscore.png:
numbers.png:
score.png:
接着我们在GameLayer中添加下面的方法,初始化Score和HighScore(效果可以看上一篇最开始的那个截图):
- (void)initLabels{
int labelPos = 25;
scoreIcon = [CCSpritespriteWithFile:@"score.png"];
scoreIcon.anchorPoint = ccp(0, 1);
scoreIcon.position = ccp(17, screenHeight -labelPos);
[self addChild:scoreIcon];
scoreLb = [CCLabelAtlaslabelWithString:@"0" charMapFile:@"numbers.png"itemWidth:16 itemHeight:20 startCharMap:'0'];
scoreLb.position = ccp(100, screenHeight -labelPos);
scoreLb.anchorPoint = ccp(0, 1);
[self addChild:scoreLb];
highScoreIcon = [CCSpritespriteWithFile:@"highscore.png"];
highScoreIcon.anchorPoint = ccp(0, 1);
highScoreIcon.position = ccp(screenWidth * 0.5 +25, screenHeight - labelPos);
[self addChild:highScoreIcon];
highScoreLb = [CCLabelAtlaslabelWithString:[NSString stringWithFormat:@"%d", highScore]charMapFile:@"numbers.png" itemWidth:16 itemHeight:20startCharMap:'0'];
highScoreLb.position = ccp(screenWidth * 0.5 +59, screenHeight - labelPos);
highScoreLb.anchorPoint = ccp(0, 1);
[self addChild:highScoreLb];
}
然后添加下面的方法,都是跟score和highscore相关的方法,比较容易理解,不多做解释:
- (void)initScores{
score = 0;
[scoreLb setString:@"0"];
}
- (void)saveHighScore{
[[NSUserDefaults standardUserDefaults]setObject:[NSNumber numberWithInt:highScore] forKey:@"highScore"];
highScore = [[[NSUserDefaultsstandardUserDefaults] objectForKey:@"highScore"] intValue];
}
- (void)addScore{
score++;
[scoreLb setString:[NSStringstringWithFormat:@"%d", score]];
if (score > highScore) {
highScore = score;
[highScoreLbsetString:[NSString stringWithFormat:@"%d", highScore]];
}
}
接下来我们添加初始化StepBlock的方法:
- (void)initSteps{
steps = [[NSMutableArray array] retain];
lowestStepY = screenHeight * 0.5f;
[steps addObject:[[StepBlock alloc]initWithPos:ccp(screenWidth * 0.5f, lowestStepY) andType:StepBlockNormal]];
lowestStepY -= stepDistance;
[steps addObject:[[StepBlock alloc]initWithPos:ccp(0, lowestStepY) andType:StepBlockNormal]];
lowestStepY -= stepDistance;
[steps addObject:[[StepBlock alloc]initWithPos:ccp(1000, lowestStepY) andType:StepBlockNormal]];
for (int i = 0; i < 6; i++) {
lowestStepY -=stepDistance;
[stepsaddObject:[[StepBlock alloc] initWithY:lowestStepY]];
}
int tagBase = 1;
for (StepBlock* step in steps) {
step.tag = tagBase++;
[self addChild:step];
}
}
需要在StepBlock类中重载下面的方法:
- (void)setTag:(NSInteger)tag{
body->SetUserData([NSNumbernumberWithInteger:tag]);
if (tagSave < 1) {
tagSave = tag;
}
}
tagSave用来区分每一个Block。
接着我们定义GameLayer的初始化方法:
- (id)init{
if (self = [super init]) {
instance = self;
//screen dimensions
CGSize screenSize =[[CCDirector sharedDirector] winSize];
screenHeight =screenSize.height;
screenWidth =screenSize.width;
//init cache
[[GB2ShapeCachesharedShapeCache] addShapesWithFile:@"steps-elements.plist"];
highScore =[[[NSUserDefaults standardUserDefaults] objectForKey:@"highScore"]intValue];
//background
background =[ParallaxBackground background];
[selfaddChild:background];
//init physics
[self initPhysics];
//init random seed
srand(time(nil));
stepDistance = 90;
gameBegin = false;
isTouchable = true;
lastScoredStep = -1;
appliedForceType =ForceNone;
[self initSteps];
[self initLabels];
[self initScores];
}
return self;
}
初始化方法中的initPhysics用来初始化Box2D相关的对象:
- (void)initPhysics{
b2Vec2 gravity;
gravity.Set(0.0f, -8.0f);
world = new b2World(gravity);
world->SetAllowSleeping(true);
world->SetContinuousPhysics(false);
myListener = new ContactListener();
world->SetContactListener(myListener);
ball = [Ball ball];
[self addChild:ball];
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); //bottom-left corner
// The body is also added to theworld.
b2Body* groundBody =world->CreateBody(&groundBodyDef);
b2EdgeShape groundBox;
b2Filter filter;
filter.maskBits = 3;
filter.categoryBits = 2;
float leftX = 15.0/PTM_RATIO;
float rightX = (screenWidth-15)/PTM_RATIO;
float Y = 2*screenHeight/PTM_RATIO;
// left
groundBox.Set(b2Vec2(leftX, Y),b2Vec2(leftX,0));
b2Fixture* fixture2 =groundBody->CreateFixture(&groundBox,0);
fixture2->SetFilterData(filter);
fixture2->SetFriction(0);
// right
groundBox.Set(b2Vec2(rightX, Y),b2Vec2(rightX, 0));
b2Fixture* fixture3 =groundBody->CreateFixture(&groundBox,0);
fixture3->SetFilterData(filter);
fixture3->SetFriction(0);
}
我们在这个方法中初始化了世界对象,listener对象,ball对象以及左右的墙体,注意一下我们定义的墙体碰撞过滤标记。
接着我们定义reset方法,用来在一局游戏结束(小球掉下去了或者升到屏幕外边去了)的时候重置游戏:
- (void)reset{
[self initScores];
[ball reset];
lastScoredStep = -1;
lowestStepY = screenHeight * 0.5f;
[[steps objectAtIndex:0]resetWithPos:ccp(screenWidth * 0.5f, lowestStepY) andType:StepBlockNormal];
lowestStepY -= stepDistance;
[[steps objectAtIndex:1] resetWithPos:ccp(0,lowestStepY) andType:StepBlockNormal];
lowestStepY -= stepDistance;
[[steps objectAtIndex:2] resetWithPos:ccp(1000,lowestStepY) andType:StepBlockNormal];
for (int i = 0; i < 6; i++) {
lowestStepY -=stepDistance;
[[stepsobjectAtIndex:(3+i)] resetWithY:lowestStepY];
}
[self scheduleUpdate];
}
最后定义update方法:
- (void)update:(ccTime)delta{
int speed = min(80 + score/10, 150);
if (ball.position.y < -20 || ball.position.y> (screenHeight + 20)) {
[self showGameOver];
return;
}
if (appliedForceType == ForceLeft) {
[ball pushLeft];
} else if (appliedForceType == ForceRight) {
[ball pushRight];
}
float length = delta * speed;
lowestStepY += length;
for (StepBlock* step in steps) {
if ([step moveUp:lengthlowestStepY:(lowestStepY - stepDistance)] == NO) {
lowestStepY -= stepDistance;
}
}
int32 velocityIterations = 8;
int32 positionIterations = 4;
world->Step(delta, velocityIterations,positionIterations);
}
update方法中用world的Step方法来执行物理计算,同时也更新StepBlock的位置,并且根据小球的受力来使其移动,根据小球的y坐标来判断是否GameOver。
showGameOver方法定义如下:
- (void)showGameOver{
gameBegin = false;
[self unscheduleUpdate];
[background stopRolling];
[self saveHighScore];
isTouchable = false;
[self schedule:@selector(enableTouch)interval:1.0f repeat:1 delay:1.0f];
}
enableTouch方法:
- (void)enableTouch{
isTouchable = true;
[self unschedule:_cmd];
}
我们需要通过点击屏幕左右来控制小球的受力(移动),因此让GameLayer实现CCTouchOneByOneDelegate委托,重载下面的三个方法:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
if (!isTouchable) {
return NO;
}
if (gameBegin) {
CGPoint touchLocation =[touch locationInView:[[CCDirector sharedDirector] view]];
touchLocation =[[CCDirector sharedDirector] convertToGL:touchLocation];
if (touchLocation.x <0.5 * screenWidth) {
appliedForceType = ForceLeft;
} else {
appliedForceType = ForceRight;
}
} else {
gameBegin = true;
[backgroundstartRolling];
[self reset];
}
return YES;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchLocation = [touchlocationInView:[[CCDirector sharedDirector] view]];
touchLocation = [[CCDirector sharedDirector]convertToGL:touchLocation];
if (touchLocation.x < 0.5 * screenWidth) {
appliedForceType =ForceLeft;
} else {
appliedForceType =ForceRight;
}
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
appliedForceType = ForceNone;
}
别忘了注册代理:
- (void)onEnterTransitionDidFinish{
[[[CCDirector sharedDirector] touchDispatcher]addTargetedDelegate:self priority:1 swallowsTouches:YES];
}
- (void)onExit{
[[[CCDirector sharedDirector] touchDispatcher]removeDelegate:self];
}
这样GameLayer就只做完成了。
由于篇幅限制,我们在下一篇中完成ContactListener和Ball类的编写。
相关文章推荐
- 手把手教你制作一款Box2D小游戏(一)
- 手把手教你制作一款Box2D小游戏(三)
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第2部分
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第3部分
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第1部分
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第2部分
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第3部分
- 手把手教你制作那个风靡的flappy bird小游戏(二)
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第1部分
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第2部分
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第3部分
- 一款很有挑战下的HTML5制作的小游戏 嘿嘿~~~
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第2部分
- 如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第1部分
- Day02 骚年,玩蛇吗?(制作小游戏贪吃蛇)
- 用cocos2d 2.1制作一个过河小游戏(1): 总概
- 如何制作一款HTML5 RPG游戏引擎——第五篇,人物&人物特效
- createjs-打豆豆小游戏制作(4)