Graphics and Games • iOS, OS X • 1:01:37
Dive into the practical workflow of developing a 2D adventure game using Sprite Kit. See how to get started, and learn about tools for optimizing image assets, creating particle systems, and building game levels. Understand how Sprite Kit integrates with your art pipeline and get expert guidance to share with your artists. Come away from the session with a fully functioning game in-hand and its code explained.
Speakers: Graeme Devine, Spencer Lindsay, Norman Wang
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Good afternoon. Welcome to Designing Games with Sprite Kit. My name is Norman Wang. I'm with the game technology team at Apple. So today, I'll be very-- I'm very excited to share with some of the very exciting features and tools for the runtime, allow you to maximize the performance, as well as minimize the iteration time to build your game.
So, for those of you who have missed the earlier session, introduction to Sprite Kit, here's a quick recap. So, Sprite Kit is a high performance 2D rendering engine that is available in OS X Mavericks and iOS 7. It has built-in physics support that features all the 2D physics need for your game.
It is the-- since the platform-- the framework is available on both iOS 7 and Mac OS X, they allow you to build once and deploy it to multiple devices that include the iOS running device and the Mac OS hardware. Not only Sprite Kit comes with a super fast runtime, but we also provide a lot of tools to allow you to integrate directly into your games or pipeline. So, we're covering some of that today. So, in terms of the feature for rendering the animation, we have a lot of features that's provided. That includes sprites, shapes, particles, we also support non-linear animation, we provide support for audio, video as well different visual effects.
So, I hope all of you have seen the adventure demo so far and it is a production quality sample code that was provided along with the release of Sprite Kit framework. They also come with code explained detailed documentation on how the game is created. So, I'll be referring to various bits and pieces from the Adventure demo in this talk to show you how quick and easy it is to integrate Sprite Kit directly into your game.
So, we'll start with looking at your Adventure art pipeline to see how the Adventure Game can do quick iteration on importing art files into the runtime. And then we will be looking at how to create various visual effects for using the Sprite Kit that include post-processing and particle system.
I'll have-- we have some very special guests today who will be joining us that will be talking about how their experience is when they build the Adventure Game using Sprite Kit. And I'll be wrapping up with how to develop some of your custom tool that include level editor, complex action editor, and some of the best practices for using Sprite Kit.
So, the Adventure Game is a very complex game. And what really goes in to the Adventure? So, Adventure manages a lot of data that include lots of artwork files and we have about 1,700 art files to load. We have a lot of sound files to load for different sound effects, we have particle effects and they represent the tree leaves falling, smoke and damages.
We have physics that allows you to interact with the wall and not ran to objects as well as AI characters. We have different-- also have Collision Mappings. For example, the entire Collision Map is data-driven and design offline and loaded them dynamically upon game startup and builds these dynamic walls inside your level.
So, let's go ahead and look at the Adventure Game startup sequence. How exactly Adventure is doing upon startup? So, when the game is load upon the game application launching handlers called, we start loading all the background assets in parallel because we need all these assets to construct our world before we start issuing the first draw call will perform all the asset loading in the background and get them loaded as fast they can.
Once we have these assets in memory, we will go ahead and create an instance of a SKScene. Once we have the scene, we'll start placing enemies, spawners, as well as the Boss into different places in the world. After that, we'll be parsing and reading an offline Collision Map that will indicate and build the physics world where exactly at the world-- lost in your world. Finally, the scene is constructed and we'll use presentScene method to present that to a SKscene which part of your [inaudible] file. And also, we also register for the game controller notifications for adding game controller support.
So, in the first session, we actually looked at some of the game update loop of what a typical game about loop using Sprite Kit. But now we're going to talk a little bit into the Adventure Game. So, the Adventure Game actually tab into this update loop at two places.
Number one, when the frame starts, update method get called. Here, what we do is we update all the input that includes controllers, touches, mouse and keyboard. So, once we have this input, we translate them into delta movement for the main character. So now, our hero knows and either walk from A to B.
So, once that's happening, we also update AI, AI will say, "OK, maybe we should provide-- perform some recasting to see whether the player or the hero is visible or not. If it is, let's translate that into a Delta movement." So, once you have this movement, we'll start issuing animation playback frame. So, I know if I move from A to B, I don't skate from A to B, I walk from A to B.
So, once that's done, we'll go to-- we wait until we finish simulating all the physics. So, if I'm walking from A to B, there happen to be a wall in between, physics will kick in and stop me right in front of the wall. So, this is a really safe place for you to assume all the objects final positions are finalized before the frame.
So, at this time, we know the character positions are finalized. We'll update the camera, so the camera will follow the player and also reveal different parts of the world. We also update the tree parallax because the camera has been moved, and the fallen leaves have to be following with the tree.
Due to that complexity of this game, it presents us with a lot of challenges. Number one, the construction of the scene cannot happen unless all the assets are loaded including 1,700 art files and we have huge amount of data dependencies in each category. If you just look at the background tiles, the level itself is a 4K x 4K tile map that gets divided into 128 by 128 grid, sorry, 32 by 32 grid and each tile is 128 by 128 size.
We also have a lot of animation frames. For example, if we look at animes, anime have idle animation, have a tack animation, also have walking animation, and death animation. So, we have different animation frame categories for every single character in the game. We also have a lot of particle effects. For example, falling trees, smoke, and also Collision Mapping need to be loaded. So, how do we address all of these by using the tools provided by Sprite Kit? We're going to go into looking at some of the art pipeline for the Adventure Game.
So, diving a little bit into the Adventure textures, again we have 1660 texture files and the game actually used Texture Atlas for all of them. Character animations are divided into several atlas. For example, walking animation, 42 frames, that goes into a single Texture Atlas. We have the environment, 1024 tiles, that's a 4K by 4K grid divided into a single Texture Atlas, we'll put that I mean to a Texture Atlas.
The environments like trees, caves and projectiles, all of them go to a single Texture Atlas as well. So, this is what it looked like, bunch of loose files as input that comes in various shapes and sizes, passing through the Sprite Kit tool and we generate a Texture Atlas, as simple as that. So, speaking about Texture Atlas, I want to talk a little bit more on this topic.
It is not a new thing, but over the years, for the game I'm looking at, that's draw call bound, not everybody is using Texture Atlas to batch their draw calls. It's because there's always this tank of work that need to be done. Traditionally, we have artists that use Photoshop with bits and pieces of these textures inside a bigger texture and save it and also manually document it where each textures are. And this really makes the iteration time suck.
OK, so if artist is not doing the work, we'll have engineers do the work, right? So, the engineer will have to either run some sort of script in the bulk-- build process or use some PayTool to run that and iteration time is pretty bad as well. So, we want to lift all that work off your shoulder when we designed Texture Atlas tool. So, you can just focus on putting the beautiful pixels on the screen.
So now, once you have Texture Atlas, there're some major benefits of having that. So, number one, it minimizes the amount of GL render state. If you issue GL to all calls that have identical render state, you can combine them into a single draw call to minimize the amount of overhead for the GL render script change.
It also minimizes the amount of disk I/O. Imagine you're loading 10-24 of this background tiles versus loading a [inaudible] of-- big texture files. We also minimize the amount of memory overhead as well as avoid memories fragmentation. I want to talk a little bit more about this and this is the Sprite Kit specific feature where-- if you look at animation frame, I'm idling here from top down, I'm about 128 by 128 size, somewhat like that.
And if I'm animating from standing up pose to an attack pose, that's an archer. So, now my frames stretch it into say 200 by 100 and if you play these animation frames side by side, you notice the feet will actually pop between frames. And to fix this, one of the common methods is to add transparency borders to your animation frame so that you make sure the back foot is actually locking down at exact position between animation frames. So, when you play this 42 frames at the same location, animation look silky smooth. OK, that solved the problem but you introduce these transparent borders and we are paying 4 bytes per pixel for something that really doesn't contribute to the screen output.
So, Sprite Kit is actually doing trimming for these transparency edges before put into to the final Texture Atlas for you, it helps you save memory. And they require zero work from your side. You can also draw unusual shape, textures, so you don't have to follow on certain hardware the power of tool rule because the texture can be any size in putting into a Texture Atlas. I can't render that directly on the screen without having to worry about that. So, to create a Texture Atlas using Sprite Kit tool is super easy. It is integrated directly into Xcode.
It's just a matter of dragging a folder into your project and you'll have your Texture Atlas. As long as the folder has extension called ".atlas" we'll go ahead and process them. Each folder will turn into a single Texture Atlas and you can start referencing them at runtime right away. That's it, as simple as that. So here, we have a bunch of files from Finder, drag them into Xcode. Call them Environment at a folderextension.atlas, press Command B and you are done.
You can also use the Texture Atlas generator not just for 2D Sprite Kit enabled games, you can also use it for any OpenGL enabled games because the Texture format is OpenGL compatible. So, if you want to build [inaudible] game, I use Texture Atlas say for [inaudible] you can go ahead and use that, the output is atlas C bundle format which contains a PNG file which is the Texture Atlas texture and the plist file.
And plist file, you can just read it in and iterate through the NCDictionaries and it will tell you all the attribute of every single texture that include whether they are rotated or not. What's the original size? Where it is inside the quad of your Texture Atlas and so on.
But if you are using Sprite Kit product template, we automatically turn on this build setting for you, so you don't have to do anything. But if you are using-- trying to enable this for your OpenGL game or some game that's not using Sprite Kit project template in build settings, make sure you turn this fully on which I'll show you in one of the demos. So, what do we do for you when you use the Texture Atlas? What really goes on when you press Command B? So, we do the very basic automatic combination of textures.
We generate hardware specific Texture Atlas. So, if you have a folder of images that contains Mac, Mac OS 2X, iPhone, iPhone 2X, iPad, iPad 2X and the new iPhone 5 resolution format. Guess what? We generate a Texture Atlas for each of those devices. So, when you load a Texture Atlas on the iPhone, you don't have to pay a cost for loading the similar textures for the iPad or iPad 2X, it doesn't really matter. We don't want you to pull-- pay a memory overhead for that.
We also do a Texture split. One, the Texture Atlas greater 2K x 2K resolution. The reason being is 2K x 2K, it is the maximum texture size that's going to be supported on all iOS 7 devices. So, as soon as they go beyond that range, it will split into another Texture Atlas file and it will automatically handle that and you still get the single plist file.
So, what else do we do? When we process each of the source images, we do automatically rotation which will take by 90-degree and look through the light, "Hey, this might fit better. OK, let's rotate it, leave it and put it in." We trimmed the transparent edges, so you don't have to pay anything that doesn't have final contribution to the screen. But when you request that texture even with the same dimension, that's how it was on the disk? So, it's magical.
For textures, that's 100 percent opaque. We're also doing extrude on each of the edges by copying those edges out by one pixel level. So, if you have a tile-based game like Adventure and you move the cameras around the background, you will not see flashing edges between because the GL is rendering into the neighboring Texture tile.
So, here's a quick summary of how we switched from loose files in Adventure demo to use Texture Atlas. Originally, we go from 1660 file down to 26 Texture Atlases. So, file iOS significantly reduced and also have some memory savings. So, if you look at the animation pixels that we're trimmed, we are trimming the transparency pixels for each of the input Textures. So the summation of the original pixel count was 90 megapixels before we turn in to Texture Atlas.
And after using Texture Atlas, the number of pixel count reduced to 29 megapixels and that's about 61 megapixels of saving, each pixel cost 4 bytes and we'll save about 244 max for doing what, dragging a folder into Xcode. So, now, here's the fun part. We have a Texture Atlas, how do we load it? OK, to load a loose file from disk, we have this SKTexture class called TextureWithImageNamed, you pass an image and we'll load from disk to SKTexture instance.
Notice, there's couple of things that's going on in the background. Now, I like to share a little bit of insight with you. So, Sprite Kit is maintaining an internal texture cache. So, if you create a spaceship texture instance, and later on in another class, you try to say, "Hey, I want to create SKTexture with the same spaceship." And guess what, we keep track of all the SKTextures, that's a memory for you, everything is probably reference conduit. All we do is, all right, reference count plus one, give you the previous instance, so you don't have to pay additional cost for creating a duplicate texture.
And also, this way, you don't have to have some sort of a [inaudible] manager, middle layer that keeps track of all these texture pointers that say, "OK, this is app delegate. Hello, I'm here, I'm the one that's loading all these textures and now let's keep array of pointers. So, if it's [inaudible] class, here you go." You don't have to write any of that.
How do load now a texture from-- a Texture Atlas once you put them into an atlas. It's the exact same API. You just reference its loose file name, you don't have to worry about which Texture Atlas it is in. You don't have to manually load a Texture Atlas, this way is 100 percent automatic. My favorite quote from Steve Jobs is, "It just works." So here, we have a Texture Atlas, inside we have the same tree that's on the bottom left corner. Let's use the same API load, you get exact same texture image.
And now, I want to talk a little bit about loose file versus Texture Atlas. We support both and they are interchangeable. So, number one, it gave you the advantage of switching back and forth between lose texture files versus Texture Atlas. You no longer have to recompile your code or having a, "Hey, if debug, let's use texture files. If released, let's use Texture Atlas." No, you don't know-- you don't have to recompile code for any of that.
And second, we take loose files, loose files will have precedents over Texture Atlas. So, having environment Texture Atlas and inside you have a tree object in there and you give that build to an artist. And the artist will say, "Oh, I wanted to just make some minor changes with these trees.
I don't want to preview that." So, guess what, the artist can just drop in that tree-- the new tree that he or she created inside your bundle, re-launched the game, we found out there's a loose over tree and then we'll manually load that, so you don't have to again compile, debug build our special artist version and you hand it out.
OK, we also provide, for advance users, the fine control of over a Texture Atlas. So, if you do feel the need of creating a SKTexture instance, feel free to do so. We support-- we follow the same UIKit and AppKit naming commands and notice here when you specify your environment, you don't have to specify extension, you don't have to specify I2X-iPhone, any of that.
So, we'll use SKTexture Construct-- the Class Initializer initialize an environment Texture Atlas. We get number of names which is array. We'll go ahead, iterate through the array and create a corresponding SKTexture by calling the SK-- the Texture Named Method. And now, I want to have a quick demo of creating and loading Texture and also I want to cover the two ways of loading a Texture from a Texture Atlas including the loose file way and second I want to cover the automatic-- the manual way of creating SKTexture Atlas instance and iterate that. So, we'll begin by creating OS X project.
[ Pause ]
Sure, so we need to render some background in our scene so we can go ahead and drag in a-- or borrow some of the environment art from Adventure. So Adventure Atlas, drag that in, drop it in and say, "I want to copy it over to the target, finished." Let's have a look. So, we have about 12 files here and they each look different, so we decided to use the big tree base which is the first picture and let's use that as our background, how do we do that? Let's clean up the initialization code.
[ Pause ]
All right, so let's create a Texture, let's load that Texture by creating Sprite node so we use the spriteNodeWithImageNamed by specifying the loose image name here. If you look at the big tree base, that matter was a big tree base [inaudible] on the left side, set up the anchorpoint, set up the position and as with the scene, we can just build and run that.
[ Pause ]
There you go. So we have one node in the scene and that's our background. OK, so this is the simple way of loading a single Texture from Texture Atlas. Cool, so let's look at the app bundle to see what atlas, what actually goes into our app. So, say, show Package Contents, let's go to the Resource folder and you notice you have an Atlas C which is the compiled version of the Source folder.
So inside, we have our Texture Atlas which pull images in there, have the corresponding plist files, you can see aliases, whether the image is fully opaque or not, we can perform some optimization in GL level. What's the original size? Where it is located inside the Texture Atlas, whether the texture is rotated, all the attributes that's needed for the runtime? Now, let's go ahead and animate the character. Let's drag in a Boss-- a packet animation into our scene.
So make sure to copy into the folder so now we still call it atlas extension and now if we look at, we have about 42 animation frames of this animation sequence. So, let's go ahead and add a character to the scene and start animating him. So first, I'd like to add an instance variable called Boss and now we have this animation frames that we want to load it in. Let's create NSArray that contains all these animation frames.
[ Pause ]
So let's initialize the array, create a SKTexture Atlas instance that's Boss-- called Boss Attack which matches our folder name. We want to iterate through this Texture Atlas by look up, how many names are there, create a corresponding SKTexture instance by using the Texture Named Selector, here you can see--
[ Pause ]
And now we want to add a Sprite node called Boss and then we set this position to be the middle of the screen and let's go ahead and add that in. And we can build that and have a look. There you go. So now we have two nodes. One is the tree, one is the Boss, so let's go ahead and look at the Bundle. And notice now, we have two compiled Texture Atlas and for the Boss, 42 animations packed into a single Texture Atlas. However the corresponding plist file.
[ Pause ]
And now to animate it, let's go ahead and add our animation playback logic to say the mouse click handler so what we want to do is we'd run action on the Sprite which is the Boss character. So, we first defined animation playback speed, we use SKAction of anime with texture and now our Boss should be able to animate.
All right, every time we click them-- the button, we'll have animation sequence. It's very simple. So now, I would like to show you the Build options. So if you go to Build settings and just make sure, because we're using the Sprite Kit template, so this option is automatically enabled.
The next part I want to cover is visual effects. Generally speaking, there are two vague categories of visual effects. Number one, post processing which means that we are to perform some level of image processing on a given render target which can be [inaudible], frame buffer or an input texture.
If you're trying to say you have a racing game and playback a motion blur, usually people grab the frame buffer, downsize, followed by app sample and then you squeeze all that with radius blur that has alpha 100 percent in there so the inside of the center of the screen as 100 percent you focus and slowly blurred out and you [inaudible] that on the final screen. So, totally support that with-- via Core Image Filters. We can't show a quick demo and we also support particle systems that usually people found a very large number of small particles on the screen and similarly it's a lifetime and we can use SKEmitterNode for that.
So in terms of using post processing with Core Image Filters, it can be applied to any SKEffectNode so the effect will apply to all the children, all the children on this app node will get flatten first and the effect will be applied on this app tree so you can specify any filter on the filter property by creating a new CIFilter.
So here, we created a Gaussian blur which is quick and simple. And you can also apply Core Image Filters with any textures. So the Texture has a selector that call Texture by applying CIFilters, here we apply same Gaussian blur to the [inaudible] input texture and then you get a result texture as having that effect on there.
So, we'll have a quick demo of Sprite Kit running with CIFilters. So here, we have a trees demo from [inaudible] DC 2012 which was created using a development version of Sprite Kit and CIFilter. So here, we're rendering trees with three different layers. We can go ahead and add, you know, Gaussian blurs to blur all to show some depths, I know as well as add some random effects so that it will show the creepy feeling. We can add some particles and use the old green filters on top and on the very top, we can add a distortion of the [inaudible] environment by applying the distortion effect on the wall, on the bulk.
[Applause] So, quick and easy. You don't have to write any of the specialized shaders and all of this is available on both OS X and iOS. Now, I want to dive in a little bit deeper for particles. So the particle system is being heavily used for Adventure. So the leaves, the damages, the flashes and the spawning effects are all done with particle effects. And the Adventure Game actually didn't create those particles manually. It uses one of the tools that I will provide as part of the Sprite Kit Framework.
I iterate these particles and generate SKS file and at the runtime, we deserialized the SKS file into SKEmitterNode and add it to the scene. All the property are already set and it's good to go. In the particle editor, we provide about eight templates and this is just one of the templates you can use to get you started to whatever the quick effect that you are looking for.
In terms of SKEmitterNode, we want to provide a flexibility for you to get to the exact look and feel that you want for your game in order to generate a particular look of the particle system therefore we provided a lot of properties. But the [inaudible] property is very time consuming therefore we build a particle editor inside Xcode.
You can use it to edit any of the attribute of SKEmitterNode and also is a good design pattern because they separate the design of the data away from the engineering of the code. And it's also one of the best ways to learn about SKEmitter attributes, because it live editor you can just drag an attribute to see the change lively because we integrate a SKView inside Xcode. Here is a quick look of what the particle editor look like inside of Xcode. As you can see, we open SKS file which a particle file inside Adventure and then we can quickly make a modification to birth rate and we see the effect right away.
For particles, we also provide keyframe sequences. You can use it to apply some of the properties of the SKEmitterNode. Here, we are applying the keyframe sequence to the color. So, it gives you 100 percent control of what a color sequence look like from the birth point of a particle till its death. So here, when the particle was spawned, it have a little bit brown color and then later on at 25 percent of age, we go to a white followed by a red and when it's-- at the end of its life cycle, we turn to a slightly blue color.
You can also use keyframe sequences on other properties. For example, apply it to a scale. You can construct this in code. To-- Here, we have a quick example of showing you how we can use a keyframe sequence to apply to a scale. So here, we specify different timelines and once it was spawn, it have a scale of 0.2, at the quarter of the list is timeline, we change to a scale to 0.7 and any values in between will be interpolating that and you can just set directly on a scale of sequence and it will be affecting the scale throughout its lifetime.
You can also add actions to particles. Each particle, as soon as they are spawned by the emitter, can ask you some of the complex behaviors. So if you have really, really complex behavior that you want-- you're going after for SKEmitterNode, we have action property. Here is a quick example. This is actually a particle system. Each of the particles is playing back 42 frames of animation sequence on a certain speed. Just to show you some of the basic functionality of how powerful particle actions can give you.
And now, once we tune the particle system to the exact look and feel of what we want, how do we load that at runtime? So what we do is we use NSKeyedUnarchiver and just say, we want to unarchive BossDamage, the SKS file and that's it. It's a one line and at this point, you have your SKEmitterNode instance ready to go, ready to be added to the scene, and ready to animate.
Before we jump into the demo, I want to do some-- give some of the quick recommendations for using particle system like any particle system, overhauling is not a good thing. So let's-- one of the good recommendations I would give out is to keep the birth down and we'll recommend to iterate your particle files inside using the Xcode editor to allow you to see the changes live, perform iteration quickly with no [inaudible], command S, command R and you see the change live right away.
When particle emitters are not visible, I'll recommend you to remove that and Spencer later on will help-- will come on the stage and show you how-- sometimes a few particles can just achieve the very same look that you're looking after. So with this, I will start a quick demo of using a particle system. Let's go ahead and create a Sprite Kit project, let's call it demo particles.
So in here, we already have the scene. Let's go ahead and create a particle file from resource. As you can see, there are eight different types that's being provided. So let's just maybe go crazy with starting with spark. Make sure it is in our target. Let's create-- and this is what it look like. Cool. So how about that's load that in game? Here I want to tab it, so that every time when a mouse click, let's generate SKEmitterNode and add that to the scene. Use NSKeyed--
[ Pause ]
-- Unarchive with file, that's where we're looking for and then we're going to NSBundle, getting the mainBundle and then we're going to get pathForResource of our-- my particle file with file extension, sks. Then-- well, we have some error, we'll go ahead and fix that. I set the position to be the location. We'll add that to the scene.
[ Pause ]
[ Pause ]
All right, so every time I click the mouse, what's the particle count now? 6,500, let's do at 60 frames per second. So, OK, so that's easy to load, one liner, setting the position, adding it, and let's go ahead and make some changes to the particle, let's see how easy it is.
So, I want to create a similar damage look as soon as I get hit in the [inaudible]. So, let's go ahead and make some changes to these particles. Number one, the birth rate is a little bit too high, I don't really need 2,000 so let's go with say, 400, that looks a little bit better and the lifetime, I want to make it slightly shorter, so let's go with that and add a little bit variance for your lifetime.
OK, so that's a little bit better. And how about we-- now the Y value is a little bit flat, let's go ahead and stretch that by a little bit, so that will be 50. I think the angle is a little bit limited, so let's go ahead and do all angles.
Speed is a little bit too high right now and we say, let's change it to 200, and let's not do variance, and obviously if this is a top down looking games, so we don't really need a gravity here, so let's go ahead and set that to be 0, and we can change the scale, say go from 0.4 and we want to do an increment on the scale in a positive direction and wallah.
So, if we decided to go ahead and change some of the damaged colors, we can change slightly like that. And how about if we make-- if we decide to that this damage is only one time, I get hit, I spawned 200 particles and the particle will finish. So, let's-- we can set that by setting the maximum attribute here. We go 200, so let's run that. So, every time I get hit, get the particle and particle goes away.
[ Applause ]
Next, I like to invite Graeme Devine and Spencer Lindsay, will be talking to us about building the Adventure Game.
- Thank you Norman. Hello, I'm Graeme Devine.
- I'm Spencer Lindsay.
- Today, we're going to be talking to you about building Adventure. So, what happens with the do to build it? [laughs] So, we know a little bit about it. We're going to talk about some of the technical challenges that we faced in actually making the demo and some of the art challenges that's being able to-- that we have to do unless you're making the demo too.
So, let's take a quick look at the demo again, just one last time so that you can-- so you've seen it lots. We talked about how the game has [inaudible] you got to talk about. So, the parallax effect that-- as the guy walks around, we have leaves on our trees and the-- that the, you know, that look so nice and parallax and so forth that looks awesome.
The guy is bumping into the wall and he sees a colliding well and he's sliding across this, you know, the walls nicely, beautiful leaves falling from the trees and looking, you know, nice and thready as they fall to the ground and Sprites are animazing like that's a real characters and we're going to talk about how we actually made that too.
Let's talk about the parallax. Parallax is an old effect that's a, you know, that we've used in games in many, many years. It's actually really easy to do. If you're looking at the formula then writing it down, you can stop doing that now 'cause you don't use formulas. The sub class SKNode from more or less everything that, you know, that we have in the game, the characters, the, you know, that the parallax nodes, anything that's in the game is really a sub class of SKNode, it's a good way of doing it.
But, you can see here that the parallax has three layers as, you know, kind of a root layer with the tree at the beginning and as it goes up, the parallax slides away from that [inaudible] 3D effect. When we think about cameras in 3D games is I think of the camera 3D game, what's a camera in the 2D game mean? And for our [inaudible], it's the center of the screen.
It's always kind of that the center of the screen, everything moves so fast, the parallax moves away from that and, you know, movement. What's driving the movement on that camera? It's actually-- in this case, our characters as the [inaudible] screen that pushing, you know, the pushing the world around and with that changes the actual camera position.
So lesson number one is always, in games, fake it all. [laughs] You make it a game, make it look good. That's the most important thing. So, it's a good [inaudible] fake 3D. Fake 3D is actually was easy to do. We have our little root object which is the roots of the actual ParallaxNode and as our object moves from the center of the screen from minus one to plus one the X and Y axes. That object is part of the ParallaxNode, kind of miscue away from that object, away from the center to give the effect of parallax. Possibly, the code that we wrote actually do that.
So, ParallaxSprite, you know, from an SKNode, [inaudible] much a little set up in there, then the actual code to actually setup, we're like good old days so we just want to load three PNG files and we want those three PNG files to be part of our ParallaxSprite and we have a voodoo number that is really the heights of a tree, 150.
That doesn't mean anything. It's just the [laughs], you know, the effect where I see [inaudible] and we want the Sprite to fade off an alpha as that character approaches the tree, we ought to be able to see the character underneath the trees and so we're going to talk about how we [inaudible] to did that too.
So, if we look at the SKNode and how it's actually set up, you can see that the SKNode is, you know, at the bottom there, there's ParallaxSprite, we got these three children, these three PNG files on top of that, you know, the RootNode and then on top of that, [inaudible] leaves that come out.
And if run through how things were actually done in the Xcode, the two lines of code work out the opposite X and opposite Y. That's, you know, that kind of show which sides are off on, you know, from that zero to one that we showed you before and we work out that for X and the Y.
Then we walk through our children and we multiply that with voodoo number, really it's just a voodoo number, they're OK to have voodoo number, it's a game. And we set that to the position on the node and that actually produces that parallax effect as the Sprite moves around because the children moving away from the center of the screen multiplied by that number chooses a very nice parallax effect. Very, very easy.
So, we also have the character-- the trees fade as the character approaches that. So how do we do that? So the Adventure character, the-- there's a ton of them in the game 'cause it's multiply the game. We work out which Adventure character is closest to this tree, look how far away that is, and then if it's too far away, it's, you know, we set it to being completely fake, so these are going to be completely fake, but if it's close enough, we do a nice little square fall off on-- as the character approaches it, so that's the tree fades away nicely. And we make it stay around a little bit, so that's what with the 0.1 and 0.9. The Collision Mapping is kind of interesting. You can see here [inaudible] collision volumes.
We [inaudible] many of them in the game. I think there's like 20 of them for the entire level. We actually ended up using the physics system. But we then definitely start out using the physics system. To begin with, we did this the wrong way. It's OK to do things the wrong way.
We load, it's already great big PNG file and we load that in the 2D array if my guy is standing on black, he can move around. My guy is standing on red, he stops. Simple, that's the way-- that seem the obvious way to do it. It turned out to be too much code, so that will be a lot of code and just him seem more and more and more to try and fix the, you know, fix the problems. It works terribly.
The guy will get stuck on the wall and he won't be able to move out and he won't slide along like [inaudible] slide along, you know, it was just not a good choice and he-- wrong. But it sure seems obvious, you know, [inaudible] write the code, I'm not going to use Sprite Kit.
I'm going to roll this on my own and it's going to be awesome. No, no, I'm not that also. [laughter] So it turns out the right way was actually pretty darn easy. It was to use the physics system built into-- in the Sprite Kit and as it turned out to be zero lines of code.
But let me qualify that 'cause there's no such thing as zero lines of code. There is a little bit of set up and that's full lines of code. So we have a rectangle which is one of those red or blue rectangles for the actual walls. We add that to the physics party and we say, "Hey, this is physics party on the Sprite." We say, "It's not been moving around, that wall is going to stay still." It's going to be called the wall and we add it into the world. And boom, that is it, we had done, that guy collides with the wall, he goes along nicely. It is all done for you from then on out and we didn't have to do anything. It was like happy day. [Inaudible] Spencer at the second lesson.
We're going to talk about the art pipeline. We have-- there's a lot of stuff to talk about in the pipe line-- art pipeline. But, I'm going to go through three. The first one is plan your art. Make sure that you know what your art is going to be before it goes in, because our resources cost money and one of the things that Graeme and I do is that Graeme will make art.
Graeme will do the program or art which is, you know, that's awesome, because, you know, Graeme will give me a PNG that is the right size, the right bit up, the right orientation or whatever. And I can take that as a communication tool and then use that as, you know, as a template for us to put the real art in. So, another one is limit the size of your resources, limit the number of your resources, make sure that you're not using system resources crazy because a lot of artist wanted to put a ton of polygons or [inaudible] or, you know, particles into a system.
And then you, you know, you bug the system, it runs slow and you're fighting with the programmer over assets. And if you can get the coolest looking effect with the smallest number of resources, that's a total win. We have kind of a contest in the lab or, you know, we try to get the neatest looking thing out of the smallest number of system resources.
And then also build only what you need. So, if you're going to build the coastline and you see part of the ocean, don't build the whole ocean, you know, just get what you need especially in 3D games but in 2D games this also applies. So you make sure that, you know, what's going to go on the screen, if it's only going to show up at 256 pixels in screen size, make it 256 pixels. Don't make it 4K, you know, make your assets as big as they're going to be.
So, this is kind of a napkin sketch, one of the critters and this was our first, you know, our first presentation of the client and they came back and they said, "You know, I like the eyes but let's get some bigger eyes. Let's get some, you know, some huge googly eyes," and we put those in and they were, "Whoa, that's a lot of eyes, let's trim the eyes." [laughter] So then we came back and we had built this thing in Maya by this point. We have the big googly eyes in Maya.
And we did a tap down Render and all of our animations were basically on alpha, tap down Renders in Maya, frame by frame out as sequential PNGs to a folder. And so this was our-- our fix, was we made it big green warts instead of big red eyeballs. And then-- so this was our final little dude, you know, this is kind of our-- after iterating back and forth, got to the critter. And then made these spreadsheets out of-- what are we calling it again?
- Atlas files.
- Atlas files, sorry, artist, programmer. [laughter] And they were super easy to build. I mean, I-- we would build a sequential list of PNG files, throw them into a folder, put them into Xcode and bang, we had this little guy getting hit by an arrow or whatever. Then the programmers were able to take that, assign it to a spread, assigned to a character position and you know, and trigger it when needed.
So another thing that we did was this particle system that you guys have been hearing about. This was kind of-- this is that wind that I was talking about where we have-- it's a really tiny amount of resources. We used 25 particles twice and so 50 particles total for all of those leads all over the world.
And you can see here, we use two little tiny particles, two little tiny textures and started them off at 100 percent scale down to zero. Zero percent opacity up to a hundred and it ended up looking like a bunch of leaves kind of floating down on the forest floor, it really worked out well.
Oh, back to me. Lesson number three, agree on stuff. You know, we're a small team and we had a very tight deadline for this. So day to day agreeing on what we're actually doing is kind of important. So, let's take a look at our internal pipeline. Communications, [inaudible] in the same office, so it actually worked out pretty darn well. I can just yell at him that, you know, and-- we never yell, yes, yes on doing stuff, but communication is very important. And it applies to things like naming schemes. You don't think that these things, I just want to make game and pull the structure.
Even the coordinate system that you use in the game 'cause we use all these tools, we use Maya, we use Photoshop, we use Xcode and each one of these has a different coordinate system and you'd really have to agree on what the number one means. And bigger things, like orientation. Orientation is a [inaudible] games.
Agree on which direction is up and write it on the white board and do not end up with a hacked file for the one is off by 90 degrees, it has plus 90 [inaudible] because that will come and haunt and you will shift and all the guy will become like this, and you wonder why and it's a plus 90, you have an [inaudible] that you forgot to change. Sorry, that might have happened. [Laughter]
Like I said before, programmer art is great and there's probably like three artists in this audience. But go home and talk to your artist and tell them to let you do art, because the art that you provide to us is an invaluable tool of communication. It allows us to iterate back and forth with you when you give us that artwork, you know that it works in code. For example, I mean the next one here, this is a Graeme's art this worked perfectly for Graeme because he has a vector, you know, that-- which way is this thing going and he's also got these three different-- this represent those three parallax layers.
So, he was able to go in, send box, play around with like, you know, all of his numbers and get all of the layers to work correctly with the camera and then he gave it to me, and then we built this stuff in Photoshop and made it all pretty and stuff and so-- then it actually look like a tree. And so, so yeah, programmer art is awesome, I love programmer.
The folder structure of the game is also really important. You have to remember, you know, Xcode does a lot of the work for you and puts things nicely into groups and so forth, resources, but that is not necessarily representing on the hard drive, they won't use all these tools, Photoshop, Maya, you know, these sound tools.
They're supposed to put things into separate folders that you will agree on, separate your game data from your level data from your game data as localization and so forth, but, you know, [inaudible] different folders. It sounds simple but that folder is where you live, so it's really important.
And I-- finally, this is another great way to communicate back and forth. Our rule that we've kind of like been going back and forth on for the past like 25 years of game development is a meaningful name, just something that means something that, you know, that makes sense, so that if you get an artist or a programmer who comes into the project halfway that they are not trying to figure out what, you know, file 73 is, you know.
And then we use underscores, no spaces, and then we pad our revision by four because you never know when you're going to have 9,999 versions of that file especially when you're-- and seriously, especially when you're doing animation. You're going to have a ton of files in animations. And then the file type at the end is really important. We go into finder for everybody on the project and we turn on-- we expose the file type at the end.
This seems simple but it's-- it really, really helps when you're trying to quickly identify whether it's a PSD, a PNG, a TXT, or whatever. So, you know, agree on a file naming convention, you can use that if you want to, but use one that works for you but, you know, this is also a great communication tool.
Lesson number four, have fun. You're in the freaking game industry. [laughs] If you're not having fun making the game then you're going to make a rotten game unless one of the big things that we differentiate ourselves on is that Spencer and I are in the game industry. I don't work in the game industry, I never worked in my life and that's what we do every single day.
So, let's go over our lessons again. Just because it's a 2D Sprite engine doesn't mean that you make 2D games, we touched the tip of the iceberg with what we can do with Sprite Kit. We could have made it complex 3D game with [inaudible] and it would have been awesome.
- Physics is much more useful than for just drawing birds epics and programmer art is beautiful. I-- really, use your placeholders with pride and give them to your artist so that they can understand what to do correctly. And as far as the less is more, we've been talking about that a lot, see how you can get them the fastest running, least, you know, quickest booting game that you possibly can and make it look super amazing because if you can get a really cool effect or really great looking game out of just hardly any resource used at all, you know, well first off you're amazing and second thing, all you have to worry about is awesome game design. So, thank you very much.
- Thank you Graeme, and Spencer. So, lastly I want to talk about how to build, not only use Sprite Kit as part of your game runtime, but build awesome game tools to improve it in a recent time. What if we decide to take Adventure to the next level? What if we want multiple level support, currently is one level. What if we want to add save and load game support so that the user doesn't have to spend [inaudible] 30 minutes just playing and finishing the game.
And what if we want to build this more sophisticated and reusable actions for every single different effects for your characters? So, Sprite Kit, every single SKNodes support NSCoding and NSCopying, so what that means every single node can be serialize and deserialize to the disk or to the iCloud over the network, however you want. So, we can implement essentially a save/load game logic using one line of code.
So, let's look at the serialization API. It's quick and easy. All you have to do is use NSKeyedArchiver parsing any SKNode, you get NSData blob and from them on, you can do write to disk, send it to the cloud or that the serialization is just the reverse. Getting NSData blob, past to the NSKeyedUnarchiver and therefore you have the corresponding SKNode instance.
So to build Adventure, in this case, Adventure level editor, we create a level editor in Mac OS X using Sprite Kit. If you're able to add an overview, overlay view on top, you can place your level tiles on top and also use the overlay view to receive any of the user input as well as the mouse manipulations.
So if user drag and drop some tiles on there, you can make it the direct manipulation to the underlying SKNode. And once everything is properly placed, you can serialize now to the scene and essentially just load that at runtime therefore you have your game level and you don't have to use change some integer, recompile, and, oh, I'm off by 50 pixels, you don't have to any of that.
Custom Actions also serialize well too. Imagine we're building a golf game. So here, I want to simulate, OK, when the golf club hit the ball, what's going to happen? What if I-- the first thing I want to do is apply some sort of an impulse to make the ball fly.
And second, I want to play some task effects that play that, wait for all the particles to finish, and it will remove that from the scene. And in the meantime when in parallel when the ball hit-- when the club hit the ball, you will hear sound effects and maybe way with the ball is airborne and then we'll play a task trail for beginners to learn from their mistakes, "Hey, this how far I'm hitting and this is the projectile for that." And if I want to build all of these, this logic, I want to use it in game for every single time when the club hit the ball, I'll have-- we can build this in code by having a sequence fall away bat of groups, it's about 20 lines of codes. But if you want to tune that, the easiest way is to use serialization API for SKActions as well.
So, SKActions itself is serializable as well. When-- As soon as it's being assigned to any SKNote, it will be kicking off from the beginning. It's-- As soon as you assign the action to the SK node, it copies on write and when actions are completed, it will be finished.
And the last section I want to quickly touch base about the best practices of using Sprite Kit. Number one, we will recommend you to use UIKit and AppKit if you need a complex layout for your text. So, we do provide a SKLabelNode for a one line that UI allow you to animate that before any complex constraints as well as with the height adjustment where Holly [phonetic] recommend to use AppKit or UIKit. So, just like any other GL view, as soon as OS X, if you enable layer-backed, you automatically get all these benefits.
And one thing I like to point out is in OS X, I'm sorry, Xcode 5, we provide a major update to Interface Builder. So if you want to design your UI with the pipe-- breaking at the part into different flows. For example here, how main screen goes to character selection screen followed by going to weapon selection, you can totally design that using Storyboard inside Xcode.
And now, how do we improve your iteration time? So, we want to integrate Sprite Kit inside your code-- with your game code so you can use that to build your level editor, if you do action editor, you can use the particle editor to edit your particles at the Sprite Kit building Texture Atlas generator is great. You just drag a folder in and you have, therefore, you have your Texture Atlas.
In terms of performance tips, we provide a building stat to show you how many draw calls as well as how many SKNotes are being rendered on screen as you can see from one of my examples. We'd like to keep the node count low for node that's no longer visible.
You can remove that, but Sprite Kit does cull out invisible node, but there's the additional if statement that we go through. But if you know ahead of time, the object is going to be no longer visible, feel free to remove that. CIFilters are expensive if you apply them to a full screen. So, we do provide a property called [inaudible] so that this we-- the result will be cached into a frame buffer so that will be reused for the following frames.
Also, we like to talk about organizing your games into different scenes. Once if you treat scenes as the fundamental building block for your games, you can plan ahead, OK, how do I-- do transitions from scene A to scene B, from UIView into the game view, from Game to Pause menu, you can do all these transitions and make your flow much, much easier.
So and-- that's the end of the session. For any questions or feedback, please contact our evangelist, Allan Schaffer. I'd like to point out for those of you who have missed the introduction to Sprite Kit, there's online videos that's going to be available tomorrow, and have a great WWDC 2013. [Applause]