作者:Ray Wenderlich
原文出处:点击打开链接
http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners
Sprite Kit是一个在iOS7上制作令人惊喜的2D游戏的新框架,它内置于iOS7 SDK。 它拥有材质精灵(以下将直接引用sprite),支持很酷的特效,比如视频、滤镜、遮罩等,内置了物理引擎库,还有很多其他的东西。
iOS7本来有一个很棒的Sprite Kit范例项目了,叫做冒险(Adventure),你可以马上将其下载下来。但是这个游戏有点复杂,而更多时候你需要的是一个越简单越好的例子来作为入门学习。这就是这篇教程的来由。
在这篇Sprite Kit的初学者教程里,你会从头到尾系统地学到如何为iPhone创建一个简单而有意思的2D游戏。如果你看过我们 Simple
Cocos2D game 这篇教程, 这个游戏可能看起来很相似 :)
在开始之前你需要确保自己安装了最新版本的Xcode(5.X),它包含了对Sprite Kit和iOS7的支持。
对了,你可以先看教程,也可以直接跳到教程结尾运行以下完整的样例项目。如果你这样做的话将会看到忍者哦。
在开始之前,我想先指出Sprite Kit 并不是你在iOS平台上制作2D游戏的唯一选择,而且它有一些优缺点是你需要事先注意的。
之后我想再回顾一下iOS上制作2D游戏其他的三种在选择并且与Sprite Kit比较一下各自的优缺点。
Sprite Kit 优点
Sprite Kit 缺点
现在很多人会有疑问:“那么我到底该选择哪个2D游戏引擎呢?”
你需要根据自己的目的做出选择。这是我的观点:
在你看完以上的所有内容后,如果你认为Sprite Kit可能正是你要寻找的东西,请继续你的阅读,我们将正式开始Sprite Kit的教程。
让我们从创建一个简单的Hello World 项目开始,它是用Xcode5内置的Sprite Kit模版创建的。
打开Xcode,选择FileNewProject,接下来选择iOSApplicationSprite Kit Game 模版,然后单击Next:
键入“SpriteKitSimpleGame”做为Product Name,设备选择iPhone,然后单击Next:
把项目保存在你硬盘上的某个位置,然后单击 Create。随后单击运行这个项目。你应该能看到下面的界面:
就像Cocos2D一样,Sprite Kit被组织在scene(场景)之上。scene是一种类似于“层级”或者“屏幕”的概念。举个例子,你可以同时创建两个scene,一个位于游戏的主显示区域,一个可以用作游戏地图展示放在其他区域,两者是并列的关系。
如果你,你会发现Sprite Kit的模版已经默认为你新建了一个scene——MyScene。打开MyScene.m 文件你会看到它包含了一些代码,这些代码实现了两个功能,把一个label放到屏幕上以及在屏幕上随意点按时添加旋转的飞船。
在这篇教程里,你将主要与MyScene打交道。但是在开始之前,你需要做一些小的改动,使得我们的游戏在横评下运行(替代默认的竖屏)。
首先,打开Xcode中target的设定:在项目导航栏中单击SpriteKitSimpleGame项目,选中对应的target。然后在Deployment Info区域内取消Orientation中Portrait(竖屏)的勾选,这样就只有Landscape
Left 和 Landscape Right 是被选中的了,如下图所示:
编译运行项目,你会看到刚刚做的改动已经顺利完成并且生效了:
然而,事实并不如此。让我们试着添加忍者到游戏中来看看为什么这样说,到底还有什么问题呢?
首先,下载 这个项目的资源文件 并且把它们拖拽到Xcode项目中。请在拖拽后弹出的对话框中确保勾选了这个选项:“Copy
items into destination group’s folder (if needed)(复制所有文件到目标group所在的文件夹)”,同时项目target也要被选中。
下一步,打开MyScene.m并且用下面的代码替换掉它原有的内容:
让我们一步一步解释下上面的代码。
编译运行,然后。。。
不对啊,屏幕白茫茫一片,没有忍者。你可能认为就是这样设计的,但这其实是一个有待解决的问题。如果你观察下刚刚在控制台输出的内容,你会看到下面的输出:
因此我们的scene 认为它的宽是320而高是568,但这恰好反了。
为了看看到底发生了什么,我们找到ViewController.m 的viewDidLoad方法:
这 里从skView的bounds属性获取了size,创建了相应大小的scene。然而,当viewDidLoad方法被调用时,skView还没有被加 到view的层级结构上,因而它不能相应方向以及布局的改变。所以skView的bounds属性此时还不是它横屏后的正确值,而是默认竖屏所对应的值, 看来这个时候不是初始化scene的好时机。
Note: 有关这个现象的更多细节,请参考Rob Mayoff 的 这个很赞的解释。
解决办法是把初始化代码的运行时机后移。请用下面这个方法替换viewDidLoad:
再次编译运行,当当当当,女士们、先生们,忍者终于出现了!
现在游戏的坐标系统已经一切正常,你会把这个忍者放在他应该放的位置,也就是在屏幕左侧面朝中央。为了做这些,切换回MyScene.m并且用下面的代码替换掉已有的那一行设置了忍者位置的代码:
移动的怪物
下一步将要把一些怪物添加到scene上,与现有的忍者形成战斗场景。为了使游戏更有意思,怪兽应该是移动的,否则游戏就毫无挑战性可言了!那么让我们在屏幕的右侧一点创建怪兽们,然后为它们设置action使它们能够向左移动。
在MyScene.m中添加如下方法:
我会慢一点把代码讲解清楚,让其尽可能容易理解。第一部分正如之前提到过的:我们需要做一些简单的计算来创建怪物对象。为它们设置合适的位置并且用和忍者sprite(player)一样的方式把它们添加到scene上。在相应的位置出现。
接下来轮到添加actions了。像Cocos2D一样,Sprite Kit提供了一些超级实用的内置actions,比如移动、旋转、淡出、动画等等。这里要在怪物身上添加3种aciton:
moveTo:
这个action,让怪物先移动
,当移动结束时继续运行removeFromParent:
这个action把怪物从scene上移除。
别忘了还有件事没做呢,你需要调用addMonster方法来创建怪物!为了让游戏再有趣一点,我们让怪物们持续不断地涌现出来。
Sprite Kit不能像Cocos2D一样设置一个每几秒运行一次的回调方法。它也不能传递一个增量时间参数给update方法。然而我们可以用一小段代码来模仿类似的定时刷新方法。首先把这些属性添加到MyScene.m的私有声明里:
我们会使用lastSpawnTimeInterval这个属性来记录上一次生成怪物的时间,
使用lastUpdateTimeInterval这个属性来记录上一次更新的时间。
下一步,你会编写一个每帧都会调用的方法。这个方法的参数是上次更新后的时间增量。由于它不会被默认调用,你需要在下一步编写另一个方法来调用它。
在这里你只是简单地把上次更新后的时间增量加给lastSpawnTimeInterval。一旦它的值大于一秒,你就要生成一个怪物然后重置时间。
接下来,添加如下方法来调用上面的updateWithTimeSinceLastUpdate方法 。
update: Sprite Kit会在每帧自动调用这个方法。
这里的代码实际上源自苹果的Adventure范例。它传入当前的时间,我们可以据此来计算出上次更新后的时间增量。值得注意的是这里做了一些必要的检查,如果出现意外致使更新的时间间隔变得超过1秒,这里会把间隔重置为1/60秒来避免奇怪的情况发生。
就是这样,编译运行之,现在你应该看到怪物们在屏幕上欢快地移动着:
到这里,你可以已经迫不及待的为忍者添加一些动作了,那么我们就添加攻击吧。攻击的实现方式有很多种,但在这个游戏里攻击会在玩家点击屏幕时触发,忍者会朝着点按的方向发射一个子弹。
我打算使用moveTo:action动作来实现子弹的前期运行动画,为了实现它需要一些数学运算。这是因为moveTo:需要传入子弹运行轨迹的终点,
由于用户点按触发的位置仅仅代表了子弹射出的方向,显然我们不能直接将其当作运行终点。实际上就算子弹超过了触摸点你也应该让子弹保持移动直到子弹超出屏
幕为止。
这是一张图片,它标注了这个问题:
就像你看到的,从子弹发射原点到用户触摸点在x轴和y轴上的偏移量会形成一个小三角形。你只要以相同的比例去实现一个顶点在屏幕边缘的大三角形即可。
为了进行这部分的运算,有一些关于向量的基本数学计算方法很有帮助(比如向量间的加减法)。然而,Sprite Kit默认并没有提供,所以你需要自己来实现了。
幸运的是这很容易实现。把下面的方法添加到文件顶部:
这些是向量运算方法的标准实现。如果你对此感到疑惑或者没有学习过向量的数学知识,可以到这里恶补一下 vector
math explanation.
下一步,添加一个新方法:
到这里已经做了很多事情,我们来一步一步回顾一下。
moveTo:
和removeFromParent
这两个action。
现在游戏里有了满天飞的手裏剑,但是你的忍者真正要做的是把怪物打下来。所以让我们添加一些代码来监测子弹是否打到了目标。
Sprite Kit一个好处是它已经内置了物理引擎。物理引擎不仅仅非常有助于模拟现实中的移动,同时也对碰撞监测提供了很好的支持。
让我们把Sprite Kit的物理引擎引入到游戏中来监测怪物和子弹的碰撞。大体上讲,下面是你准备要做的:
现在你理解了战斗(指子弹打怪物的过程)的计划,是时候付诸行动了!
So now you have shurikens flying everywhere – but what your ninja really wants to do is to lay some smack down. So let’s add in some code to detect when your projectiles intersect your targets.
There are various ways to solve this with Cocos2D, including using one of the included physics libraries: Box2D or Chipmunk. However to keep things simple, you are going to implement simple collision detection yourself.
To do this, you first need to keep better track of the targets and projectiles currently in the scene. Add the following to your HelloWorldLayer class declaration:
这里设置了两个数组,等下就会用到。一个是子弹的,一个是怪物的。
NSMutableArray * _monsters; NSMutableArray * _projectiles; |
And initialize the arrays in your init method:
在你的场景初始化方法里初始化这些数组
_monsters = [[NSMutableArray alloc] init];
_projectiles = [[NSMutableArray alloc] init];
|
And while you’re thinking of it, clean up the memory in your dealloc method (at the time of writing this tutorial, ARC isn’t enabled by default in the Cocos2D 2.X template):
趁你还在思考这个,别忘记在dealloc方法里清除内存把,(这个教程里,Cocos2D 2.X 模板不支持ARC)
[_monsters release];
_monsters = nil;
[_projectiles release];
_projectiles = nil;
|
Note: Even though ARC isn’t enabled by default in the Cocos2D template, it is very easy to do so. To learn how, check out this tutorial.
Now, modify your addMonster method to add the new monster to the monsters array and set a tag for future use:
monster.tag = 1;
[_monsters addObject:monster];
|
And modify your ccTouchesEnded method to add the new projectile to the projectiles array and set a tag for future use:
projectile.tag = 2;
[_projectiles addObject:projectile];
|
Finally, modify your both of your CCCallBlockN blocks to remove the sprite from the appropriate array:
// CCCallBlockN in addMonster
[_monsters removeObject:node];
// CCCallBlockN in ccTouchesEnded
[_projectiles removeObject:node];
|
Build and run the project to make sure everything is still working OK. There should be no noticeable difference at this point, but now you have the bookkeeping you need to implement some collision detection.
Now add the following new method:
- (void)update:(ccTime)dt {
NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];
for (CCSprite *projectile in _projectiles) {
NSMutableArray *monstersToDelete = [[NSMutableArray alloc] init];
for (CCSprite *monster in _monsters) {
if (CGRectIntersectsRect(projectile.boundingBox, monster.boundingBox)) {
[monstersToDelete addObject:monster];
}
}
for (CCSprite *monster in monstersToDelete) {
[_monsters removeObject:monster];
[self removeChild:monster cleanup:YES];
}
if (monstersToDelete.count > 0) {
[projectilesToDelete addObject:projectile];
}
[monstersToDelete release];
}
for (CCSprite *projectile in projectilesToDelete) {
[_projectiles removeObject:projectile];
[self removeChild:projectile cleanup:YES];
}
[projectilesToDelete release];
}
|
The above should be pretty clear. You just iterate through your projectiles and monsters, creating rectangles corresponding to their bounding boxes, and use CGRectIntersectsRect to check for intersections. If any are found, you remove them from the scene and from the arrays.
Note that you have to add the objects to a “toDelete” array because you can’t remove an object from an array while you are iterating through it. Again, there are more optimal ways to implement this kind of thing, but you are going for the simple approach.
You just need one more thing before you’re ready to roll – schedule this method to run as often as possible by adding the following line to your init method:
[self schedule:@selector(update:)];
|
Give it a build and run, and now when your projectiles intersect targets they should disappear!
You’re pretty close to having a workable (but extremely simple) game now. You just need to add some sound effects and music (since what kind of game doesn’t have sound!) and some simple game logic.
If you’ve read our blog series on audio programming for the iPhone, you’ll be extremely pleased to hear how simple the Cocos2D developers have made it to play basic sound effects in your game.
You already have some cool background music I made and an awesoem pew-pew sound effect in your project, from the resources for this tutorial you downloaded earlier. You just need to play them!
To do this, add the following import to the top of HelloWorldLayer.m:
#import "SimpleAudioEngine.h"
|
In your init method, start up the background music as follows:
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"];
|
And in your ccTouchesEnded method play the sound effect as follows:
[[SimpleAudioEngine sharedEngine] playEffect:@"pew-pew-lei.caf"];
|
Now, let’s create a new scene and layer that will serve as your “You Win” or “You Lose” indicator. Create a new file with the iOS\cocos2d v2.x\CCNode class template, make it a subclass of CCLayerColor, and click Next. Name it GameOverLayer, and click Create.
Then replace GameOverLayer.h with the following code:
#import "cocos2d.h"
@interface GameOverLayer : CCLayerColor
+(CCScene *) sceneWithWon:(BOOL)won;
- (id)initWithWon:(BOOL)won;
@end
|
Then replace GameOverLayer.m with the following code:
#import "GameOverLayer.h"
#import "HelloWorldLayer.h"
@implementation GameOverLayer
+(CCScene *) sceneWithWon:(BOOL)won {
CCScene *scene = [CCScene node];
GameOverLayer *layer = [[[GameOverLayer alloc] initWithWon:won] autorelease];
[scene addChild: layer];
return scene;
}
- (id)initWithWon:(BOOL)won {
if ((self = [super initWithColor:ccc4(255, 255, 255, 255)])) {
NSString * message;
if (won) {
message = @"You Won!";
} else {
message = @"You Lose :[";
}
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCLabelTTF * label = [CCLabelTTF labelWithString:message fontName:@"Arial" fontSize:32];
label.color = ccc3(0,0,0);
label.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:label];
[self runAction:
[CCSequence actions:
[CCDelayTime actionWithDuration:3],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[[CCDirector sharedDirector] replaceScene:[HelloWorldLayer scene]];
}],
nil]];
}
return self;
}
@end
|
Note that there are two different objects here: a scene and a layer. The scene can contain any number of layers, however in this example it just has one. The layer just puts a label in the middle of the screen, and schedules a transition to occur 3 seconds in the future back to the Hello World scene.
Finally, let’s add some extremely basic game logic. First, let’s keep track of the projectiles the player has destroyed. Add a member variable to your HelloWorldLayer class in HelloWorldLayer.h as follows:
int _monstersDestroyed;
|
Inside HelloWorldLayer.m, add an import for the GameOverScene class:
#import "GameOverLayer.h"
|
Increment the count and check for the win condition in your update method inside the monstersToDelete loop right after removeChild:monster:
_monstersDestroyed++;
if (_monstersDestroyed > 30) {
CCScene *gameOverScene = [GameOverLayer sceneWithWon:YES];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
|
And finally let’s make it so that if even one target gets by, you lose. In addMonster:’s CCCallBlockN callback, add the following right after removeFromParentAndCleanup:
CCScene *gameOverScene = [GameOverLayer sceneWithWon:NO];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
|
Go ahead and give it a build and run, and you should now have win and lose conditions and see a game over scene when appropriate!
And that’s a wrap! Here’s the full code for the simple Cocos2D iPhone game that you developed thus far.
This project could be a nice basis for playing around some more with Cocos2D by adding some new features into the project. Maybe try adding in a bar chart to show how many more targets you have to destroy before you win (check out the drawPrimitivesTest sample project for examples of how to do that). Maybe add cooler death animations for when the monsters are destroyed (see ActionsTest, EffectsTest, and EffectsAdvancedTest projects for that). Maybe add more sounds, artwork, or gameplay logic just for fun. The sky’s the limit!
If you want to keep going with this tutorial series, check out part two, How To Add A Rotating Turret, or part three, Harder Monsters and More Levels!
Also, if you’d like to keep learning more about Cocos2D, we have ton of Cocos2D tutorials on this site – this is the right place to be! :]
If you have any questions or comments about this tutorial, please join the discussion below!
让我们添加两个常量开始。将它们添加到MyScene.m中:
Start by adding these two constants to the top of MyScene.m:
static const uint32_t projectileCategory = 0x1 << 0;
static const uint32_t monsterCategory = 0x1 << 1;
|
This is setting up the two categories you’ll need in a bit – one for the projectiles and one for the monsters.
Note: You may be wondering what the fancy syntax is here. Note that the category on Sprite Kit is just a single 32-bit integer, and acts as a bitmask. This is a fancy way of saying each of the 32-bits in the integer represents a single category (and hence you can have 32 categories max). Here you’re setting the first bit to indicate a projectile, and the next bit over to represent a monster.
Next, inside initWithSize add these lines after adding the player to the scene:
self.physicsWorld.gravity = CGVectorMake(0,0);
self.physicsWorld.contactDelegate = self;
|
This sets up the physics world to have no gravity, and sets the scene as the delegate to be notified when two physics bodies collide.
Inside the addMonster method, add these lines right after creating the monster sprite:
monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:monster.size]; // 1
monster.physicsBody.dynamic = YES; // 2
monster.physicsBody.categoryBitMask = monsterCategory; // 3
monster.physicsBody.contactTestBitMask = projectileCategory; // 4
monster.physicsBody.collisionBitMask = 0; // 5
|
Let’s go over what this does line by line.
monsterCategory
you defined earlier.contactTestBitMask
indicates what categories of objects this object should notify the contact listener when they intersect. You choose projectiles here.collisionBitMask
indicates what categories of objects this object that the physics engine handle contact responses to (i.e. bounce off of). You don’t want the monster and projectile to bounce off each other – it’s OK for them to go right through each other in this game – so you set this to 0.Next add some similar code to touchesEnded:withEvent:, right after the line setting the projectile’s position:
projectile.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:projectile.size.width/2];
projectile.physicsBody.dynamic = YES;
projectile.physicsBody.categoryBitMask = projectileCategory;
projectile.physicsBody.contactTestBitMask = monsterCategory;
projectile.physicsBody.collisionBitMask = 0;
projectile.physicsBody.usesPreciseCollisionDetection = YES;
|
As a test, see if you can understand each line here and what it does. If not, just refer back to the points explained above!
As a second test, see if you can spot two differences. Answer below!
Solution Inside: What Are the Differences? | Show |
---|---|
Next, add a method that will be called when the projectile collides with the monster. Note that nothing calls this automatically, you will be calling this later.
- (void)projectile:(SKSpriteNode *)projectile didCollideWithMonster:(SKSpriteNode *)monster {
NSLog(@"Hit");
[projectile removeFromParent];
[monster removeFromParent];
}
|
All you do here is remove the projectile and monster from the scene when they collide. Pretty simple, eh?
Now it’s time to implement the contact delegate method. Add the following new method to the file:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
// 1
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
// 2
if ((firstBody.categoryBitMask & projectileCategory) != 0 &&
(secondBody.categoryBitMask & monsterCategory) != 0)
{
[self projectile:(SKSpriteNode *) firstBody.node didCollideWithMonster:(SKSpriteNode *) secondBody.node];
}
}
|
Since you set the scene as the contactDelegate
of the physics world earlier, this method will be called whenever two physics bodies collide (and their contactTestBitMask
s are set appropriately).
There are two parts to this method:
One last step – mark the private interface as implementing SKPhysicsContactDelegate
to make the compiler happy:
@interface MyScene () <SKPhysicsContactDelegate>
|
Give it a build and run, and now when your projectiles intersect targets they should disappear!
You’re pretty close to having a workable (but extremely simple) game now. You just need to add some sound effects and music (since what kind of game doesn’t have sound!) and some simple game logic.
Sprite Kit does not come with an audio engine like Cocos2D does, but the good news it does come with a simple way to play sound effects via actions, and that you can play background music pretty easily with AVFoundation.
You already have some cool background music I made and an awesome pew-pew sound effect in your project, from the resources for this tutorial you added to your project earlier. You just need to play them!
To do this, add the following import to the top of ViewController.m:
@import AVFoundation; |
This demonstrates the new modules feature in iOS 7 – basically by using the new @import keyword, you can bring in the header files (and library) for a framework in a single, efficient step. To learn more about this, check out Chapter 10 in iOS 7 by Tutorials, What’s New with Objective-C and Foundation.
Next add a new property and private interface:
@interface ViewController ()
@property (nonatomic) AVAudioPlayer * backgroundMusicPlayer;
@end
|
Add this code to viewWillLayoutSubviews, right after the call to [super viewWillLayoutSubviews]
:
NSError *error; NSURL * backgroundMusicURL = [[NSBundle mainBundle] URLForResource:@"background-music-aac" withExtension:@"caf"]; self.backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:backgroundMusicURL error:&error]; self.backgroundMusicPlayer.numberOfLoops = -1; [self.backgroundMusicPlayer prepareToPlay]; [self.backgroundMusicPlayer play]; |
This is some simple code to start the background music playing with endless loops.
As for the sound effect, switch back to MyScene.m and add this line to the top of touchesEnded:withEvent::
[self runAction:[SKAction playSoundFileNamed:@"pew-pew-lei.caf" waitForCompletion:NO]];
|
Pretty handy, eh? You can play a sound effect with one line!
Now, let’s create a new scene and layer that will serve as your “You Win” or “You Lose” indicator. Create a new file with the iOS\Cocoa Touch\Objective-C class template, name the class GameOverScene, make it a subclass of SKScene, and click Next and then Create.
Then replace GameOverScene.h with the following code:
#import <SpriteKit/SpriteKit.h>
@interface GameOverScene : SKScene
-(id)initWithSize:(CGSize)size won:(BOOL)won;
@end
|
Here you imported the Sprite Kit header and marked that you are implementing a special initializer that takes a parameter of whether the user won the level or not in addition to the size.
Then replace GameOverLayer.m with the following code:
#import "GameOverScene.h"
#import "MyScene.h"
@implementation GameOverScene
-(id)initWithSize:(CGSize)size won:(BOOL)won {
if (self = [super initWithSize:size]) {
// 1
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// 2
NSString * message;
if (won) {
message = @"You Won!";
} else {
message = @"You Lose :[";
}
// 3
SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
label.text = message;
label.fontSize = 40;
label.fontColor = [SKColor blackColor];
label.position = CGPointMake(self.size.width/2, self.size.height/2);
[self addChild:label];
// 4
[self runAction:
[SKAction sequence:@[
[SKAction waitForDuration:3.0],
[SKAction runBlock:^{
// 5
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * myScene = [[MyScene alloc] initWithSize:self.size];
[self.view presentScene:myScene transition: reveal];
}]
]]
];
}
return self;
}
@end
|
There are four parts to this method:
won
parameter, sets the message to either “You Won” or “You Lose”.runBlock
action to run some arbitrary code.presentScene:transition:
method on the self.view
property.So far so good, now you just need to set up your main scene to load the game over scene when appropriate.
To do this, first add an import for the new scene at the top of MyScene.m:
#import "GameOverScene.h"
|
Then inside addMonster, replace the last line that runs the actions on the monster with the following:
SKAction * loseAction = [SKAction runBlock:^{
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
[self.view presentScene:gameOverScene transition: reveal];
}];
[monster runAction:[SKAction sequence:@[actionMove, loseAction, actionMoveDone]]];
|
This creates a new “lose action” that displays the game over scene when a monster goes off-screen. See if you understand each line here, if not refer to the explanation for the previous code block.
Also, another pop-quiz for you: why do you run the loseAction
before actionMoveDone
? Try reversing them to see what happens if you don’t know.
Solution Inside: Why is Lose Action First? | Show |
---|---|
Now you should handle the win case too – don’t be cruel to your players! :] Add a new property to the private interface:
@property (nonatomic) int monstersDestroyed;
|
And add this to the bottom of projectile:didCollideWithMonster::
self.monstersDestroyed++;
if (self.monstersDestroyed > 30) {
SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:YES];
[self.view presentScene:gameOverScene transition: reveal];
}
|
Go ahead and give it a build and run, and you should now have win and lose conditions and see a game over scene when appropriate!
And that’s a wrap! Here is the full source code for this Sprite Kit tutorial for beginners.
I hope you enjoyed learning about Sprite Kit and are inspired to make your own game!
If you want to learn more about Sprite Kit, you should check out our book iOS Games by Tutorials. We’ll teach you everything you need to know – from physics, to tile maps, to particle systems, and even making your own level editor.
If you have any questions or comments about this tutorial, please join the discussion below!
原文:http://www.cnblogs.com/A--G/p/4914407.html