A Quick Tour of the Project
Adventure is a single-level game built entirely using the Sprite Kit framework. Rather than having to maintain multiple projects, we created Adventure using a single Xcode project that targets both iOS and OS X, so that all of the game-specific classes are shared between both platforms. In addition to the shared game code, each target has platform-specific resources (storyboard and xib files) and classes responsible for setting up a basic app container ready to display the Adventure scene.
First, play the game to get an idea of how everything fits together. Next, read the first section in this chapter to learn the terminology we use for the characters you encounter. When you’re ready to delve into the project to see how everything works, continue this quick tour, which describes the important parts of the Xcode project and explains the decisions that went into building the class hierarchy behind Adventure.
The Characters in Adventure
When you play Adventure, you have a choice of two different hero characters, warrior or archer, as shown in Figure 2-1. Both hero characters fire projectiles—the warrior throws a hammer, the archer shoots an arrow.
As you walk around the maze, you run into goblins like the one shown in Figure 2-2. A goblin will automatically seek out and chase your hero character until you kill it with a projectile.
Goblins are spawned from caves at various fixed points around the maze, each with two burning torches outside, as shown in Figure 2-3. If you destroy a cave by firing repeatedly at it, the cave stops spawning goblins.
Finally, when you reach the end of the level, you encounter the boss character, shown in Figure 2-4. The boss doesn’t move from its location until your hero is nearby; then it chases and attacks you. As you might expect, getting hit by the boss decreases your health. Killing the boss finishes the level and ends the game.
Although they’re not technically characters, Adventure also features trees all around the level, as shown in Figure 2-5.
Trees are constructed from multiple images layered on top of each other. When you walk around the level and the camera moves to follow your character, you’ll notice that these image layers move at different rates, resulting in a parallax effect that gives the world a three dimensional quality.
Some of the trees emit individual leaf nodes to simulate falling leaves, adding to the sense of movement and bringing the world to life. Trees also fade out automatically when a hero is underneath them so that you can see where you’re going.
The goblin caves are also built from multiple parallax image layers, which again move at different rates when the camera moves.
The Adventure Class Hierarchy
When creating the classes for Adventure, we tried to strike a balance between leveraging inheritance to minimize code duplication while keeping the hierarchy relatively simple so you can quickly figure out how everything fits together. We deliberately factored shared or generic behavior into abstract classes, so that it’s easy to extend the game with your own additional functionality in the future.
Figure 2-6 shows the hierarchy of all the shared game classes. The blue boxes indicate abstract classes designed to be subclassed; the tan boxes indicate the concrete classes that we instantiate directly at runtime.
There’s only one level in Adventure, which translates into a single scene in Sprite Kit terms, but we’ve split the implementation across two classes. The
APAMultiplayerLayeredCharacterScene class is a generic subclass of
SKScene and implements behavior that would be needed by any scene in the game. If you wanted to extend Adventure with another level, you’d create a subclass of this abstract scene class.
APAMultiplayerLayeredCharacterScene keeps track of the players in the game, and handles user input to control the heroes. It also builds a basic tree of empty nodes (the node tree) to represent the different layers in the game. These layers dictate the drawing order of the sprites. For example, ground tiles are always drawn underneath the characters, and foliage always appears above everything else. For more information about node trees, see A Node Tree Defines What Appears in a Scene in SpriteKit Programming Guide.
The concrete subclass of
APAAdventureScene class—is responsible for creating level-specific content. In addition to preloading the necessary images, emitters, and animation frames, this class is responsible for determining the initial placement of all sprites within the level, including the ground tiles, collision walls, characters, and the static elements like caves and trees; this process is described in detail in Building the World.
APAAdventureScene is also the physics delegate for collisions within the scene, as described in Handling Collisions.
Each time through the update loop (called once per frame)
APAAdventureScene updates all the characters in the scene, as well as the layer offsets of parallax sprites. To keep memory and CPU usage to a minimum, particle emitters are disabled and removed from the scene if they are too far away from the default player’s hero to be seen, and added again if the hero gets near enough. The update loop is discussed in detail in Keeping Up to Date.
All of the character and tree classes in Adventure are descended ultimately from
APAParallaxSprite. This class inherits from
SKSpriteNode, but adds optional parallax behavior used by trees and goblin caves.
As the name suggests, the
APATree class represents a tree in the level. This class adds a single method,
updateAlphaWithScene:, called by the scene once per frame. We use this method to update the sprite’s
alpha value, making the tree almost transparent whenever a hero is underneath it.
All other sprites in Adventure inherit from the
APACharacter class, which is responsible for all basic character movement and animation. This class also adds an extra
shadowBlob node to the scene to represent a character’s shadow, and controls a character’s health, triggering the death sequence if health drops to zero.
For more information on movement, see Controlling the Characters; for more information on animation, see Animating the Characters.
APAHeroCharacter class adds the basic behavior common to both warrior and archer hero characters. This behavior includes configuring particle emitters to show damage, alerting the scene object when the hero is killed, and firing a projectile when you trigger the fire action. The
APAHeroCharacter class has two subclasses that are responsible for providing a suitable projectile—
APAWarrior, which throws a hammer, and
APAArcher, which shoots an arrow.
The enemies in the scene (goblins, goblin caves, and the level boss), all inherit from the
APAEnemyCharacter class. This class extends the
APACharacter class by adding a single
intelligence property for an
APAArtificialIntelligence object that gives these characters their artificial intelligence (AI).
APAArtificialIntelligence base class simply keeps track of the relevant enemy, as well as a
target property that’s used by AI subclasses to refer to the current hero target.
APAEnemyCharacter updates the AI object each time the character is updated (once per frame).
APABoss, initialize the
intelligence property with an instance of the
APAChaseAI class, which tracks the position of the closest hero to determine whether it’s within chase or attack range. Last but not least, the
APACave class uses the
APASpawnAI class to determine how often goblin caves should generate new goblins, again based on proximity to the closest hero character.
The Xcode Project
The classes and resources in the Xcode project are divided across multiple groups to make it easy to navigate through all the different files. All of the game-specific classes are in the Adventure - Shared group, which in turn contains groups with classes related to the Scene, the Sprites, AI, and finally some general graphics Utilities used throughout the game.
#pragma mark directives in each class to group methods with related behavior. This means that you can get a quick outline of a class implementation using the jump bar, as shown in Figure 2-7.
When you want to test Adventure on a different device, you can switch between the two targets—iOS and OS X—by using the Scheme drop-down menu shown in Figure 2-1.
OS X Classes and Resources
The files for the OS X version of the game are in the Adventure - OS X group. The
MainMenu.xib file contains a single window, which contains an
SKView instance, an image view for the game logo, a progress indicator shown when loading assets, and two buttons for choosing between the archer and warrior heroes (these have their alpha value set to 0 in the xib file so that they are invisible until needed).
APAAppDelegateOSX class handles the logic necessary to load the scene resources, hide and show the UI elements, and add the
APAAdventureScene for display by the
SKView as described in Building the World.
The OS X entitlements are set to enable App Sandbox for the game and allow access to USB devices to support external game controllers.
iOS Classes and Resources
The files specific to the iOS target are in the Adventure - iOS group. The app’s user interface, built from a storyboard file, contains a single view controller, which is an instance of the
APAViewController class. The view controller’s content view contains the same items as the OS X window: an
SKView object, an image view, an activity indicator, and the two buttons.
APAViewController class is responsible for loading the scene resources and dealing with the UI elements, as described in Building the World.
Art and Special Effects Resources
Adventure contains a large number of images, sounds, texture atlases, and particle emitters. These assets are contained within the Assets group, and are shared between both OS X and iOS versions of the game. If you expand this group, you’ll find the files are organized into subgroups by type.
Adventure uses texture atlases for all textures, including character animation frames, the tiles that make up the background, and the images used for elements like the trees, caves, and projectiles. We placed these images inside
.atlas folders; at compilation time, Xcode builds each folder of images into a texture atlas.
When built, a texture atlas contains at least one image file, which has multiple subimages inside, as shown in Figure 2-9. A corresponding
.plist file describes the offset, size, and rotation of each element within this file so that Sprite Kit can draw just the relevant texture at runtime.
It’s much more efficient to use a texture atlas than to use multiple individual files, because Sprite Kit can draw objects that share the same atlas in a single rendering call to the GPU. For example, the trees and caves all use the textures from the Environment atlas, which means they can all be drawn at the same time. If the textures were in separate files, Sprite Kit would have to make one rendering call to the GPU per group of objects using each texture.
Xcode provides a particle emitter editor, which lets you create and adjust Sprite Kit particle emitters using a visual interface, as shown in Figure 2-10.
We created many of Adventure’s particle emitters using this tool. At runtime, each
.sks file is unarchived into an
SKEmitterNode, in much the same way as an Interface Builder storyboard is unarchived into a set of objects.
Particle emitters are one of the most performance-intensive elements in the game, so it’s important to decrease, as much as possible, the birthrate and number of nodes without losing the desired effect. The visual editor provides an easy way to adjust these settings, and shows a realtime preview of the results.