 
  萬箭齊發 Demo
這個練習主要是把前面學到的做個整合,做出一個在畫面上不斷往上發射的火箭。依然利用前的範例來修改,大致的步驟如下:- 新增一個自製的火箭 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
修改 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
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.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
回到 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]];
    }
}
記得在 init 中加入
self.touchEnabled = YES;
 圖片來源:
圖片來源:[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.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。