原文地址:http://code.zynga.com/2012/10/creating-a-game-with-cocosbuilder/
要制作的游戏类似于青蛙跳,游戏名字叫做:Cocos Dragon 废话不多,直接上截图
游戏具备的特性:
1、支持各种基本动画
2、简单的对判断进行一些处理
3、判断一些输赢
- 创建工程
首先,打开xcode,选择一个目录,创建一个cocos2d工程,名称就叫做CocosDragon。游戏的资源可以在我的资源中下载,下载之后放在目录中的Resources文件夹中。
此时,再打开cocosBiuder,同样创建一个新工程,名字也叫做CocosDragon,保存路径可以设置为cocos2d工程的Resources文件夹,这样创建之后,你就可以看到cocos2d工程中的图片资源了。当然在上层目录也可以,不过在cocosBiulder中看到的是Resources文件夹,要多点下才能看到图片资源。cocosBiulder会自动创建一些helloWorld工程,这些ccb文件和图片资源可以删除。
- 创建游戏主菜单
在cocosBiulder的菜单中,file->New->CCLayer,创建一个layer层,选择竖屏模式
选择好之后,点击create,名字保存为MainMenuScene,这样就创建了一个新的文档,在右边的界面中会自动打开。在这个空白的layer中,我们要添加一些元素,包括渐变背景色、一个程序图标、一个开始按钮以及一些可以移动变化的白云。
第一个:
渐变背景,在cocosBiulder的工具中,点击CCLayerGradient按钮,如下图,就会有一个渐变背景layer创建了。
创建之后需要设置一些属性,可以在右侧的属性视图中修改:
大小:
颜色需要设置2个,初始颜色和终止颜色
背景设置完之后,需要增加程序图标,这个很简单,在屏幕上点下,确定选择了layer,这里可能是有bug,不点击的话,可能会添加失败,然后在logo图片拖到指定的位置,位置可以通过手动调节、或者直接修改位置属性、或者功能键+上下左右键都可以。
程序图标加上之后,相对于其他程序而言有点单调,我们可以加一个动画效果,使程序载入的时候,图标看起来像是从天上掉下来的那样。
首先,我们要设置动画的总时间,点击,在弹出的对话框中将时间长度设置为00:02:00。然后选择logo图标,从cocosBiulder的菜单中,选择Animation/ Insert Keyframe /Position,或者直接按快捷键P,此时会创建一个位置帧。这是结束的位置帧,我们还需要创建一个初始的位置帧,将时间标记符,就是那个动画帧中的一个竖线,
拖到最左侧初始处,然后在图片向上拖到屏幕外,此时选择Animation/ Insert Keyframe /Position,或者直接按快捷键P,会再次创建一个位置帧。
此时你运行左下侧的播放按钮,就可以看到动画效果了。
此时动画是按照线性时间播放的,如果想要一些渐进渐出效果,以及其他一些特效,可以在两个位置帧之间的地方点击右键,在菜单中可以有多种效果可以选择,在此我们可以选择Bounce Out特效。
图标创建好了,接下来我们需要创建一个开始按钮。首先选择layer,这个可以直接在屏幕空白处点下,也可以直接在下面的列表中选择根layer,在工具中,点击,此时会创建一个菜单layer,这个不用管它,只是用来放置菜单项的。
接下来就是创建菜单项了,选择上面右侧的那个,点击下就会创建一个菜单项了,在右侧属性视图中,选择它要显示的图片,之后拖到屏幕的指定位置,方法和程序图标一样。
按钮同样可以增加一个动画,开始时从屏幕下方冒上来,方法与程序图标一样。
此时屏幕上还差一些白云没有添加。在左侧的图片中,拖一些白云图片到屏幕中,如下图一样设置好位置与层次(z-order),设置层次的方法,选择菜单Object/ Arrange / Bring Forward 或者 Arrange / Send Backward ,就可以增加或者减少层次了。
只是增加了白云还不够,我们要让他动起来。我们将要给白云增加2中动画:一个是程序载入时白云从2层移动过来,一个是屏幕显示时,白云在不停的变化大小。
程序默认有一个动画,名字叫做Default timeline,而我们需要2个,所以需要改下,点击最右侧
,在弹出的对话框中增加一个动画Loop,并把原来的名字改为intro,autoPlay可以打钩,这个属性是程序运行时决定是否自动播放还是通过代码来控制其播放。
第一个动画,在动画的菜单中选择intro,然后编辑帧,方法我就不多说了,参考程序图标的添加方法。
第二个动画,选择Loop,编辑帧的方法大致与程序图标的方法一样,只不过在选择帧的时候,要选择scale,也就是说,插入帧的时候,按S而不是P即可,而且要多插入一个中间帧,因为初始和终止帧是一样的,只有中间帧是不同的。不同的帧也不是位置不同,而是大小不同,大小可以直接在界面上拉动,也可以在属性中修改,不过我遇到一个bug,就是x,y缩放系数必须一样,否则会编译不过。
注意,有时动画的时间会很长或者很短,在下面可能会看不清楚,你可以通过拖拉上面的按钮来调节帧之间的时间间隔
还有一点,就不同动画之间的播放次序,在左下角可以设置,设置自己,就是循环播放,设置其他,就是在之之后播放,什么都不设置,就是No chained timeline,这个动画只有你来决定何时播放。
编辑完动画之后,这个主菜单已经大致可以了,现在就是需要将这个文件和程序中的控制类对应起来,我们在右侧的属性中,这次该文件对应的类名为MainMenuScene,这个名字可以随便起,不过在xcode的项目中一定要有这个类,否则你的菜单加载了也不起作用。
还有一个按钮,我们点击的时候,需要做一个处理,所以需要响应下点击事件,在属性视图中
这个场景到此就全部设定好了,现在保存下,再次我提醒下大家,一定要每个ccb文件都要单独保存一次,否则你会死的很惨。
- 游戏场景
同样的方法和设置,创建一个根Layer,命名为GameScene,保存路径不变,和主菜单保存在一起。
然后在创建一个渐变背景,与前面一样的方法,不多说了。
这个场景的东西不多,只有一个计分的标签,还有一个用来加载游戏信息的空layer,我们来加上它们。
添加空layer很简单,在工具中,点击,就创建好了,然后在右侧的代码映射属性中,选择Doc root var,并设置名字为levelLayer。
接着添加计分标签,点击,然后设置映射代码,选择Doc root var,并设置名字为scoreLabel。
接下来就是创建游戏对象了,我们的游戏对象包括5种:炸弹、金币、结束金币、小飞龙、爆炸特效.在这里抱怨下啊,对象可以设自定义置属性,但是拖到场景里面之后,这个自定义属性修改了无效。就像金币一样,设置一个结束标志,最后一个改为1即可代表结束金币,完全没有必要再创建一个结束金币对象,不能在场景中修改自定义属性是一个很不方便的事情,不同的关卡,同一个对象的自定义属性很可能不同,如果因此就要创建一个新的对象,那个实在是太麻烦了。
好了不废话了,现在开始创建龙.
- 龙
菜单中选择File/New,类型选择CCnode,去掉全屏选择,设置类名为 Dragon,同时设好映射代码类: Dragon,也即是将来你要在xcode中创建一个类,来处理相关的一些东西。
拖一张身体的图片到界面中,名字是gameobjects.plist/dragon-wing.png。
需要设置的属性
接着,托一个翅膀进来,名字为gameobjects.plist/dragon-body.png,位置设置为(0,0).
然后给这个翅膀加上动画,使指能够上下挥动,其实就是增加一个旋转动画,注意的要点就是锚点。
动画的设置方法和之前也没有大的不同,动画时间设置为1S, 设置帧的时候按R,起始帧和终止帧一样,增加一个中间帧,时间为00:00:15,角度设置为-80度
坐标的翅膀做好了,设置循环,剩下的就是右边的翅膀了,只需要将坐标的翅膀进行X翻转即可。
正常时的飞翔动画做好了,还需要做一个被炸的动画。
这个动画同前面的几种都不同,是帧动画,按F即可,在中间帧选择gameobjects.plist/dragon-body-hit.png,最终结果如下:
- 炸弹
用创建龙的方法创建一个炸弹,命名为Bomb,对应代码类为Bomb。
这个炸弹包括2个部分,一个是身体,一个是棱角,直接拖入图片,gameobjects.plist-bomb-spikes.png和gameobjects.plist/bomb-body.png。
接下来增加一个动画, 使棱角转起来,起始帧和终止帧都按R,终止帧选择360度,设置为循环。
- 金币
同样的方法,创建一个金币,命名为Coin,对应代码类设为Coin。
拖入图片 gameobjects.plist/coin01.png,然后增加一个金币翻转的动画,时间设置为00:01:06。
将时间条拉到初始帧的位置,选择coin对象,选择左侧图片中的所有帧的图片,点击右键,创建动画帧。
此时,所有的动画帧的间隔都是默认的,需要修改下时间间隔,在动画编辑栏中,用鼠标圈中所有帧,选择菜单Animation/tretch Selected Keyframes,在对话框中可以修改时间缩放系数。最后设置该动画为循环方式。
- 结束金币
这个和金币一样,可以自己创建,也可以直接复制coin的ccb文件改个名字,叫做EndCoin,不过对应的类都一样,就是需要增加一个自定义属性BOOL,isEndCoin,并设置为1
另外,为了区别与其他金币,将这个金币的颜色改下
- 爆炸效果
同样的方法,创建一个文件命名为Explosion,对应代码类为Explosion,动画效果设置为2s,点击增加2个粒子效果
现在所有的对象都创建好了,可以创建一个场景来容纳他们。
- Level层
如同创建主菜单那样,创建一个Level文件,对应代码类为Level,不过设置的大小要注意,高度设置为4096.
这个创建好了之后,就需要添加对象了,这个很简单,直接将前面创建的对象拖到这个场景中即可,随意拖放。
另外,为了在xcode中更快的访问小龙对象,我们可以选中龙,然后在其右侧的对应代码属性中,设置
最终,整个场景拖放完
现在,所有的ccb文件都已经创建好了,也保存了?那么就可以发布了,在菜单中File/Publish,就发布完毕了,此时每个ccb文件都会生成一个ccbi文件,而这个ccbi文件就是xcode需要的文件。
现在我们开始编辑xcode代码了,不过再次之前需要加载ccbi文件的解析包,这个在cocosbiulder的例子中有,直接载入工程即可,还有一点就是要在编译命令中,加入CCB_ENABLE_UNZIP.
首先,将所有的图片资源加载进来。
然后,在所有的ccbi文件加载进来。
接着,为每个ccbi文件创建对应的代码类
- MainMenuScene
继承于CCLayer。
在实现文件中,处理开始消息,这个对应与前面的play按钮。
- (void) pressedPlay:(id)sender
{
// Load the game scene
CCScene* gameScene = [CCBReader sceneWithNodeGraphFromFile:@"GameScene.ccbi"];
// Go to the game scene
[[CCDirector sharedDirector] replaceScene:gameScene];
}
另外,我们要先加载ccbi文件,可以在AppDelegate.m中,添加头文件CCBReader.h,在其加载场景的地方,替换如下代码
// Load the main menu scene from the ccbi-file
CCScene
* mainScene = [CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"];
// Then add the scene to the stack. The director will run it when it automatically when the view is displayed.
[director_ pushScene: mainScene];
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- GameScene
@interface GameScene : CCLayer
{
CCLayer* levelLayer;
CCLabelTTF* scoreLabel;
CCNode* level;
int score;
}
@property (nonatomic,assign) int score;
+ (GameScene*) sharedScene;
- (void) handleGameOver;
- (void) handleLevelComplete;
@end
实现文件中
static
GameScene* sharedScene;
+ (GameScene*) sharedScene
{
return sharedScene;
}
@synthesize score;
- (void) didLoadFromCCB
{
// Save a reference to the currently used instance of GameScene
sharedScene = self;
self.score = 0;
// Load the level
level = [CCBReader nodeGraphFromFile:@"Level.ccbi"];
// And add it to the game scene
[levelLayer addChild:level];
}
- (void) setScore:(int)s
{
score = s;
[scoreLabel setString:[NSString stringWithFormat:@"%d",s]];
}
- (void) handleGameOver
{
[[CCDirector sharedDirector] replaceScene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]];
}
- (void) handleLevelComplete
{
[[CCDirector sharedDirector] replaceScene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]];
}
- GameObject
@interface GameObject : CCNode
{
BOOL isScheduledForRemove;
}
@property (nonatomic,assign) BOOL isScheduledForRemove;
@property (nonatomic,readonly) float radius;
- (void) update;
- (void) handleCollisionWith:(GameObject*)gameObject;
@end
@implementation GameObject
@synthesize isScheduledForRemove;
// Update is called for every game object once every frame
- (void) update
{}
// If this game object has collided with another game object this method is called
- (void) handleCollisionWith:(GameObject *)gameObject
{}
// Returns the radius of this game object
- (float) radius
{ return
0;
}
@end
- Dragon
@interface Dragon : GameObject
{
float ySpeed;
float xTarget;
}
@property (nonatomic,assign) float xTarget;
@end
实现文件
#import "Dragon.h"
#import "Coin.h"
#import "Bomb.h"
#import "GameScene.h"
#import "CCBAnimationManager.h"
#define kCJStartSpeed 8
#define kCJCoinSpeed 8
#define kCJStartTarget 160
#define kCJTargetFilterFactor 0.05
#define kCJSlowDownFactor 0.995
#define kCJGravitySpeed 0.1
#define kCJGameOverSpeed -10
#define kCJDeltaToRotationFactor 5
#define kCJTargetFilterFactor 0.05
#define kCJSlowDownFactor 0.995
#define kCJGravitySpeed 0.1
#define kCJGameOverSpeed -10
#define kCJDeltaToRotationFactor 5
@synthesize xTarget;
- (id) init
{
self = [super init];
if (!self) return NULL;
xTarget = kCJStartTarget;
ySpeed = kCJStartSpeed;
return self;
}
- (void) update
{
// Calculate new position
CGPoint oldPosition = self.position;
float xNew = xTarget * kCJTargetFilterFactor + oldPosition.x * (1-kCJTargetFilterFactor);
float yNew = oldPosition.y + ySpeed; self.position = ccp(xNew,yNew);
// Update the vertical speed
ySpeed = (ySpeed - kCJGravitySpeed) * kCJSlowDownFactor;
// Tilt the dragon depending on horizontal speed
float xDelta = xNew - oldPosition.x;
self.rotation = xDelta * kCJDeltaToRotationFactor;
// Check for game over
if (ySpeed < kCJGameOverSpeed)
{
[[GameScene sharedScene] handleGameOver];
}
}
- (void) handleCollisionWith:(GameObject *)gameObject
{
if ([gameObject isKindOfClass:[Coin class]])
{
// Took a coin
ySpeed = kCJCoinSpeed;
[GameScene sharedScene].score += 1;
}
else if ([gameObject isKindOfClass:[Bomb class]])
{
// Hit a bomb
if (ySpeed > 0) ySpeed = 0;
CCBAnimationManager* animationManager = self.userObject;
NSLog(@"animationManager: %@", animationManager);
[animationManager runAnimationsForSequenceNamed:@"Hit"];
}
}
- (float) radius
{
return 25;
}
- Coin
@interface Coin : GameObject
{
BOOL isEndCoin;
}
@property (nonatomic,assign) BOOL isEndCoin;
实现文件@end
@synthesize isEndCoin;
- (void) handleCollisionWith:(GameObject *)gameObject
{
if ([gameObject isKindOfClass:[Dragon class]])
{
if (isEndCoin)
{
// Level is complete!
[[GameScene sharedScene] handleLevelComplete];
}
self.isScheduledForRemove = YES;
}
}
- (float) radius
{
return 15;
}
实现文件
- Bomb
- (void) handleCollisionWith:(GameObject *)gameObject
{
if ([gameObject isKindOfClass:[Dragon class]])
{
// Collided with the dragon, remove object and add an explosion instead
self.isScheduledForRemove = YES;
CCNode* explosion = [CCBReader nodeGraphFromFile:@"Explosion.ccbi"];
explosion.position = self.position;
[self.parent addChild:explosion];
}
}
- (float) radius
{
return 15;
}
- Explosion
#import "CCBAnimationManager.h"
@interface Explosion : GameObject <CCBAnimationManagerDelegate>
@end
实现文件
- (void) didLoadFromCCB
{
// Setup a delegate method for the animationManager of the explosion
CCBAnimationManager* animationManager = self.userObject;
animationManager.delegate = self;
}
- (void) completedAnimationSequenceNamed:(NSString *)name
{
// Remove the explosion object after the animation has finished
self.isScheduledForRemove = YES;
}
- Level
@class Dragon; @interface Level : CCLayer
{
Dragon* dragon;
}
@end
实现文件
#import "Dragon.h"
#import "GameObject.h"
#define kCJScrollFilterFactor 0.1
#define kCJDragonTargetOffset 80
- (void) onEnter
{
[super onEnter];
// Schedule a selector that is called every frame
[self schedule:@selector(update:)];
// Make sure touches are enabled
self.isTouchEnabled = YES;
}
- (void) onExit
{
[super onExit];
// Remove the scheduled selector
[self unscheduleAllSelectors];
}
- (void) update:(ccTime)delta
{
// Iterate through all objects in the level layer
CCNode* child;
CCARRAY_FOREACH(self.children, child)
{
// Check if the child is a game object
if ([child isKindOfClass:[GameObject class]])
{
GameObject* gameObject = (GameObject*)child;
// Update all game objects
[gameObject update];
// Check for collisions with dragon
if (gameObject != dragon)
{
if (ccpDistance(gameObject.position, dragon.position) < gameObject.radius + dragon.radius)
{
// Notify the game objects that they have collided
[gameObject handleCollisionWith:dragon];
[dragon handleCollisionWith:gameObject];
}
}
}
}
// Check for objects to remove
NSMutableArray* gameObjectsToRemove = [NSMutableArray array];
CCARRAY_FOREACH(self.children, child)
{
if ([child isKindOfClass:[GameObject class]])
{
GameObject* gameObject = (GameObject*)child;
if (gameObject.isScheduledForRemove)
{
[gameObjectsToRemove addObject:gameObject];
}
}
}
for (GameObject* gameObject in gameObjectsToRemove)
{
[self removeChild:gameObject cleanup:YES];
}
// Adjust the position of the layer so dragon is visible
float yTarget = kCJDragonTargetOffset - dragon.position.y;
CGPoint oldLayerPosition = self.position;
float xNew = oldLayerPosition.x;
float yNew = yTarget * kCJScrollFilterFactor + oldLayerPosition.y * (1.0f - kCJScrollFilterFactor);
self.position = ccp(xNew, yNew);
}
- (void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView: [touch view]];
dragon.xTarget = touchLocation.x;
}
- (void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView: [touch view]];
dragon.xTarget = touchLocation.x;
}