本节书摘来自异步社区《iOS和tvOS 2D游戏开发教程》一书中的第1章,第1.2节显示精灵,作者 【美】raywenderlich.com教程开发组,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.2 显示精灵
在制作2D游戏的时候,通常要将表示游戏的各种要素的图像放置到屏幕上,如英雄、敌人、子弹等,如图1-18所示。这些图像中的每一个,都叫做精灵(sprite)。
图1-18
Sprite Kit有一个叫做SKSpriteNode的特殊的类,它使得创建和使用精灵更为容易。我们就是使用这个类来为游戏添加所有的精灵的。让我们来尝试一下。
1.2.1 创建精灵
打开GameScene.swift,给didMoveToView()添加如下的一行,就放在设置了背景颜色之后:
let background = SKSpriteNode(imageNamed: "background1")
不需要传入该图像的扩展名,Sprite Kit将自动为你确定它。
编译并运行,现在先忽略警告。好了,你认为这很简单吧,但是现在,你还是会看到一个空白的界面,怎么会这样呢?
1.2.2 把精灵加到场景
这确实很简单。因为在你把精灵作为场景的一个子节点或者场景的一个后代节点添加之前,精灵是不会显示在屏幕上的。
要做到这一点,在上面的那一行代码之后,添加如下这行代码:
addChild(background)
我们稍后将学习节点和场景。现在,再次编译并运行,你将会看到背景的一部分出现在屏幕的左下方,如图1-19所示。
图1-19
显然,这还不是我们想要的样子。要让背景处于正确的位置,必须要设置其位置。
1.2.3 定位精灵
默认情况下,Sprite Kit将精灵放置在(0, 0),这在Sprite Kit中表示屏幕左下方的位置。注意,iOS中的坐标系统和UIKit的坐标系统不同,在iOS中,(0, 0)表示左上方。
尝试设置position属性,从而将背景放置到其他的某个位置。添加如下的一行代码,放在调用addChild(background)之前:
background.position = CGPoint(x: size.width/2, y: size.height/2)
这里,我们将背景设置到了屏幕的中央。即便是这样一行简单的代码,也有重要的4点需要了解:
1.position属性的类型是CGPoint,这是一个简单的结构体,包含了x和y部分。
2.可以很容易地使用如下所示的初始化程序来创建一个CGPoint。
struct CGPoint {
var x: CGFloat
var y: CGFloat
}
3.既然在一个SKScene子类中编写这段代码,任何时候,都可以使用size属性来访问场景的大小。size属性的类型是CGSize,这是和CGPoint一样的一个简单结构体,包含了width和height部分。
struct CGSize {
var width: CGFloat
var height: CGFloat
}
4.一个精灵的位置在其父节点的坐标空间之中,在这个例子中,其父节点就是场景本身。我们将在第5章中更详细地介绍这一点。
编译并运行,现在,背景完全可见了,如图1-20所示。
注意
你可能注意到了,在iPhone设备上,是无法看到整个背景的,其顶部和底部的一部分重叠了。这是因为这款游戏设计为在iPad和iPhone上都可以工作,正如本章前面的1.1.1小节“通用App支持”所介绍的那样。
图1-20
1.2.4 设置精灵的锚点
设置背景精灵的位置,意味着把精灵的中心点设置为该位置。这就说明了为什么在此之前我们只能够看到背景的上半部分。在我们设置精灵的位置之前,其默认位置在(0, 0),这会将精灵的中心放置在屏幕的左下角,因此,我们只能够看到精灵的上半部分。
可以通过设置精灵的锚点来改变这一行为。把锚点当做是“精灵中的一个点,通过这个点将精灵固定在一个特定的位置”。图1-21展示了放置在屏幕中心的精灵,但是它们使用了不同的锚点。
图1-21
要看看这是如何工作的,找到将背景位置设置为屏幕中心的那一行代码,并且用如下的代码替换它。
background.anchorPoint = CGPoint.zero
background.position = CGPoint.zero
CGPoint.zero是的(0, 0)一种方便的简写。这里,我们将精灵的锚点设置为(0, 0),以便将其精灵的左下角固定到所设置的位置,也就是(0, 0)。
编译并运行,现在图像仍然在正确的位置,如图1-22所示。
图1-22
这之所以有效,是因为我们将背景图像的左下角固定到了场景的左下角。
这里,为了学习的目的,我们修改了背景的锚点。然而,通常可以将锚点保留为其默认值(0.5, 0.5),除非你有特定的需求,要让精灵围绕一个特定的点旋转,我们将在下一小节给出这样的一个例子。
因此,简而言之,当设置精灵的位置的时候,默认情况下,你将设置精灵的中心点的位置。
1.2.5 旋转精灵
要旋转一个精灵,直接设置其zRotation属性。在调用addChild()之前,添加如下的这行代码,从而尝试一下旋转背景精灵。
background.zRotation = CGFloat(M_PI) / 8
旋转值以弧度为单位,这是用来度量角的一个单位。这个示例将精灵旋转π/ 8个弧度,这等于22.5°。还要注意将M_PI(这是一个Double类型)转换为一个CGFloat。之所以要这么做,是因为zRotation需要一个CGFloat,而Swift不会像其他的语言那样,自动地在这些类型之间转换。
注意
我发现用角度来考虑旋转比用弧度要容易,不知道你怎么看。在本书稍后,我们将创建一个辅助程序,来实现角度和弧度之间的转换。
编译并运行,查看一下旋转后的背景精灵,如图1-23所示。
图1-23
这展示了重要的一点,精灵是围绕其锚点旋转的。由于我们将背景精灵的锚点设置为(0, 0),背景将围绕着其左下角旋转。
注意
记住,在iPhone上,图像的左下角实际上在屏幕之外。如果不确定为什么这样,请参考本章前面的1.1.1小节“通用App支持”。
尝试一下围绕着精灵的中心点来旋转精灵。将设置精灵位置和锚点的代码行,替换为如下的代 码行:
background.position = CGPoint(x: size.width/2, y: size.height/2)
background.anchorPoint = CGPoint(x: 0.5, y: 0.5) // default
编译并运行,这一次,背景将会围绕其中心点旋转,如图1-24所示。
图1-24
了解了这些知识点就很好了。但是对于Zombie Conga,我们并不想要一个旋转后的背景,所以,注释掉如下这行代码:
// background.zRotation = CGFloat(M_PI) / 8
如果对于在游戏中什么时候需要修改锚点还心存疑惑,假想一下我们要创建这样一个角色,其身体是由不同的精灵组成的,而每个精灵分别表示脑袋、身体、左胳膊、右胳膊、左腿到右腿,等等,如图1-25所示。
图1-25
如果需要绕着这个角色的关节来旋转它的身体的各个部分,那么对于每一个精灵,都必须要修改其锚点。
再一次强调,通常应该保持锚点为默认位置,除非你有特殊的需求,就像图1-25中所给出的例子那样。
1.2.6 获取精灵的大小
有时候,当操作精灵的时候,我们想要知道它有多大。精灵的大小默认为图像的大小。在Sprite Kit中,表示图像的类叫做材质。
在addChild()调用的后面,添加如下的代码行,以获取背景的大小并将其打印到控制台:
let mySize = background.size
print("Size: \(mySize)")
编译并运行,在控制台的输出中,应该会看到如下所示的内容:
Size: (2048.0, 1536.0)
有时候,通过编程(就像上面那样)而不是使用直接编码的数字来获取精灵的大小,这是很有用的。你的代码将会健壮很多,并且更易于修改。
1.2.7 精灵和节点
在前面,我们学习了如何让精灵出现在屏幕上,这需要将其作为场景的一个子节点或者一个后代节点添加。本小节将更深入地介绍节点的概念。
在Sprite Kit中,屏幕上所显示的一切内容,都派生自一个叫做SKNode的类。场景类(SKScene)和精灵类(SKSpriteNode)都派生自SKNode,如图1-26所示。
SKSpriteNode的很多功能都是继承自SKNode的。例如,position和zRotation属性都是派生自SKNode的,而并非SKSpriteNode自己所特有的。这意味着,你可以对场景本身或者派生自SKNode的任何对象做相同的事情,就像能够设置一个精灵的位置或旋转精灵一样。
可以将出现在屏幕上的一切内容综合起来,看做是节点组成的一幅图,这通常称之为场景图。例如,假设Zombie Conga游戏中只有一个僵尸、两只小猫和一个猫女士,其场景图如图1-27所示。
图1-27
我们将在第5章中更详细地了解节点以及能对节点做的事情。现在,我们将把精灵作为场景的直接子节点添加。
1.2.8 节点和z位置
每个节点都有一个名为zPosition的属性,它的默认值为0。每个节点都会按照其子节点的z位置,从低到高依次绘制其子节点。
在本章前面,我们给GameViewController.swift添加了如下这一行:
skView.ignoresSiblingOrder = true
如果ignoresSiblingOrder为true,对于拥有相同的zPosition属性的每一个子节点,Sprite Kit将不保证按照何种顺序来绘制它们。
如果ignoresSiblingOrder为false,对于拥有相同的zPosition属性的每一个子节点,Sprite Kit将按照它们添加到父节点的顺序来绘制它们。
通常,将这个属性设置为true是较好的做法,因为这允许Sprite Kit在幕后进行性能优化,以使得你的游戏运行得更快。
然而,如果不小心的话,将这个属性设置为true也可能会引发问题。例如,如果把和背景拥有相同的zPosition的一个僵尸添加到了场景中(如果还是把僵尸和背景的zPositon都保留为默认的0的话,就可能会发生这种情况),Sprite Kit可能会把背景绘制于僵尸之上,这会盖住了僵尸而让玩家看不到它。并且如果僵尸很吓人的话,想象一下看不到它的情况。
为了避免这种情况,我们将背景的zPosition设置为-1。通过这种方式,Sprite Kit将会先绘制它,然后再绘制添加到场景中的任何其他内容(这些内容的默认的zPosition为0)。
background.zPosition = -1
1.2.9 最后修整
本章的内容到此为止了。正如你所看到的,只需要三四行代码就可以把精灵添加到场景中。步骤如下:
1.创建精灵。
2.放置精灵。
3.可选地设置zPosition。
4.把精灵添加到场景图中。
现在,我们来把僵尸添加到场景中,以测试一下新学的知识。