Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: tech-talks-2013-25
$eventId
ID of event: tech-talks
$eventContentId
ID of session without event part: 2013-25
$eventShortId
Shortened ID of event: tech-talks
$year
Year of session: 2013
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2013] [Tech Talk 25] Developing...

iOS 7 Tech Talks #25

Developing 2D Games with Sprite Kit

2013 • 48:07

Sprite Kit is a powerful graphics framework ready-made for developing 2D action games, platformers, puzzle games, and much more. Dive into the practical workflow of developing a 2D game and learn key details about the Sprite Kit API. Discover how to leverage built-in physics support to make animations look real, and learn about using particle systems to create essential game effects such as fire, snow, explosions, and smoke.

Speaker: Allan Schaffer

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

All right. Hello, everyone. Hope you're enjoying the day so far. My name is Allan Schaffer. I'm the Game Technologies Evangelist at Apple. And this session is about something that I know that a lot of you are very excited about just from talking to you over in the lab and before in the day, which is Sprite Kit.

But, you know, before I really dive into that content, what I want to do is just reflect on some of the motivations behind Sprite Kit that we talked about back when we introduced it back at WWDC. You know, if you really look at the App Store and some of the most iconic games that are on the platform, many of them are 2D, of course, and a lot of them have some very common needs. They need to be able to draw a lot of sprites and particles for characters.

They need to be able to create a framework for 2D game development. And something that would be built from the beginning to accomplish those things with performance as a central tenet of the framework. You know, not something where you really had to kind of opt into the performance API. Or, you know, you had to kind of opt into the performance API. Or adopt something special to do that. But where efficiency was really part of the baseline of the framework. Another big motivation also was that it would keep up with our technologies.

Something that from day one would support 64-bit applications. And would support iOS 7 and so on. And a lot of that is what's driven the development of Sprite Kit. And I think that we've built something that you'll really love. So Sprite Kit starts with a really fast, really optimized API.

And it's a really fast engine for drawing sprites, arbitrary shapes, and particle effects. And then it also provides you with capabilities for doing animations to animate those objects in the scene. And has a totally integrated physics engine to drive things, make them more lifelike and more fun in the game. And Sprite Kit also integrates with our media stack in iOS 7 to play audio and video. And to apply image processing effects to the scene.

And then it also integrates with our media stack in iOS 7 to play audio and video. And those are some of the capabilities on the API side of Sprite Kit. But we also wanted to provide some tools to simplify development. So within Xcode 5, there's a particle editor built right into the tool. So you can edit particle effects and bring them into your app. And a texture atlas generator as well. Which is something that's very core to getting good performance in any 2D game engine.

Here's my agenda for the session. Today I want to cover some of the real, real basics of Sprite Kit. Just go through sprites and particles and how to get started from scratch with Sprite Kit. Then dive into some more advanced topics like textures and texture atlases. Really, texture atlases being one of the two keys to getting great performance with Sprite Kit.

Then I want to transition to some things that we didn't cover at WWDC. The first of those is scene transitions, which is something that will be very commonly done in a Sprite Kit-based game. Then cover drawing order. That's actually the second key to getting great performance when you're using Sprite Kit. And then go back to some just introductory material again and show you how some of the animation part of Sprite Kit works.

So let's get started just with a quick tour of the API and some of these basics with sprites and particles. So in Sprite Kit, your scene is composed of a tree structure of many elements called nodes. All of these things are nodes, everything you see. Now, some nodes contain visual content, shapes, images, all the things in blue here. So for example, if this were a scene, my background image, the trees, the hero, the character, and if I had a life counter and a hit point bar, all of those visual parts of the scene are different nodes.

And then other parts of the scene, typically the interior nodes in this graph are often used as organizational elements for grouping or just to be able to do transformations on an entire subtree of the graph. So you see those, those are the green ones here. So I'm organizing this graph logically into a background layer, a foreground layer, and a HUD layer. And I'm able to apply, change different properties on those nodes and those changes then apply to the children.

But then, of course, we have to take that scene graph and somehow get it to be displayed in your app's view hierarchy. And to do that, you'll use an SK View. So an SK View is a UI view or on the Mac, an NSView subclass that knows how to kick off the rendering of your scene. And so once you've dropped an SK View into your app, you can tell it to present the scene, and you'll be off and running.

And at that point, then, that's where the Sprite Kit game loop kicks in. And so I want to show that to you here. So this is a sequence of events that are going to happen every frame once your scene has been assigned to a view. So every frame.

Sprite Kit is going to want to render your scene, but before it does that, it does a few other tasks and lets you tie in your game code into the cycle. So every frame is going to start off with Sprite Kit calling your update method for the scene. And so this is where you can be doing any of your game logic. Maybe you'll choose to spawn new enemies, or you'll update your score, update the state of the game, choose to progress to a new level, and so on.

Whatever is specific to your game update for this particular frame. Then after you've done all that, Sprite Kit is going to evaluate its actions or any animations that are attached to the nodes in the scene that are active for this current frame. Then after those have been evaluated, you'll get a callback.

So, As shown here, did evaluate actions so that you can react to essentially the result of those animations. So this is after the animations have changed the positions of the nodes, for example, or whatever the result of that action is. So for instance, if an animation ended up moving a sprite off screen, then you might want to remove it from the scene so it won't be included in any further processing. That sort of thing is what you can do here.

Then after that's done, Sprite Kit, if you're using physics, it will then simulate any physics that you've set up on the nodes in your scene, and it'll update you about any collisions that have occurred and other things. And then it'll call you back to let you know that all of that has been completed. And now at this point, this is the last time that your code can touch the scene before it's rendered.

So you can make any final changes to it. Now before you hand off to Sprite Kit again. And so then finally, Sprite Kit tells, or the SK view, tells the scene to render itself. And then hopefully this entire loop then continues 30 or 60 frames per second. Right? So that's the game loop.

But so now let me switch over and take a look at some of the different kinds of nodes you have in Sprite Kit. This is an illustration of the class hierarchy. So all the node types inherit ultimately from SKNode, and then there's a number of different specialized subclasses to do various kind of interesting things in your game. So nodes for text labels, nodes for particle systems, for drawing arbitrary shapes, for drawing sprites.

Your scene is actually a special kind of node that can have effects applied to it. And a crop node is a kind of node that can apply a mask. Now I'm not going to go through all of these, but just a couple of the highlights here, starting with the base class, SKNode.

So this is an object that if it's in your scene, it's typically something that you'll be using to organize other nodes into groups, or as a handle, for example, to transform a subtree of children. And you can see a few of the properties here of the SK node, and there's really, there's a lot more. This is the place, as you're looking through the documentation, that you should really start to see a lot of the capabilities of Sprite Kit.

Even though most of the time you'll probably be using the Sprite node, check out all of the methods and properties that can be set on SK node, because obviously they're inherited. But so, you can see, you can get your parent, the node's children, its transformations for position, rotation, and scale, properties to control visibility, like its alpha, whether or not it's hidden.

And of course, just note that any properties that are set on a parent node implicitly apply to that node. And to all of its children. So by setting the hidden flag, for example, you can hide an entire subtree, for instance. And then nodes can run actions, meaning animations, and they can take part in the physics calculations as well.

So that's SK Node. Let me just go on. Next up is Sprite Node. So this is obviously Sprite Kit, Sprite Node. Here's the most important node type that you have in the toolkit. And really, probably the main node class that you'll be using in your games. Probably 90, 80, 90% of the nodes that you have will be Sprite Nodes. And a Sprite Node can be one of two things. It can either just draw a simple color, or it can hold an image, typically with alpha to give the illusion of a cutout shape, or it can be a blend of the two.

[ Transcript missing ]

There's a whole lot of different properties that you can set up with your particle effects. Of course, you're going to specify a texture image, and then there's really just all these different parameters that you can fiddle with, looking at the list. The scale, the rotation, the emission, blending, birth rate, and so on. Kind of endless possibilities here. There's a lot of fiddling that you could do.

Very early on, it became clear that we needed to move this out of just the code and provide a tool to be able to manipulate all of this. And so that's why we have in Xcode a particle editor built right into Xcode 5, so you don't have to leave your project. And really, it's just very, very simple. So what this does is it lets you have an SK emitter node, and you can just manipulate all of its properties visually.

And then just have that saved into your project. It's going to create an archive of that SK emitter node, and then you can just use NSKeyedUnarchiver to bring it into your app. And this is great, because it lets you kind of separate all the adjustments of your particle system outside of a compilation cycle. Okay, so that's actually where I want to stop and show a quick demo of a simple SpriteKit app.

That I'll build up here. First, let me give you just a very quick tour around this. So this is really just an app that's built mostly off of the SpriteKit template that you can get in Xcode. And then I've added just a couple of simple things. I've added a ship into it, but here's just how it works. When you initialize this scene, it starts out by setting its background color. It creates a label node that's going to say hello world in the middle of the frame.

But that wasn't very interesting. So down here, when you touch, I have it set up so that it will create a Sprite node the first time that you do a touch. And set the position of that Sprite to the location of the touch and add it to the scene.

And then as I move around down here and touch is moved, I'm just updating the position of the Sprite called ship. And as I'm moving. So let's just run that in the simulator really quickly. Show you where we're starting. Hello world. Okay. And now here's my Sprite. And as I'm moving around just by hand here, the Sprite moves around. Okay. Great.

Nothing too exciting yet. But on the other hand, I mean, that really is kind of all it takes to get up and running with the very, very basics of SpriteKit. And that's kind of nice. You know, you can have something where then you are beginning to work on your artwork very, very quickly. But so that's where I want to go with this. I'm going to start with this. I'm going to start adding some things in here. And I want to show you the particle system. So here I'm in iOS resources, SpriteKit particle file. I'll hit next.

And I don't know if you can see this very well. We provide a number of different particle templates. Here I've chosen fire, but there's a whole bunch more that you can go and experiment with. I'm going to stick with fire. Hit next. And call it exhaust. So there's the beginning of sort of a default fire effect that we provide.

And now there's some different things I want to go in here and adjust. And so let me zoom in so you can see what I'm doing. So first, I'm going to take the birth rate of the particles down into the realm of about 100. I can either just do that by moving the mouse around, or I can go in here and type 100.

I want to change the angle to go down instead of up. And I'm going to make this a little more narrow. Yeah, that looks about right. That's about where I want it, about half the size as it was. And then I'm going to add one more color to the end of it. So I think I may have to zoom out to find my color picker.

And sure enough, yep, there it is. I'm going to make it a little brighter. Okay. So there. I've twiddled a couple of things with this particle effect here. So now, if you can see it, it starts out sort of reddish and ends kind of greenish through the lifetime of each particle. And it's just saved here in my project, exhaust.sks. And I'm going to go back into the scene and bring that in and do some code here to bring it in.

So through the magic of Xcode, I think I can just paste in a line of code there. Yep, there it is. I know that you guys all can do the same as well. But okay, so let's look at what this is doing. SK emitter node, I'm using an NSKeyed unarchiver to, and then just make your way through all that to unarchive exhaust.sks. Right? So now I have my SK emitter node sitting in this variable. So I'm going to go back to my Xline of code. Okay. I'm going to set its position down to the center bottom of the ship.

And then add the emitter as a child of the ship. OK? Run that. Should be no surprises here. Okay, so there it is, right? So I have my emitter in the scene, and as I move around, it moves along with it. Okay, I'm not quite done here, though, because as you can see, everything is working almost correctly, but it kind of feels like the whole thing is like on a plate of glass, like the smoke is actually moving as the ship moves along as well. And that's not quite what I want. What's happening is because all of the emitter particles are being positioned relative to the ship, and as I change the position of the ship, that change propagates into the children, and so they're all kind of moving along with it.

That's not what I want. I want the emitter particles to be emitted from the position of the ship, but once they're emitted, I want them to be kind of in free space. I want them to be -- remain relative to the scene instead of relative to the ship. So I've got one more line of code.

So what I can do is on the emitter itself, I can say, hey, emitter, your target node is, well, in this case, self, because self is the scene. So your target is the entire scene rather than your parent. And now if I run that... Great. Now it's working the way you would expect. As I move the ship around, the particles just kind of fall relative to where they started from.

Okay, so that is just a very simple example of Sprite Kit. Good performance, of course, in this case, and obviously just some very easy code to write. All I've really done here that's more than a single line of code is the wraparound here for messing with an NSKeyed unarchiver, right? And that is itself very simple. Okay, so let's go back.

Okay, so let me recap the node types and what I've gone through so far. So I covered nodes, sprites, and emitters. I briefly mentioned scenes. I'll go back to that in a little bit. But then there really are, there's a lot of other node types. As I said, we have labels, which render a text string. That was the Hello World. Video nodes are very powerful. You can have video in your scene acting as a sprite, as a full-fledged sprite. You know, not just an overlay, but it can have effects that are relative to it and so on.

Shape nodes let you render a shape based on a CG path. Effect nodes let you put a core image filter, apply a core image filter to a subtree of your scene. And crop nodes will create a mask from a subtree and allow you to apply that mask to the rendering. So it's a lot of good capabilities there.

Allan Schaffer Okay, so those are the node types. And that's sort of just the very quick introduction to the node family and kind of how SpriteKit works. What I want to move on to now is to talk about textures and texture atlases. Allan Schaffer And textures, the fundamental SpriteKit bitmap image type is SK texture.

So this is the image that is in a sprite node, and it's the image that's also used for particles as well. Allan Schaffer And the thing about this class is really its flexibility. There's a lot of different ways that you can create a texture in SpriteKit. You can load them from a file, which is what I was just showing, using texture with image named or the convenience method that's on sprite node, which is what you saw before. Allan Schaffer You can create them from a CG image ref.

You can create them from an NSData with a size from a UI image. Allan Schaffer Or even create them from another SLR image. Allan Schaffer Or what I'll show here are the subregion of another SK texture. And the reason why I highlight that is because this is actually how texture atlases are implemented in SpriteKit.

Allan Schaffer When you're creating a texture from a subregion of some other one, we don't actually go and copy the bits out and create some second copy sitting somewhere. Allan Schaffer We just have it index that original texture. Allan Schaffer And this enables it to be very efficient. You can have one large image that's loaded and then be sampling subregions out of it very, very efficiently.

All of this is hinting at texture atlases, so let's talk about that more explicitly now. As I said, texture atlases really are one of the keys to performance with Sprite Kit. And really, with any 2D graphics engine, the ability to minimize state changes in your underlying graphics engine by using things like texture atlases is just absolutely critical.

So the idea of a texture atlas is to take a whole bunch of loose texture images, and rather than dealing with them all individually kind of in a small way for each one, is to just take them all and put them, basically paste them all into one larger image, and then sample regions of that larger image during runtime. So the underlying graphics engine then can just load that large image into the GPU.

And then the code is doing is saying, oh, okay, I want this little region of it or that little region of it, rather than having to have a bunch of little textures and be swapping them in and out all the time. So it's much more efficient. What you see here in this diagram is we ship an example along with Sprite Kit called Adventure. It's a big, full-fledged adventure game. And it has a lot of different assets and artwork.

And so on along with it. And so these are some of the art assets, like trees and caves and minions and so on. So rather than having, like, all of these be separate textures that we're dealing with and loading into the GPU independently, we just throw them all into a texture atlas for performance.

[ Transcript missing ]

And so this is something that's very common, and we hadn't really described that very much. But it's just important to realize, and that you're not doing anything unusual if you start using multiple scenes. Now, one thing, though, was that we also wanted to make the transitions, you know, of course, you want your transitions to look very nice.

And so for that, we provide an object called an SK transition. So this does a transition between your current scene and a new one coming in. And a visual transition. And there's a lot of different transitions that are built in. We have transitions for a crossfade between scenes or fade through a color. There's transitions of a door closing and the new scene is on the door. Or doors opening and the new scene is back on.

There's transitions behind that. There's transitions to flip horizontally and vertically to the new scene. Or to move and push and reveal and so on. But then also, something that I'm really kind of jazzed about is that there are a lot of CI filter, core image filter-based transitions. Swipe, flash, page curl, ripple, copy machine, disintegrate, dissolve. There's all these different core image-based.

Transition effects. And it's also possible for you to extend that and create custom transitions by creating a CI filter subclass. Now, you can't write a new core image kernel in iOS. But you can subclass CI filter and create a whole filter chain within your new custom subclass. And have some really intricate transition effects.

And I'll just, as an aside, I'll mention that same idea can also. Be applied to an SK emitter node which wants a CI filter as well. So, there's tremendous power there for doing image-based effects with your 2D objects or your entire scene. But so, let's take a look, kind of back up and look at a simpler example. Let's say that I have a transition.

And I want to move the new scene up from the bottom and cover up the old scene. So, I'm going to create a move in. With direction transition. Of direction up. And the duration of the transition is going to be two seconds. And then next. Okay. So, I have a decision to make here. About whether I want to pause the rendering of either the new scene or the old scene or both.

Now, by default, once I begin a transition, both will pause. But in this case. By default. In this case, what I'm saying is, no, I don't want to pause the outgoing scene. I want it to keep playing until the new one comes fully into place two seconds later. And keep rendering until that transition ends.

So that's what this does here. Pauses outgoing scene as no. And then finally here, this is the part that actually just does the work. I tell the view to present the new scene and pass in the transition. And that's it. Okay, so that's just a real quick look at transitions and dealing with multiple scenes and something you'll be actually doing a lot of with Sprite Kit under very normal circumstances.

Next, a really important topic. I want to talk about drawing order because this has such a profound impact on performance. It's another topic also that we didn't get to at WWDC last June. The lesson here is that the more that you can let Sprite Kit manage the drawing order of your sprites and particles and so on, the more optimization Sprite Kit can apply to the rendering. Okay, so the more that you take control, the less optimized it can be. The more that you give control, the more efficient it can be.

All right, so by default, we give you total control. We put you in control of drawing order. And it's a Sprite engine, and it's using the painter's algorithm for all of its drawing. So by default, we follow two very simple rules. A parent draws its content before it renders its children, and children are rendered in the order that they appear in the child array. Right? So let's look at an example here with this helicopter.

So the missiles will be drawn first, then the body of the helicopter will be drawn next, then the primary rotor will be overlaid on that, and then the tail rotor will be overlaid last. Okay. And you get the result that's here, and it looks correct, and everything is fine. And actually, this is okay. But the issue is if you are drawing a lot of these objects, then you'll want to do something that's more efficient. You'll want to give more. control over the SpriteKit to optimize this.

So there's a couple of different optimizations you can apply. The first is this, is that you can give your nodes an explicit height in the scene specified relative to their parent. So when this is added in, then each node's sort of global height will be calculated when we go to render the scene. And all of the nodes will be rendered from lowest to highest.

And then for any node where the height value is the same, where it's equal, then the parent will be drawn first, and then siblings drawn in child order. But so let's apply that now to this graphic here. So I'm saying that the helicopter overall is at height 100 in the scene. That's where the body is, right at 100.

Primary rotor relative to its parent is at height 1, but of course globally that would be 101. So is the tail rotor at 101? And then the missiles are at height 99, right? So obviously when we go to draw this, we would draw the missiles first, then the body, then the primary and the tail in the order that they appear here.

And so the result for one helicopter would appear identical, of course. But the issue now is if I add thousands of helicopters, then we would end up drawing all of the missiles together. We would end up drawing all of the bodies together. And then the primary and tail rotor. And that can be much more efficient because there's many cases where we can avoid state changes if that's the case.

But so now, besides this, though, there's one more piece of this. And that's that you can also, you have the ability to remove the constraint that nodes with the same height will be drawn in child order. And instead, give that power over to Sprite Kit to decide the order based on whatever would be the most efficient way of drawing it. Essentially, whatever would minimize the number of draw calls.

In the underlying graphics hardware. So, and we use the variable here is ignores sibling order. And you can set that on a scene. So, if you put these two things together, you set ignores sibling order on your scene, and then you give your nodes an explicit height, then now every node's global height is going to be calculated, and they will all be drawn from lowest to highest.

You know, thanks, so that they work with the painter's algorithm. And then, but for anywhere where two nodes have equal height, Sprite Kit is going to sort them to whatever would be the most optimal amount of batching, or essentially to reduce the number of drawing calls that will be made, reduce the number of state changes. Okay.

This is something that can give any Sprite Kit app really fantastic performance. This is what we encourage you to opt into for anything that you can hand this control over. Ultimately, this is something that's very simple, but it just requires you to set up your scene in a particular way. Here's the key insight to have with all of this and how you design the layout of your scene.

To compose your scene into layers implicitly, where give objects a common height in each layer, and then anything that might overlap with each other, like how the body of the helicopter overlaps with the missiles, put those things each in a different layer. Then set that ignore sibling order flag on the scene, and then Sprite Kit now can take full advantage of both your knowledge of how the scene needs to look visually based on layering, but also do something that's completely efficient with the graphics hardware.

Okay. So that is it, what I wanted to talk about for drawing order. And just now for this very last section of the talk, I actually want to back up a bit to some introductory material again and talk just in a little more detail about the capabilities that Sprite Kit has for drawing animation.

So the action system is how we do animation in Sprite Kit. We really wanted to create an animation system that would be very simple to use, super powerful, have a lot of flexibility, but ultimately that would also have an API that's very simple to kind of type and real simple for programmers to understand and read how an animation is put together just by looking at the code.

And so all of it is wrapped up in one class called SK Action. And as you can see, SK Actions can be applied directly to a node. They start running immediately. They're removed from the node when they finish. And so it ends up being something that's just very intuitive. It's almost like a scripting language for Sprite Kit because you don't really have to write a lot of extra management code for dealing with animations.

Allan Schaffer So here's some just simple examples of different, you know, essentially the built-in basic actions, the built-in animations that Sprite Kit provides, a few of them here. You can see an animation to rotate a sprite by a particular angle, to move a sprite from here to there, to fade the alpha of a sprite to, you know, change its transparency, and a couple to scale either uniform in each direction or different. And so this is just a really simple example of a little bit of the things that you can do to take control of different scaling in X and Y.

So these are just some of the really basic actions that you can apply. But now, something that's very cool is that you can just start to group actions together and combine them together. So for example here. So I can create action sequences where the first action will run to completion, for example, then start a second one, then start a third one.

And all that it needs is some very simple code here where I'm just running an action and specifying a sequence and then using Objective-C's array notation to lay those out. This would be a sequence. I can also specify actions to run, to be grouped, to run simultaneously. That's what I'm showing here. As you can see, each one can have its own duration. It can be completely independent of the other ones. Same kind of syntax here, just a group instead of a sequence. sequence.

And then compound actions themselves can be combined together. So here I'm using both a sequence and a group. So first I do, you know, with this, I would be moving the node to a particular location. Then simultaneously I would be scaling and rotating the node. And then when that's finished, I fade the node out. Right? And all of it, again, through some very simple-to-use syntax here.

Okay, so that's compound actions. There's also a number of different specialty actions that we provide with Sprite Kit. You know, one that's very commonly used is this for texture animation. So here I'm just, I'm animating my boss character from that adventure game to create this walking animation. And of course, all of these images started out as individual loose files. Then we put them all into an Atlas folder, and Sprite Kit created an Atlas for us. And then in our code, we just load them all out of the Atlas and then say, okay, animate. And so this can be very, very efficient to do this.

Another specialty action used a lot is to have a node follow a CG path. So you can see that here. This is something, I guess, really commonly used in arcade games, right? Where you would see the bad guys follow a particular pattern while they're shooting at you and so on. So you have the example that I just showed. You also have the option of having the orientation of the node stay constant as it goes along the path.

Another really commonly used action and a commonly needed functionality is the ability to remove a node from its parent, which ultimately removes it from the scene, meaning it no longer is rendered, it no longer costs anything for physics, and so on. And that can be done as an action. So you see that here, where, for example, I might, after something happens, my character dies, I would fade it out as one action, and then remove it from the scene.

And just set that up as a sequence here. SK action sequence, fade and remove. Right? And the last of these that I'll show you is just you can also set up an action to run any arbitrary block of code that you write. And this ends up being super powerful.

So, you know, here I'm setting up some actions that are going to fade a node out, so fade out with duration. Then I remove that node from its parent. And then maybe I have some more code that I've written that's going to show my game over menu, and I would run that block. But all of this can just be put in a 2D game. executed here as an action sequence.

Right? Okay. So that's quite a long look through actions, but there's really, there's a huge catalog of more actions, obviously, that I didn't have time to show all of you. Actions to colorize nodes and play sounds, repeat other actions, and so on. But really, just a lot of power here and a lot more that we can go into if you have questions about it.

But okay, so that takes me to the end of the session and my agenda here. Actually, we've covered quite a bit. Gave you a quick introduction to Sprite Kit and just sort of how the API works. Then we talked about sprites and particles, those different node types. Went into atlases to tell you how that works. Scene transitions, drawing order, and then wrapped up here with actions and animation.

If you have any questions about Sprite Kit, you're welcome to contact me. Here's my email address here, or come find us on the developer forums. And a reminder also about the lab. We have some great folks here in the lab along with us here in the venue who can answer your questions about Sprite Kit, help you optimize your code, and so on. So, thank you very much.