Developer Blog

Apple Sprite Kit Demo

With Xcode 5 Apple is releasing Sprite Kit, a great new tool for making 2D games that abstracts away a lot of boilerplate.

I threw together a quick demo using the Apple Adventure game as a reference. The code referenced in this blog post can be found here: https://github.com/asail77/OLTTSpriteDemo

As you scan through the code, you will notice that most of it is located in OLMyScene.h. With 541 lines of code, you are able to get a big bang for your buck! This is because Sprite Kit does most of the work for you. All you have to concentrate on is the logic of your game and the graphics.

Atlas Folders

One of the interesting features of Sprite Kit is the .atlas folder. Place a group of photos (i.e. a series of graphics for an action like “Goblin_Walk”) in the folder and Xcode will automatically create an atlas file when the application is packaged. You can see this by going to the package contents and opening one of the atlas folders. You will notice that all of the images are combined into a single image with some corresponding xml used to reference the original images.

The images are packed together and arranged in a way that results in less disk usage. Additionally, with less files to load from disk, you have better performance.

Note that -(void)loadWorldData is used initially to load all atlas and particle files before the demo begins. When these files are needed later, they are copied from memory rather than reloaded from disk:

1
SKEmitterNode *archerProjectile = [_archerProjectile copy];

This helps to ensure you are maintaing a good frame rate.

Partical Files

Another cool feature of Sprite Kit is the ability to create particle files and completely customize them. In Xcode, open up the Particle Files folder under Resources and view the .sks files to see this in action.

Note that for the ArcherProjectile particle file, I found that the particle would not travel nicely through the world (i.e. create a long trail) when “fired” by the Hero. This was solved by setting the target node to the world:

1
2
3
-(void)fireAtAntagonist {
  ...
  archerProjectile.targetNode = _world;

Action Chaining

One of my favorite features of Sprite Kit is the ability to chain actions together and attach them to an SKSpriteNode.

For example, when launching the spaceship, I want a few events to happen:

  1. I want the spaceship to follow a path that I calculate based on the position of all Antagonists in view, but I want this path to start on the left side of the screen and end on the right with a total time of 4 seconds.

  2. At the same time, when the spaceship is half way through its path (2 seconds in), I want all antagonists to be “killed” making it appear as if the spaceship has blown them up.

  3. After 4 seconds, when the spaceship has exited the screen, I want it to be removed from the world and released.

All of this can be achieved with the following:

1
2
3
4
5
6
  // Launch spaceship
  [_spaceship runAction:[SKAction group:@[[SKAction followPath:path asOffset:false orientToPath:YES duration:4],
                                          [SKAction sequence:@[[SKAction waitForDuration: 2],
                                                               [SKAction runBlock:^{[self killAllAntagonists];}],
                                                               [SKAction waitForDuration: 2],
                                                               [SKAction removeFromParent]]]]]];

Physics

Most of the code is self explanatory, with the update loop being used continuously to update the Hero and Antagonists. The physics callback of the render loop is not used here, but it is worth noting that I am using physics to prevent the characters from overlapping and to detect collisions:

1
2
3
4
5
6
7
8
9
- (void)createMoreAntagonists {
  ...
  // Set collision physics
  antagonist.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:50];
  antagonist.physicsBody.categoryBitMask = ColliderTypeAntagonist;
  antagonist.physicsBody.collisionBitMask = ColliderTypeProjectile | ColliderTypeAntagonist | ColliderTypeHero;
  antagonist.physicsBody.contactTestBitMask = ColliderTypeProjectile;
  ...
}

Defining the physical boundary of an object, setting the types of objects it can collide with, and deciding when to trigger collision events are all one liners. You can respond to collision events by implementing the didBeginContact method:

1
2
3
4
5
6
7
8
9
10
- (void)didBeginContact:(SKPhysicsContact *)contact {
  SKNode *bodyA = contact.bodyA.node;
  SKNode *bodyB = contact.bodyB.node;

  // Check for Hero projectile to Antagonist collision.  If found, blow up Antagonist
  if ([bodyA isKindOfClass:[SKEmitterNode class]] && [bodyB isKindOfClass:[OLAntagonist class]])
    [self blowUpAntagonist:(OLAntagonist *)bodyB];
  else if ([bodyB isKindOfClass:[SKEmitterNode class]] && [bodyA isKindOfClass:[OLAntagonist class]])
    [self blowUpAntagonist:(OLAntagonist *)bodyA];
}

End Result

Below is a screenshot of what to expect!

Alt text

Comments