sprite kit 精靈運動

現在我們開始給忍者添加一些動作,首先從發射炮彈開始!實際上有多種方法來實現炮彈的發射,不過,在這裏要實現的方法時當用戶tap屏幕時,從忍者的方位到tap的方位發射一顆炮彈。

由於本文是針對初級開發者,所以在這裏我使用moveTo:動作來實現,不過這需要做一點點的數學運算——因爲moveTo:方法需要指定炮彈的目的地,但是又不能直接使用touch point(因爲touch point僅僅代表需要發射的方向)。實際上我們需要讓炮彈穿過touch point,直到炮彈在屏幕中消失。

如下圖,演示了上面的相關內容:

如圖所示,我們可以通過origin point到touch point得到一個小的三角形。我們要做的就是根據這個小三角形的比例創建出一個大的三角形——而你知道你想要的一個端點是離開屏幕的地方。

爲了做這個計算,如果有一些基本的矢量方法可供調用(例如矢量的加減法),那麼會非常有幫助,但很不幸的時Sprite Kit並沒有提供相關方法,所以,我們必須自己實現。

不過很幸運的時這非常容易實現。將下面的方法添加到文件的頂部(implementation之前):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static inline CGPoint rwAdd(CGPoint a, CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

static inline CGPoint rwSub(CGPoint a, CGPoint b) {
    return CGPointMake(a.x - b.x, a.y - b.y);
}

static inline CGPoint rwMult(CGPoint a, float b) {
    return CGPointMake(a.x * b, a.y * b);
}

static inline float rwLength(CGPoint a) {
    return sqrtf(a.x * a.x + a.y * a.y);
}

// Makes a vector have a length of 1
static inline CGPoint rwNormalize(CGPoint a) {
    float length = rwLength(a);
    return CGPointMake(a.x / length, a.y / length);
}

上面實現了一些標準的矢量函數。如果你看得不是太明白,請看這裏關於矢量方法的解釋

接着,在文件中添加一個新的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    // 1 - Choose one of the touches to work with
    UITouch * touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];

    // 2 - Set up initial location of projectile
    SKSpriteNode * projectile = [SKSpriteNode spriteNodeWithImageNamed:@"projectile"];
    projectile.position = self.player.position;

    // 3- Determine offset of location to projectile
    CGPoint offset = rwSub(location, projectile.position);

    // 4 - Bail out if you are shooting down or backwards
    if (offset.x <= 0) return;

    // 5 - OK to add now - we've double checked position
    [self addChild:projectile];

    // 6 - Get the direction of where to shoot
    CGPoint direction = rwNormalize(offset);

    // 7 - Make it shoot far enough to be guaranteed off screen
    CGPoint shootAmount = rwMult(direction, 1000);

    // 8 - Add the shoot amount to the current position       
    CGPoint realDest = rwAdd(shootAmount, projectile.position);

    // 9 - Create the actions
    float velocity = 480.0/1.0;
    float realMoveDuration = self.size.width / velocity;
    SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];
    SKAction * actionMoveDone = [SKAction removeFromParent];
    [projectile runAction:[SKAction sequence:@[actionMove, actionMoveDone]]];

}

上面的代碼中做了很多事情,我們來詳細看看。

  1. SpriteKit爲我們做了很棒的一件事情就是它提供了一個UITouch的category,該category中有locationInNode:previousLocationInNode:方法。這兩個方法可以幫助我們定位到在SKNode內部座標系中touch的座標位置。這樣一來,我們就可以尋得到在場景座標系中touch的位置。
  2. 然後創建一個炮彈,並將其放置到忍者的地方,以當做其開始位置。注意,現在還沒有將其添加到場景中,因爲還需要先做一個合理性的檢查——該遊戲不允許忍者向後發射。
  3. 接着利用touch位置減去炮彈的當前位置,這樣就能獲得一個從當前位置到touch位置的矢量。
  4. 如果X值小於0,就意味着忍者將要向後發射,由於在這裏的遊戲中是不允許的(真實中的忍者是不回頭的!),所以就return。
  5. 否則,將可以將炮彈添加到場景中。
  6. 調用方法rwNormalize,將offset轉換爲一個單位矢量(長度爲1)。這樣做可以讓在相同方向上,根據確定的長度來構建一個矢量更加容易(因爲1 * length = length)。
  7. 在單位矢量的方向上乘以1000。爲什麼是1000呢?因爲着肯定足夠超過屏幕邊緣了 :]
  8. 將上一步中計算得到的位置與炮彈的位置相加,以獲得炮彈最終結束的位置。
  9. 最後,參照之前構建怪物時的方法,創建moveTo:removeFromParent:兩個actions。

編譯並允許程序,現在忍者可以發射炮彈了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章