這個練習主要是把前面學到的做個整合,做出一個在畫面上不斷往上發射的火箭。依然利用前的範例來修改,大致的步驟如下:
- 新增一個自製的火箭 CCSprite
- 修改 GameLayer.m 將原先的 CCSprite 換成我們的,並且每秒產生一個火箭。
自製火箭 Sprite
前面的範例中,我們將全部旳程式碼都寫在 GameLayer 中,並且只產生一個火箭,現在我們要把有關火箭的資料移動到一個新的 CCSprite 類別裡,取名為 RocketSprite。選擇 XCode 的選單 File -> New -> File… -> Objective-C Class
類別名稱為 RocketSprite ,父類別為 CCSprite。
RocketSprite.h
//
// RocketSprite.h
// HelloCocos2D
//
// Created by Tony on 13/10/18.
// Copyright (c) 2013年 TonyCubeSoft. All rights reserved.
//
#import "CCSprite.h"
@interface RocketSprite : CCSprite
@property (nonatomic, assign) CGPoint startPosition;
@property (nonatomic, assign) CGPoint endPosition;
@property (nonatomic, assign) NSInteger duration;
- (id)initFromStartPosition:(CGPoint)start toEndPosition:(CGPoint)end andDuration:(NSInteger)dt;
- (void)launch;
@end
說明:startPosition 為火箭建立時的起始座標。
endPosition 為火箭的目的地座標。
duration 表示火箭從起點到終點要可以執行的時間秒數。
以上 3 個參數的不同,會讓火箭產生不同的移動速度。
init 方法用來初始化,launch 方法則是啟動火箭。
RocketSprite.m
//
// RocketSprite.m
// HelloCocos2D
//
// Created by Tony on 13/10/18.
// Copyright (c) 2013年 TonyCubeSoft. All rights reserved.
//
#import "RocketSprite.h"
#import "cocos2d.h"
@implementation RocketSprite
@synthesize startPosition, endPosition, duration;
- (id)initFromStartPosition:(CGPoint)start toEndPosition:(CGPoint)end andDuration:(NSInteger)dt
{
self = [super init];
if (self != nil) {
startPosition = start;
endPosition = end;
duration = dt;
[self setPosition:ccp(startPosition.x, startPosition.y)];
[self rocketAnimation];
}
return self;
}
- (void)rocketAnimation
{
CCAnimation *rocketAnim = [CCAnimation animation];
[rocketAnim addSpriteFrameWithFilename:@"rocket1.png"];
[rocketAnim addSpriteFrameWithFilename:@"rocket2.png"];
[rocketAnim setDelayPerUnit:0.1f];
[rocketAnim setRestoreOriginalFrame:YES];
CCAnimate *rocketAnimationAction = [CCAnimate actionWithAnimation:rocketAnim];
CCRepeatForever *repeatRocketAnimation = [CCRepeatForever actionWithAction:rocketAnimationAction];
[self runAction:repeatRocketAnimation];
}
- (void)launch
{
//movie to
CCMoveTo *moveTo = [CCMoveTo actionWithDuration:duration position:ccp(endPosition.x, endPosition.y)];
//out to die
CCCallBlockN *outToDie = [CCCallBlockN actionWithBlock:^(CCNode *node){
[node removeFromParentAndCleanup:YES];
}];
CCAction *seqAction = [CCSequence actions:moveTo, outToDie, nil];
[self runAction:seqAction];
}
@end
這個的程式大多和之前的相同,只是把火箭的部份改為 self ,因為自己本身就是火箭。
修改 GameLayer
GameLayer.m
//
// GameLayer.m
// HelloCocos2D
//
// Created by Tony on 13/10/17.
// Copyright (c) 2013年 TonyCubeSoft. All rights reserved.
//
#import "GameLayer.h"
#import "cocos2d.h"
#import "stdlib.h"
#import "RocketSprite.h"
@implementation GameLayer
- (id)init
{
if( (self=[super init]) ) {
[self schedule:@selector(rocketFactory:) interval:0.5f];
}
return self;
}
- (void)rocketFactory:(ccTime)dt
{
[self addRocket];
}
- (void)addRocket
{
CGSize size = [[CCDirector sharedDirector] winSize];
int screenW = size.width;
int screenH = size.height;
int rndX = arc4random() % screenW;
int rndY = arc4random() % screenH;
CGPoint start = CGPointMake(rndX, rndY * -1);
CGPoint end = CGPointMake(rndX, screenH + 64);//超出螢幕,火箭高度的一半
int rndDur = (arc4random() % 5) + 1;
RocketSprite *rocket = [[RocketSprite alloc] initFromStartPosition:start toEndPosition:end andDuration:rndDur];
[self addChild:rocket];
[rocket launch];
}
@end
GameLayer 現在變得清爽多了,除了觸碰的部份被拿掉,其餘有關火箭的部份被移到 RocketSprite 中,所以 GameLayer 現在只做一件事,定期產生火箭並發射。schedule 方法用來每隔一段時間執行一次所指定的方法。這邊是指定每 0.5 秒執行一次 rocketFactory 方法,而這個方法就只是新增一個火箭而已。
addRocket 的部份,要設定火箭的起點和終點,我們以螢幕尺寸為主要參考依據。x 座標以隨機方式取得螢幕寬度之內的一個值,y 座標則是以螢幕高度來取隨機值。
因為我們要讓火箭往上直直的前進,所以起點及終點的 x 值是相同的。起點的座標乘 -1 是為了讓它在螢幕下方產生,才不會憑空出現。終點則是螢幕的高度加上火箭高度的一半,也就是出了螢幕之後才 remove 掉。
火箭移動的時間也是用隨機的。但是因為 arch4random() % 5 所產生的隨機值是 0~4,而時間如果設為 0 則什麼都看不到就跑完了,所以必須加 1 讓值變為 1~5。
最後建立我們的 RocketSprite,並把參數帶入,加到圖層中,再呼叫 launch 啟動火箭。 完成後看起來是這樣
讓火箭速度一致
在這個練習中,火箭的移動時間是用隨機的,所以移動時有快有慢,加上每個火箭的起點不同,所以移動的距離也不同,因此也會影響移動的快慢(即使每個都設一樣的移動時間)。但是有時候我們必須讓每個發射體維持一樣的速度,例如子彈,這時候就要利用速度公式來算出移動所需的時間。速度公式:v = r / t請參考 wiki 速度
速度等於距離除以時間,可是我們要的是時間,因此轉換成「時間 = 距離/速度」
所以 addRocket 中部份程式碼修改如下:
//int rndDur = (arc4random() % 5) + 1;
//t = r/v
//距離為火箭的終點﹣起點
float dist = (screenH + 64) - (rndY * -1);
//速度設為 100 (即 1 秒移動 100px)
float velocity = 100.0f;
float time = dist / velocity;
RocketSprite *rocket = [[RocketSprite alloc] initFromStartPosition:start toEndPosition:end andDuration:time];
原本的隨機時間註解掉,改用新的計算過的時間。接著把 RocketSprite 中的 init 最後的時間參數由 NSInteger 改為 float
- (id)initFromStartPosition:(CGPoint)start toEndPosition:(CGPoint)end andDuration:(float)dt;
切換場景
現在,我們要讓遊戲結束,所以要把場景由現在的遊戲場景切換到結束場景。其實只要簡單的一行程式碼:
[[CCDirector sharedDirector] replaceScene:[GameOverScene node]];
我們快速的做出兩組檔案,一個是 GameOverScene,用來顯示 GameOverLayer,程式碼如下:
GameOverScene.m
//
// GameOverScene.m
// HelloCocos2D
//
// Created by Tony on 13/10/22.
// Copyright (c) 2013年 TonyCubeSoft. All rights reserved.
//
#import "GameOverScene.h"
#import "cocos2d.h"
#import "GameOverLayer.h"
@implementation GameOverScene
- (id)init
{
self = [super init];
if (self != nil) {
GameOverLayer *overLayer = [GameOverLayer node];
[self addChild:overLayer];
}
return self;
}
@end
GameOverLayer.m
//
// GameOverLayer.m
// HelloCocos2D
//
// Created by Tony on 13/10/22.
// Copyright (c) 2013年 TonyCubeSoft. All rights reserved.
//
#import "GameOverLayer.h"
#import "cocos2d.h"
@implementation GameOverLayer
- (id)init
{
self = [super init];
if (self != nil) {
CCSprite *backgroundSprite = [CCSprite spriteWithFile:@"gameover.png"];
CGSize size = [[CCDirector sharedDirector] winSize];
[backgroundSprite setPosition:ccp(size.width/2, size.height/2)];
[self addChild:backgroundSprite z:0 tag:0];
CCLabelTTF *lblOver = [CCLabelTTF labelWithString:@"Game Over" fontName:@"Marker Felt" fontSize:50];
lblOver.position = ccp(size.width - lblOver.contentSize.width/2 - 30, lblOver.contentSize.height/2 + 30);
[self addChild:lblOver z:1];
}
return self;
}
@end
在一張底圖上放上 "Game Over"文字。回到 GameLayer.m 加入觸碰事件。首先加入一個 Exit 的文字,讓我們可以去觸碰,lblExit 為成員變數
- (void)addExitButton
{
lblExit = [CCLabelTTF labelWithString:@"Exit" fontName:@"Marker Felt" fontSize:35];
lblExit.position = ccp(lblExit.contentSize.width/2, lblExit.contentSize.height/2);
[self addChild:lblExit];
}
接著加入觸碰事件
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@">>> touch");
UITouch *touch = [touches anyObject];
CGPoint point = [self convertTouchToNodeSpace:touch];
if (CGRectContainsPoint([lblExit boundingBox], point)) {
[self stopSound];
[[CCDirector sharedDirector] replaceScene:[GameOverScene node]];
}
}
取得觸碰物件並轉換座標空間,接著去判斷觸碰點是否在 lblExit 矩形範圍內,若是就停止背景聲音並切換場景。記得在 init 中加入
self.touchEnabled = YES;
這樣就完成了。只要觸碰 lblExit 文字,就會遊戲結束,並切換到 GameOver 場景,如下圖:
圖片來源:[1]http://bloody-disgusting.com/news/3233794/alien-pic-newcomers-shooting-footage-from-space/
[2]http://mysteriousuniverse.org/2013/05/the-pros-and-cons-of-being-a-cosmonaut/
我要留言
留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。