注:本文譯自Sprite Kit Tutorial for Beginners
目錄
Sprite Kit的優點和缺點
Sprite Kit vs Cocos2D-iPhone vs Cocos2D-X vs Unity
Hello, Sprite Kit!
橫屏顯示
移動怪獸
發射炮彈
碰撞檢測: 概述
碰撞檢測: 實現
收尾
何去何從?
橫屏顯示
首先,在Project Navigator中單擊SpriteKitSimpleGame工程以打開target設置,選中SpriteKitSimpleGame target。然後在Deployment Info中,不要勾選Portrait,只選中Landscape和Landscape Right,如下所示:
編譯並運行工程,會看到如下運行畫面:
下面我們試著添加一個忍者(ninja)。
首先,下載此工程的資源文件,並將其拖拽到Xcode工程中。確保勾選上“Copy items into destination group’s folder (if needed)”和SpriteKitSimpleGame target。
接著,打開MyScene.m,並用下面的內容替換之:
#import "MyScene.h"
// 1
@interface MyScene ()
@property (nonatomic) SKSpriteNode * player;
@end
@implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
// 2
NSLog(@"Size: %@", NSStringFromCGSize(size));
// 3
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// 4
self.player = [SKSpriteNode spriteNodeWithImageNamed:@"player"];
self.player.position = CGPointMake(100, 100);
[self addChild:self.player];
}
return self;
}
@end
我們來看看上面的代碼。
為了給player(例如忍者)聲明一個私有變量,在這裡創建了一個私有的interface,之後可以把這個私有變量添加到場景中。
在這裡打印出了場景的size,至於什麼原因很快你就會看到了。
在Sprite Kit中設置一個場景的背景色非常簡單——只需要設置backgroundColor屬性,在這裡將其設置位白色。
在Sprite Kit場景中添加一個精靈同樣非常簡單,只需要使用spriteNodeWithImageNamed方法,並把一副圖片的名稱傳遞進去就可以創建一個精靈。接著設置一下精靈的位置,然後調用addChild方法將該精靈添加到場景中。在代碼中將忍者的位置設置為(100, 100),該位置是從屏幕的左下角到右上角計算的。
編譯並運行,看看效果如何…
呀!屏幕是白色的,並沒有看到忍者。這是為什麼呢?你可能在想設計之初就是這樣的,實際上這裡有一個問題。
如果你觀察一下控制台輸出的內容,會看到如下內容
1
SpriteKitSimpleGame[3139:907] Size: {320, 568}
可能你會認為場景的寬度是320,高度則是568——實際上剛好相反!
我們來看看具體發生了什麼:定位到ViewController.m的viewDidLoad方法:
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
上面的代碼中利用view的邊界size創建了場景。不過請注意,當viewDidLoad被調用的時候,在這之前view已經被添加到view層次結構中了,因此它還沒有響應出布局的改變。所以view的邊界可能還不正確,進而在viewDidLoad中並不是開啟場景的最佳時機。
提醒:要想了解更多相關內容,請看由Rob Mayoff帶來的最佳解釋。
解決方法就是將開啟場景代碼的過程再靠後一點。用下面的代碼替換viewDidLoad:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}
編譯並運行程序,可以看到,忍者已經顯示在屏幕中了!
如上圖所示,可以看到坐標系已經正確了,如果想要把忍者的位置設置為其中間靠左,那麼在MyScene.m中用下面的代碼來替換設置忍者位置相關的代碼:
1
self.player.position = CGPointMake(self.player.size.width/2, self.frame.size.height/2);
移動怪獸
接下來,我們希望在場景中添加一些怪獸,讓忍者進行攻擊。為了讓游戲更有趣一點,希望怪獸能夠移動——否則沒有太大的挑戰!OK,我們就在屏幕的右邊,離屏的方式創建怪獸,並給怪獸設置一個動作:告訴它們往左邊移動。
將下面這個方法添加到MyScene.m中:
- (void)addMonster {
// Create sprite
SKSpriteNode * monster = [SKSpriteNode spriteNodeWithImageNamed:@"monster"];
// Determine where to spawn the monster along the Y axis
int minY = monster.size.height / 2;
int maxY = self.frame.size.height - monster.size.height / 2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
// Create the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
monster.position = CGPointMake(self.frame.size.width + monster.size.width/2, actualY);
[self addChild:monster];
// Determine speed of the monster
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(-monster.size.width/2, actualY) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[monster runAction:[SKAction sequence:@[actionMove, actionMoveDone]]];
}
在上面,我盡量讓代碼看起來容易理解。首先是通過一個簡單的計算,確定怪獸出現的位置,並將該位置設置給怪獸,然後將其添加到場景中。
接著是添加動作(actions)。跟Cocos2D一樣,Sprite Kit同樣提供了很多方便的內置動作,例如移動動作、旋轉動作、淡入淡出動作、動畫動作等。在這裡我們只需要在怪獸上使用3中動作即可:
moveTo:duration:使用這個動作可以把怪獸從屏幕外邊移動到左邊。移動過程中,我們可以指定移動持續的時間,上面的代碼中,指定為2-4秒之間的一個隨機數。
removeFromParent:在Sprite Kit中,可以使用該方法,方便的將某個node從parent中移除,能有效的從場景中刪除某個對象。此處,將不再需要顯示的怪獸從場景中移除。這個功能非常的重要,否則當有源源不斷的怪獸出現在場景中時,會耗盡設備的所有資源。
sequence:sequence動作可以一次性就把一系列動作串聯起來按照一定順序執行。通過該方法我們就能讓moveTo:方法先執行,當完成之後,在執行removeFromParent:動作。
最後,我們需要做的事情就是調用上面這個方法addMonster,以實際的創建出怪獸!為了更加好玩,下面我們來讓怪獸隨著時間持續的出現在屏幕中。
在Sprite Kit中,並不能像Cocos2D一樣,可以配置每隔X秒就回調一下update方法。同樣也不支持將從上次更新到目前為止的時間差傳入方法中。(非常令人吃驚!)。
不過,我們可以通過一小段代碼來仿造這種行為。首先在MyScene.m的private interface中添加如下屬性:
@property (nonatomic) NSTimeInterval lastSpawnTimeInterval;
@property (nonatomic) NSTimeInterval lastUpdateTimeInterval;
通過lastSpawnTimeInterval可以記錄著最近出現怪獸時的時間,而lastUpdateTimeInterval可以記錄著上次更新時的時間。
接著,我們寫一個方法,該方法在畫面每一幀更新的時候都會被調用。記住,該方法不會被自動調用——需要另外寫一個方法來調用它:
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 1) {
self.lastSpawnTimeInterval = 0;
[self addMonster];
}
}
上面的代碼中簡單的將上次更新(update調用)的時間追加到self.lastSpawnTimeInterval中。一旦該時間大於1秒,就在場景中新增一個怪獸,並將lastSpawnTimeInterval重置。
最後,添加如下方法來調用上面的方法:
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
Sprite Kit在顯示每幀時都會調用上面的update:方法。
上面的代碼其實是來自蘋果提供的Adventure示例中。該方法會傳入當前的時間,在其中,會做一些計算,以確定出上一幀更新的時間。注意,在代碼中做了一些合理性的檢查,以避免從上一幀更新到現在已經過去了大量時間,並且將間隔重置為1/60秒,避免出現奇怪的行為。
現在編譯並運行程序,可以看到許多怪獸從左邊移動到屏幕右邊並消失。