原文地址: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;
}