深入Sprite Kit

学习Sprite Kit最好的方法是在实践中观察它。此示例创建一对场景和各自的动画内容。通过这个例子,你将学习使用Sprite Kit内容的一些基础技术,包括:

·      场景在一个基于Sprite Kit的游戏中的角色。

·      如何组织节点树来绘制内容。

·      使用动作让场景内容动起来。

·      如何添加交互到场景。

·      场景之间的过渡。

·      在一个场景里模拟物理。

一旦你完成这个项目,你可以用它来试验其他Sprite Kit概念。你可以在这个例子的结尾找到一些建议。

你应该已经熟悉创建iOS应用程序之前通过这个项目工作。欲了解更多信息,请参阅今天开始开发iOS应用程序的。大多数Sprite Kit在这个例子中的代码是相同的OS X。

让我们开始吧


本次练习需要Xcode 5.0。使用的单一视图的应用程序模板创建一个新的iOS应用程序的Xcode项目。

在创建项目时,请使用以下值:

·      产品名称:SpriteWalkthrough

·      ClassPrefix:Sprite

·      设备:iPad

添加Sprite Kit框架到项目中。

创建你的第一个场景


Sprite Kit内容被放置在一个窗口中,就像其他可视化内容那样。Sprite Kit内容由SKView类渲染呈现。SKView对象渲染的内容称为一个场景,它是一个SKScene对象。场景参与响应链,还有其他使它们适合于游戏的功能。

因为Sprite Kit内容由视图对象渲染,你可以在视图层次组合这个视图与其他视图。例如,你可以使用标准的按钮控件,并把它们放在你的Sprite Kit视图上面。或者,你可以添加交互到精灵来实现自己的按钮,选择权在你。在这个例子中,稍候你会看到如何实现场景交互。

配置视图控制器来使用Sprite Kit


1.    打开项目的storyboard。它有一个单一的视图控制器(SpriteViewController)。选择视图控制器的view对象并把它的类改成SKView

2.    在视图控制器的实现文件添加一个导入行。

#import <SpriteKit/SpriteKit.h>

3.    实现视图控制器的viewDidLoad方法来配置视图。

- (void)viewDidLoad
{
[super viewDidLoad];
SKView * spriteView =(SKView *)self.view;
spriteView.showsDrawCount = YES;
spriteView.showsNodeCount = YES;
spriteView.showsFPS = YES;
}

4.    代码开启了描述场景如何渲染视图的诊断信息。最重要的一块信息是帧率( spriteView.showsFPS),你希望你的游戏尽可能在一个恒定的帧率下运行。其他行展示了在视图中显示了多少个节点,以及使用多少绘画传递来渲染内容(越少越好)的详情。

接下来,添加第一个场景。

创建Hello场景


1.    创建一个名为HelloScene新类并让它作为SKScene类的子类。

2.    在你的视图控制器导入场景的头文件。

#import “HelloScene.h”

3.    修改视图控制器来创建场景,并在视图中呈现场景。

 - (void)viewWillAppear:(BOOL)animated
{
HelloScene *hello = [[HelloScene alloc] initWithSize:CGSizeMake(768,1024)];
SKView *spriteView =(SKView *)self.view;
[spriteView presentScene:hello];
}

现在,构建并运行项目。该应用程序应该启动并显示一个只有诊断信息的空白屏幕。

将内容添加到场景


当设计一个基于Sprite Kit的游戏,你要为你的游戏界面各主要大块(chuck)设计不同的场景类。例如,你可以为主菜单创建一个场景而为游戏设置创建另一个单独的场景。在这里,你会遵循类 似的设计。这第一个场景显示了传统的“Hello World”文本。

大多数情况下,你可以配置一个场景在它被视图首次呈现时的内容。这跟视图控制器只在视图属性被引用时加载他们的视图的方式是类似的。在这个例子中,代码在didMoveToView:方法内部,每当场景在视图中显示时该方法会被调用。

在场景中显示Hello文本


1.    添加一个新的属性到场景的实现文件中来跟踪场景是否已创建其内容。

@interface HelloScene()
@property BOOL contentCreated;
@end

该属性跟踪并不需要向客户端公开的状态,所以,在实现文件中它一个私有接口声明里实现。

2.    实现场景的didMoveToView:方法。

- (self)didMoveToView:(SKView *)view
{
if(!self.contentCreated)
{
[self createSceneContents];
self.contentCreated = YES;
}
}

每当视图呈现场景时, didMoveToView:方法都会被调用。但是,在这种情况下,场景的内容应只在场景第一次呈现时进行配置。因此,这段代码使用先前定义的属性( contentCreated)来跟踪场景的内容是否已经被初始化。

3.    实现场景的createSceneContents方法。

 - (void)createSceneContents
{
self.backgroundColor = [SKColor blueColor];
self.scaleMode = SKSceneScaleModeAspectFit;
[self AddChild:[self newHelloNode];
}

场景在绘制它的子元素之前用背景色绘制视图的区域。注意使用SKColor类创建color对象。事实上,SKColor不是一个类,它是一个宏,在iOS上映射为UIColor而在OS X上它映射为NSColor它的存在是为了使创建跨平台的代码更容易。

场景的缩放(scale)模式决定如何进行缩放以适应视图。在这个例子中,代码缩放视图,以便你可以看到场景的所有内容,如果需要使用宽屏(letterboxing)。

4.    实现场景的newHelloNode方法。

- (SKLabelNode *)newHelloNode
{
SKLabelNode * helloNode = [SKLabelNode labelNodeWithFontNamed:@“Chalkduster”];
@helloNode.text =“Hello, World!”
helloNode.fontSize = 42;
helloNode.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
return helloNode;
}

你永远不用编写显式执行绘图命令的代码,而如果你使用OpenGL ES或Quartz 2D你就需要。在Sprite Kit中,你通过创建节点对象并把它们添加到场景中来添加内容。所有绘制必须由Sprite Kit中提供的类来执行。你可以自定义这些类的行为来产生许多不同的图形效果。然而,通过控制所有的绘图,Sprite Kit可以对如何进行绘图应用许多优化。

现在构建并运行该项目。你现在应该看到一个蓝色屏幕上面有“Hello, World!”。现在,你已经学会了绘制Sprite Kit内容的所有基础知识。

使用动作让场景动起来


静态文本很友好,但如果文字可以动起来,它会更有趣。大多数的时候,你通过执行动作(action)移动场景周围的东西。Sprite Kit中的大多数动作对一个节点应用变化。创建action对象来描述你想要的改变,然后告诉一个节点来运行它。然后,当渲染场景时,动作被执行,在几个帧上发生变化直到它完成。

当用户触摸场景内容,文字动起来然后淡出。

让文本动起来


1.    添加以下代码到newHelloNode方法:

helloName.name = @“helloNode”;

所有节点都有一个名称属性,你可以设置它来描述节点。当你想能够在稍后找到它,或当你想构建基于节点名称的行为时,你应该命名一个节点。稍后,你可以搜索树中与名称相匹配的节点。

在这个例子中,你给标签的一个名称以便稍后可以找到它。在实际的游戏中,你可能会得给呈现相同类型的内容的任何节点以相同的名称。例如,如果你的游戏把每个怪物呈现为一个节点,你可能会命名节点为monster

2.    重载场景类的touchesBegan:withEvent方法。当场景接收到触摸事件,它查找名为helloNode节点,并告诉它要运行一个简短的动画。

所有节点对象都是iOS上UIResponder 或OS X上NSResponder 的子类。这意味着你可以创建Sprite Kit节点类的子类来添加交互到场景中的任何一个节点。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
SKNode *helloNode = [self childNodeWithName:@“helloNode”];
If(helloNode != nil)
{
helloNode.name = nil;
SKAction *moveUp = [SKAction moveByX:0 y:100.0 duration:0.5];
SKAction *zoom = [SKAction scaleTo:2.0 duration:0.25];
SKAction *pause = [SKAction waitForDuration:0.5];
SKAction *fadeAway = SKAction fadeWithDuration:0.25];
SKAction *remove = [SKAction removeFromParent];
SKAction * moveSequence = [SKAction sequence:@[moveUp, zoom, pause, fadeAway, remove];
[helloNode runAction:moveSequence];
}
}

为了防止节点响应重复按压,代码会清除节点的名称。然后,它构建动作对象来执行各种操作。最后,它组合这些动作创建一个
动作序列;序列运行时,按顺序执行每个动作。最后,它告诉标签节点执行序列动作。

运行的应用程序。你应该看到像之前那样的文字。在屏幕的底部,节点计数应该是。现在,点击视图内部。你应该看到文字动画并淡出。在它淡出后,节点计数应该变为,因为节点已从父节点中删除。

场景之间的转换


Sprite Kit让场景之间的过渡变得很容易。场景之间的过渡时,你可以坚持保留它们,或清除它们。在这个例子中,你将创建第二个场景类,来学习一些其他的游戏行为。“Hello, World!”文字从屏幕上消失时,代码创建一个新的场景并过渡到它。Hello场景过渡在后会被丢弃。

创建飞船场景


1.    创建一个名为SpaceshipScene新类并让它成为SKScene类的子类。

2.    实现代码来初始化飞船场景的内容。此代码类似于你为HelloScene类实现的代码。

@interface SpaceshipScene()
@property BOOL contentCreated;
@end @implementation SpaceshipScene
- (void)didMoveToView:(SKView *)view
{
If(!self.contentCreated)
{
[self createSceneContents];
self.contentCreated = YES;
}
} - (void)createSceneContents
{
self.backgroundColor = [SKColor blackColor];
self.scaleMode = SKSceneScaleModeAspectFit;
}

3.    在 HelloScene.m文件中导入 SpaceshipScene.h头。

#import "SpaceshipScene.h"

4.    在
touchesBegan:withEvent方法中,更改
runAction:
调用为新的调用
runAction:completion:。实现完成处理来创建并呈现一个新的场景。

[helloNode runAction:moveSequence completion:^ {
SKScene * spaceshipScene = [[SpaceshipScene alloc] initWithSize:self.size];
SKTransition *doors= [SKTransition doorsOpenVerticalWithDuration:0.5];
[self.view presentScene:spaceshipScene transition:doors];
}];

构建并运行该项目。当你触摸场景内部时,文字淡出,然后在视图过渡到新的场景。你应该看到一个黑色的屏幕。

使用节点构建复杂的内容


新的场景还没有任何内容,所以你准备要添加一个飞船到场景。要构建这个太空飞船,你需要使用多个SKSpriteNode对象来创造了飞船和它表面的灯光。每个精灵节点都将执行动作。

精灵节点是在一个Sprite Kit应用程序中最常见用于创建内容的类。他们可以绘制无纹理或纹理的矩形。在这个例子中,你要使用无纹理对象。稍后,这些占位符(placeholder)可以很容易地用纹理精灵进行替换,而不改变它们的行为。在实际的游戏中,你可能需要几十个或上百个节点来创建你的游戏的可视化内容。但是,从本质上说,那些精灵将使用与这个简单的例子相同 的技术。

虽然你可以直接添加所有三个精灵到场景,但这并不是Sprite Kit的方式。闪烁的灯光是飞船的一部分!如果飞船移动,灯光应该和它一起移动。解决的办法是使飞船节点成为它们的父节点,同样地场景将是飞船的父节点。光的坐标将要相对于父节点的位置来指定,而父节点是在子精灵图像的中心。

添加飞船


1.    在SpaceshipScene.m,添加代码到createSceneContents方法来创建飞船。

SKSpriteNode *spaceship = [self newSpaceship];
spaceship.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame)-150);
[self addChild:spaceship];

2.    实现
newSpaceship
方法。

 - (SKSpriteNode *)newSpaceship
{
SKSpriteNode *hull= [[SKSpriteNode alloc] initWithColor:[SKColor grayColor] size:CGSizeMake(64,32); SKAction *hover= [SKAction sequence:@[
[SKAction waitForDuration:1.0]
[SKAction moveByX:100 y:50.0 duration:1.0]
[SKAction waitForDuration:1.0]
[SKAction moveByX:-100.0 y:-50 duration:1.0]];
[hull runAction:[SKAction repeatActionForever:hover]; return hull;}

此方法创建飞船的船体,并添加了一个简短的动画。需要注意的是引入了一种新的动作。一个重复的动作不断地重复的传递给它的动作。在这种情况下,序列一直重复。

现在构建并运行应用程序来看当前的行为,你应该看到一个矩形。

在建立复杂的有孩子的节点时,把用来在构造方法后面或者甚至是在子类中创建节点的代码分离出来,是一个很好的主意。这使得它更容易改变精灵的组成和行为,而无需改变使用精灵的客户端(client)。

3.    添加代码到newSpaceship方法来添加灯光。

SKSpriteNode *light1= [self newLight];
light1.position = CGPointMake(-28.0,6.0);
[hull addChild:light1]; SKSpriteNode *light2= [self newLight];
Light2.position = CGPointMake(28.0,6.0);
[hull addChild:light2];

4.    实现newLight方法。

- (SKSpriteNode *)newLight
{
SKSpriteNode *light = [[SKSpriteNode alloc] initWithColor:[SKColor yellowColor] size:CGSizeMake(8,8)]; SKAction *blink= [SKAction sequence:@ [
[SKAction fadeOutWithDuration:0.25]
[SKAction fadeInWithDuration:0.25]];
SKAction * blinkForever = [SKAction repeatActionForever:blink];
[light runAction:blinkForever]; return light;
}

当你运行应用程序时,你应该看到一对灯在飞船上。当飞船移动,灯光和它一起移动。这三个节点全都是连续动画。你可以添加额外的动作,让灯光在船的周围移动,它们总是相对船体移动。

创建能交互的节点


在实际的游戏中,你通常需要节点之间能交互。把行为添加给精灵的方法有很多,所以这个例子仅展示其中之一。你将添加新节点到场景,使用物理子系统模拟它们的运动并实现碰撞效果。

Sprite Kit提供了一个完整的物理模拟,你可以使用它添加自动行为到节点。也就是说,物理在使其移动的节点上自动模拟,而不是在节点上执行动作。当它与物理系统一部分的其他节点交互时,碰撞自动计算并执行。

添加物理模拟到飞船场景


1.    更改newSpaceship方法来添加一个物理体到飞船。

hull.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hull.size];

构建并运行应用程序。等一下!飞船垂直坠落到屏幕下方。这是因为重力施加到飞船的物理体。即使移动动作仍在运行,物理效果也被应用到飞船上。

2.    更改的newSpaceship方法来防止飞船受物理交互影响。

hull.physicsBody.dynamic = NO;

当你现在运行它时,应用程序像之前那样运行。飞船不再受重力影响。稍后,这也意味着飞船的速度将不会受到碰撞的影响,。

3.    添加代码到createSceneContents方法来生成大量岩石。

SKAction * makeRocks = [SKAction sequence:@ [
[SKAction performSelector:@selector(addRock) onTarget:self]
[SKAction waitForDuration:0.10 withRange:0.15]
]];
[self runAction:[SKAction repeatActionForever:makeRocks];

场景也是一个节点,因此它也可以运行动作。在这种情况下,自定义操作调用场景上的方法来创建岩石。序列创建一个岩石,然后等待一段随机时间。重复这个动作,场景不断产生大量新的岩石。

4.    实现addRock方法。

static inline:CGFloat skRandf() {
return rand()/(CGFloat)RAND_MAX;
} static inline CGFloat skRand(CGFloat low, CGFloat high) {
return skRandf()*(high - low) + low;
} - (void)addRock
{
SKSpriteNode *rock = [[SKSpriteNode alloc] initWithColor:[SKColor brownColor] size:CGSizeMake(8,8)];
rock.position = CGPointMake(skRand(0, self.size.width),self.size.height-50);
rock.name = @“rock”;
rock.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rock.size];
rock.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:rock];
}

构建并运行该项目。岩石现在应该从场景上方落下来。当一块石头击中了船,岩石从船上反弹。没有添加动作来移动岩石。岩石下落并与船碰撞完全是由于物理子系统的作用。

岩石都很小且移动速度非常快,所以代码指定精确的碰撞,以确保所有的碰撞都检测到。

如果你让应用程序运行了一段时间,帧率会开始下降,即使节点计数仍然很低。这是因为节点的代码仅显示出场景中可见的节点。然而,当岩石落下到场景的底部时,它们继续存在于场景中,这意味着物理还在对它们模拟。最终,有如此多的节点正在处理以致Sprite Kit减慢了。

5.    实现场景中的didSimulatePhysics方法来当岩石移动到屏幕之外时移除它们。

- (void)didSimulatePhysics
{
[self enumerateChildNodesWithName:@“rock” usingBlock:^(SKNode *node, BOOL *stop){
if (node.position.y <0)
[node removeFromParent];
}];
}

每次场景处理一帧,都运行动作和模拟物理。你的游戏可以挂接到这个过程中来执行其他自定义代码。在每一帧,场景将处理物理,然后移除移出屏幕底部的所有岩石。当你运行应用程序时,帧率保持不变。

在场景中,预处理及后处理与动作和物理结合的地方,就是你建立你的游戏的行为的地主。

这就是你第一次体验Sprite Kit!其它一切都是你在这里看到的基本技术的细化。

试试这个!


这里有一些东西,你可以尝试:

·      做一个OS X版本的这个例子。你在视图控制器写的代码,在OS X上通常是在一个应用程序委托中实现。响应代码需要改变来使用鼠标事件而不是触摸事件。但是,代码的其余部分应是相同的。

·      使用纹理精灵呈现船和岩石。(提示:“使用精灵”

·      尝试在触摸事件的响应中移动飞船。(提示:“添加动作节点”“构建场景”)。

·      添加额外的图形效果到场景(提示:“使用其他节点类型”

·      岩石与船舶碰撞时添加其他行为。例如,使岩石发生爆炸。(提示:“模拟物理”

Sprite Kit编程指南(1)-深入Sprite Kit的更多相关文章

  1. Sprite Kit编程指南中文版下载

    下载地址:http://download.csdn.net/detail/xin814/6032573 关于Sprite Kit 重要提示:  这是API或开发技术的一个初版文档.虽然本文档的技术准确 ...

  2. iOS多线程编程指南

    iOS多线程编程指南(拓展篇)(1) 一.Cocoa 在Cocoa上面使用多线程的指南包括以下这些: (1)不可改变的对象一般是线程安全的.一旦你创建了它们,你可以把这些对象在线程间安全的传递.另一方 ...

  3. OpenGL编程指南(第七版)

    OpenGL编程指南(第七版) 转自:http://blog.csdn.net/w540982016044/article/details/21287645 在接触OpenGL中,配置显得相当麻烦,特 ...

  4. 编译opengl编程指南第八版示例代码通过

    最近在编译opengl编程指南第八版的示例代码,如下 #include <iostream> #include "vgl.h" #include "LoadS ...

  5. 高质量C++/C编程指南(林锐)

    推荐-高质量C++/C编程指南(林锐) 版本/状态 作者 参与者 起止日期 备注 V 0.9 草稿文件 林锐   2001-7-1至 2001-7-18 林锐起草 V 1.0 正式文件 林锐   20 ...

  6. iOS ---Extension编程指南

    当iOS 8.0和OS X v10.10发布后,一个全新的概念出现在我们眼前,那就是应用扩展.顾名思义,应用扩展允许开发者扩展应用的自定义功能和内容,能够让用户在使用其他app时使用该项功能.你可以开 ...

  7. Lambda 表达式(C# 编程指南) 微软microsoft官方说明

    Visual Studio 2013 其他版本 Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地 ...

  8. KVC/KVO原理详解及编程指南

    一.简介 1.KVC简介 2.KVO简介 二.KVC相关技术 1.Key和Key Path 2.点语法和KVC 3.一对多关系(To-Many)中的集合访问器方法 4.键值验证(Key-Value V ...

  9. iOS多线程编程指南(二)线程管理

    当应用程序生成一个新的线程的时候,该线程变成应用程序进程空间内的一个实体.每个线程都拥有它自己的执行堆栈,由内核调度独立的运行时间片.一个线程可以和其他线程或其他进程通信,执行I/O操作,甚至执行任何 ...

随机推荐

  1. android避免service被杀

    1.在service中重写下面的方法,这个方法有三个返回值, START_STICKY是service被kill掉后自动重写创建@Override    public int onStartComma ...

  2. 使用Xcode和Instruments调试解决iOS内存泄露【转】

    转载自:http://blog.csdn.net/totogo2010/article/details/8233565 虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露 ...

  3. 远程连接到Fedora

    首先执行以下3点(主要是前两点) 第一: 开启ssh #service sshd restart 第二:关闭防火墙 #service iptables stop 第三:selinux(重启电脑后失效) ...

  4. html与css的移动端与pc端需要注意的事项

    一个移动端与pc端之间最主要的也就是尺寸问题,苹果与安卓的机型尺寸大小相差甚多,一个尺寸都会影响用户的体验.那么我们来了解一下一些常用的解决方法. 一般在网页中都会在头部有一些这样的代码 <me ...

  5. scala中的view bound与context bound

    1.scala中的<%意识是“view bounds”(视界) ,它比<:的使用范围更广,还能进行隐式转换,是一种语法糖. 下面的两种写法是等效的,在编译之后完全一样. object Te ...

  6. C语言结构体的字节对齐

    Test Code: #include <iostream> #include <cstring> using namespace std; struct A{ int a; ...

  7. C#中KeyDown和KeyPress区别

    1.比如说TexBox 输入'a' 按下->触发KeyDown事件,然后去处理 ->将a显示输入到文本框后 ->触发KeyPress事件

  8. 关于JDBC中Class.forName的疑惑

    一直以来都不知道为什么执行了 Class.forName(); 之后,通过DriverManager.getConnection(); 就可以获取相关数据库的连接Connection的实现呢?今天看了 ...

  9. windows安装Apache HTTP服务器报错:无法启动,因为应用程序的并行配置不正确

    Apache HTTP服务器安装后报:无法启动,因为应用程序的并行配置不正确-(已解决)   0条评论 [摘要:本创做品,出自 “深蓝的blog” 专客,迎接转载,转载时请务必说明出处,不然有权穷究版 ...

  10. 复习了下自定义style的使用

    一.为什么要自定义style 这是样式与控件本身脱离的一种方式.style就像html中的css,只负责自定义样式.View控件在layout中就只负责声明自己就可以了. 就像这样: 首先在style ...