注:本文译自Sprite Kit Tutorial: Making a Universal App: Part 1
本文将介绍如何制作一个通用程序(打鼹鼠的游戏)——可以在iPhone和iPad上运行(包括retina显示的支持。)
学习本文之前,需要掌握以下知识:
Sprite Kit教程:初学者 1
Sprite Kit教程:初学者 2
Sprite Kit教程:初学者 3
英文原文在这里:Sprite Kit Tutorial for Beginners
Sprite Kit教程:动画和纹理图集 1
Sprite Kit教程:动画和纹理图集 2
英文原文在这里:Sprite Kit Tutorial: Animations and Texture Atlases
Sprite Kit教程:如何拖放Sprites
英文原文在这里:Sprite Kit Tutorial: How To Drag and Drop Sprites
如果还没有看上面的这些文章(或者相关的知识),建议你先去看一下。
本文会有两篇文章。第一篇,会先创建一个基本的游戏——可爱的小鼹鼠聪洞里面弹出来。为了让游戏在iPhone和iPad(支持retina显示)上看起来很优美,本文还花了大量的时间来考虑如何做游戏的美术规划和坐标。
我们希望程序可以在iPhone 3.5英寸,4英寸(iPhone 5)和iPad上良好的运行,所以在开始之前,我们需要认真的做好UI规划。
为了搞明白需要什么样的UI尺寸,我们先来看看下面的相关内容:
下面开始吧!
Retina显示
在iPhone中,non-retina和retina在显示上的最大区别就是retina的分辨率是non-retina的2倍。所以在non-retina上面分辨率为 480 * 320(landscape),而retina则是960 * 640.
同样iPad也分为non-retina和retina,它们的分辨率相差也为2倍,non-retina显示的分辨率是1024 * 768像素,而retina上面则是2048 * 1536像素!
稍等,你可能在想:双倍分辨率岂不是打乱了所有已经写好的程序,例如iPhone上的480 * 320和iPad的1024 * 768?这是有可能的,除非是在Sprite Kit中设置尺寸或者坐标,此时实际上是在UIKit中进行设置,并且设置的尺寸单位叫做points
,而不是像素(pixels)。
在non-retina显示上,无论是iPhone火iPad,一个point代表一个pixel,而在retina上面,一个point代表2个pixels。所以将位置设置为(10,10)point时,non-retina上将是(10,10),而retina上则是(20,20),所以它们依然会显示在相同的偏移量上。不错吧!
当使用苹果提供的控件或者Core Graphics时,苹果已经写好了相关代码,让它们在retina显示起来很好看。
唯一需要注意的就是关于使用的图片。比如在iPhone或iPad程序中又一个200 * 200d 图片。如果什么事情都不做的话,在retina上面会自动的将这个图片放大两倍——这看起来不是太好,因为我们并没有提供相关分辨率的图片。
因此针对retina显示我们需要提供所有图片的另外一个版本,也就是说需要一个普通的版本,以及另外2倍分辨率的一个版本。如果将2倍分辨率图片命名为”@2x”后缀,那么当利用[SKSpriteNode spriteNodeWithImageNamed:…]或者类似的APIs加载sprite时,它会自动的将@2x图片加载到retina显示上。
所以在开发针对retina显示的Sprite Kit游戏时也很简单——只需要添加@2x的图片,基本上就搞定了。
4英寸iPhone显示
iPhone 5设备在屏幕上显示的分辨率比以前的更大了,对于游戏显示上来说,这非常的好。本文中的处理很简单,只需要将背景图片做一个扩展延伸即可。
iPhone 5的分辨率是1136 * 640——宽高比为16:9。用point来衡量的话则是568 * 320.
iPad和iPhone的宽高比
上面我们已经看到要处理retina显示很容易,但是要想创建一个通用的程序呢(可以运行在iPhone和iPad设备上)。
其实要想创建一个通用的程序还真有一个麻烦的事情——iPhone和iPad的宽高比不一样!
iPhone的比例是1.5(480 * 320 或960 * 640),而iPad是1.33(768 * 1024或1536 * 2048)。
由于比例不同,如果一副能够在non-retina iPad(768×1024)上完整显示,你希望将其在iPhone上重用,那么不会完整的匹配上,如果将其缩放,按照宽度进行适配(乘以0.9375),会得到720×960的尺寸,这样就会把高度剪切掉一部分。
发生这种情况会让人比较烦恼,我们不仅需要处理背景图片的问题,不同的宽高比导致不同设备间使用相同的坐标比较困难。
下面是我了解到的一些对应的处理方法:
可玩区域
。这样剩下的区域可以用一个背景图片来覆盖,不要让玩家关注这一剩下的区域。这样一来在不同设备间进行左边的转换和重用要非常方便。本文将利用这种方法。iOS 模拟器选项
下面这些模拟器可以运行iOS 7:
注意:这里并没有non-retina iPhone——因为没有任何一台no-retina iPhone或iPod touch可以运行iOS 7。
另外由于Sprite Kit是在iOS 7中才引入的,所以就不用考虑no-retina iPhone或iPod touch设备了。
基于上面的一些讨论,下面是本文的相关计划:
-568
的纹理图集,并将其可玩区域
居中。-ipad
的纹理图集,并将其坐标转换到可玩区域
中,另外在使用适当的字体大小等。来这里可以下载到本文的UI资源。解压出下载到的文件,可以看到如下一些内容:
上面搞了这么多,现在终于可以开始了!
打开Xcode,选择File > New > Project…,然后选中Sprite Kit Game并单击Next。将工程命名为WhackAMole,devices选中universal,接着再单击Next。选择一个路径来保存工程,然后单击Create。
当工程打开之后,应该能看到Project Navigator中的工程文件已经被选中了,如果没有选中,那么将其选中,然后在target中选中WhackAModle,以及选中顶部的General,在Deployment info里面可以看到一些设备朝向的勾选框。在这里我们的游戏是landscape的,所以勾选上iPhone和iPad的Landscape Left和Landscape Right。
另外,为了让朝向正确,还需要对代码做一些修改。打开ViewController.m文件并用下面的viewWillLayoutSubviews:方法替换viewDidLoad方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
为什么要这样做呢?默认情况下View Controller views是以竖直的方式加载,所以横屏模式下,当viewDidLoad被调用的时候不能保证尺寸是正确的,不过当viewWillLayoutSubviews被调用的时候view的size将是正确的。如上代码所示,大多数代码与viewdidLoad中的相同。需要关注的就是if语句中关于skView.scene的配置。当然在这里需要判断一下skView.scene是否已经存在(viewWillLayoutSubviews方法可能会被多次调用)。
纹理图集的配置非常简单。首选创建一个文件夹并且文件名已.atlas
结尾。接着将那些UI元素拷贝到这个文件夹里面。然后在Xcode工程中添加这个文件夹即可!
简单吧!当在编译程序的时候,Xcode会把.atlas
结尾的文件夹中的图片生成纹理图集。
注意:添加到.atlas
文件夹中的图片尺寸不能超过2048×2048 pixels,否则会出错——2048×2048 pixels是自动生成纹理图集的最大尺寸。
下面看看具体如何做。找到之前下载的压缩文件,在压缩文件中有一个名为TextureAtlases的文件夹。这个文件夹中包含了3中设备类型的UI元素(iPad, iPhone, 和 WidescreeniPhone)。这些文件家中都包含有.atlas
文件夹。我们将TextureAtlases文件夹拖至工程中,确保勾选上Copy items into destination group’s folder (if needed)
。
本文中为了让一切变得简单点,我们为每种类型的设备准备了一套纹理图集(iPhone 3.5-inch, iPhone 4-inch 和 iPads)。在iPhone 4英寸中可以重用iPhone3.5英寸中的一些纹理图集,
在开始修改scene中显示内容之前,我们需要添加一个宏以及一个helper方法。打开MyScene.m文件,并在文件的头部添加如下一行代码(在#import下面):
1
|
|
上面这个宏可以判断程序是否允许在4英寸的屏幕中,该宏将被用在helper方法中,如果要了解上面宏的详细内容,看这里。
接着添加一个helper方法——为运行程序的设备获取正确的SKTextureAtla。这个方法接收一个文件名,并在文件名尾部添加一个正确的标示符,然后返回正确的一个SKTextureAtla。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
上面的代码做了些什么?
-568
。-ipad
。接着找到initWithSize:方法。移除掉设置背景颜色和创建Hell World lable的6行代码,然后用下面的代码替换之:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
我们来看看上面的代码都做了什么。
Add background
这部分代码使用之前的helper方法创建一个背景纹理图集。接着从背景纹理图集中构建一个dirt sprite。最后将其缩小2倍,并将其添加到scene正中间。将其缩小的目的是为了节省空间。Add foreground
这部分代码跟上面background中的十分相似,只不过这两个foreground sprite在同一个纹理图集中罢了。这里用了一种方便的方法来放置图片:设置anchor point(顶部图片设置middle/bottom,底部图片设置middle/top)。这种方法不需要做复杂的数学运算,就能在所有的设备上做出正确的显示。另外需要注意的是iPhone上的背景图片有一部分将不会显示出来,不过在这里并没有太大的影响。另外需要留意的是设置了图片的zPosition值,这样可以确保图片的正确排序。SKSpriteNode的zPosition属性
这个数学用来决定每个sprite在scene所处层次的位置。可以将其看做一个蛋糕,其中dirt sprite处于最底层,所以使用最小的一个值。添加别的层时增加相应的值,所以上半部分前景图设置为1,而下半部分设置为3,那么2呢?这个值是留给鼹鼠的——因为鼹鼠将出现在上部前景图上面,而在下部前景图后面。在运行程序之前,再做一点清理工作。找到touchesBegan:方法,并将其删除掉。
编译并运行程序,现在可以看到屏幕上显示出了背景图和前景图!并且在iPhone和iPad模拟器中运行,也能正确的显示!如下图所示:
在这个游戏中,我们将添加3个鼹鼠到scene中——上图中的每个洞放一个。鼹鼠默认是在地下的,偶尔会弹出来,当弹出来时,我们可以打击它们。
首先我们先将鼹鼠放到每个洞中。为了确保鼹鼠位置的正确,最好先把鼹鼠显示在最上面,等调好位置之后,在将其放到后台去。
打开MyScene.h文件,并按照如下代码进行修改:
1 2 3 4 5 6 7 8 |
|
上面的代码添加了一个SKTexture和一个数组。创建鼹鼠的时候会用到SKTexture,创建好的每个鼹鼠会被添加到数组中,这样方便之后循环获得每个鼹鼠。
在添加鼹鼠之前,首先定位到MyScene.m的顶部,并将下面这行代码添加到@implementation MyScene
之前。
1
|
|
这是一个float类型的常量,用来对鼹鼠进行定位。
接着,将如下代码添加到initWithSize:方法最后面:
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 |
|
上面的代码首先创建并加载一个SKTextureAtlas。接着根据sprite纹理图集中的mole_1.png 创建一个SKTexture,这将用来创建3个鼹鼠。Texture的重用性可以让Sprite Kit处理和渲染sprite更加高效。
接下来的这个值用来设置center。如果设备是4英寸的iPhone,那么这个center值将反映出额外的尺寸。
接着为每个鼹鼠创建对应的sprite,并将它们放置到scene中,还把它们添加到鼹鼠数组中。注意,每个鼹鼠的位置是利用center位置和文件头部定义的常量决定的。针对iPhone 3.5英寸的设备,鼹鼠位置处在480×320的可玩区域,而如何是iPad,相关位置需要做转换,所以下面写了一个helper方法convertPoint。
将下面这个方法添加到initWithSize:方法后面:
1 2 3 4 5 6 7 8 |
|
上面这个方法将可玩区域的point转换到iPad上适当的位置。记住:
就这样,上面的方法就是简单的给出iPad中正确的位置。
编译并运行程序,可以看到scene中有3个鼹鼠,它们的位置已经设置正确!你最好在iPhone 3.5-inch, iPhone 4-inch, iPad, 和 iPad Retina设备上都运行一下,以确保位置的正确。
至此,我们已经把鼹鼠放置好了,下面我们添加一些代码让鼹鼠从洞里面跳出来吧。
首先,将这些sprite(鼹鼠)的zPosition从999设置为2,这样就可以把鼹鼠藏起来了。
然后,将下面的代码添加到update:方法中:
1 2 3 4 5 6 7 |
|
需要注意的是每帧的显示都会调用update方法。该方法被调用的时候我们都会尝试着弹出一些鼹鼠。在代码中循环遍历处理了每个鼹鼠,并给每个鼹鼠1/3的机会从洞中弹出来。不过记住我们只能弹出那么还没有弹出来的鼹鼠——很简单的一个判断方法就是检查一下sprite的属性hasActions返回的值,如果还有action在运行,那么hasActions将返回YES。
接着,实现一下popMole方法:
1 2 3 4 5 6 7 8 9 10 11 |
|
上面的代码使用了Sprite Kit中的一些action,让鼹鼠弹出洞来,并暂停半秒钟,然后在弹回去。我们来细看一下上面代码的意思:
搞定!编译并运行程序,可以看到鼹鼠会从它们的洞口弹出来!
本文的代码工程在这里。
下一篇文章Sprite Kit教程:制作一个通用程序 2
中会给鼹鼠添加一些可爱的动画(笑和被击中),并添加一个玩法——打击鼹鼠,并赚取点数,并添加一些音效。