An Introduction to GameplayKit: Part 1

Introduction

Alongside all of the new features and frameworks in iOS 9 and OS X El Capitan, with this year's releases Apple also created an entirely new framework catered to game developers, GameplayKit. With existing graphics APIs (SpriteKit, SceneKit, and Metal) making it easy to create great looking games on iOS and OS X, Apple has now released GameplayKit to make it easy to create games that play well. This new framework contains many classes and functionalities that can be used to easily add complex logic to your games.

In this first tutorial, I will teach you about two major aspects of the GameplayKt framework:

  • entities and components
  • state machines

Prerequisites

This tutorial requires that you are running Xcode 7 on OS X Yosemite or later. While not required, it is recommended that you have a physical device running iOS 9 as you will get much better performance when testing the SpriteKit-based game used in this tutorial.

1. Getting Started

You will firstly need to download the starter project for this series of tutorials from GitHub. Once you've done this, open the project in Xcode and run it on either the iOS Simulator or your device.

You will see that it is a very basic game in which you control a blue dot and navigate around the map. When you collide with a red dot, you lose two points. When you collide with a green dot, you gain one point. When you collide with a yellow dot, your own dot becomes frozen for a couple of seconds.

Initial Game

At this stage, it is a very basic game, but throughout the course of this tutorial series, and with the help of GameplayKit, we are going to add in a lot more functionality and gameplay elements.

2. Entities and Components

The first major aspect of the new GameplayKit framework is a code-structuring concept based on entities and components. It works by allowing you, the developer, to write common code that is used by many different object types in your game while keeping it well organized and manageable. The concept of entities and components is meant to eliminate the common inheritance-based approach to share common functionality between object types. The easiest way to understand this concept is with some examples so imagine the following scenario:

You are building a tower defense game with three main types of towers, Fire, Ice, and Heal. The three types of towers would share some common data, such as health, size, and strength. Your Fire and Ice towers need to be able to target incoming enemies to shoot at whereas your Heal tower does not. All that your Heal tower needs to do is repair your other towers within a certain radius as they receive damage.

With this basic game model in mind, let's see how your code could be organized using an inheritance structure:

  • A parent Tower class containing common data such as health, size, and strength.
  • FireTowerIceTower, and HealTower classes that would inherit from the Tower class.
  • In the HealTower class, you have the logic responsible for healing your other towers within a certain radius.

So far, this structure is okay, but a problem now arises when you need to implement the Fire and Ice towers' targeting ability. Do you just copy and paste the same code into both of your FireTower and IceTower classes? If you need to make any changes, you will then need to change your code in more than one place, which is tedious and error-prone. On top of this, what happens if you want to add in a new tower type that also needs this targeting functionality. Do you copy and paste it a third time?

The best way seems to be to put this targeting logic in the parent Tower class. This would allow you to just have one copy of the code that only needs to be edited in one place. Adding this code here, however, would make the Tower class a lot larger and more complicated than it needs to be when not all of its subclasses need that functionality. If you also wanted to add more shared functionality between your tower types, your Tower class would gradually become larger and larger, which would make it hard to work with.

As you can see, while it is possible to create a game model based on inheritance, it can very quickly and easily become unorganized and difficult to manage.

Now, let's see how this same game model could be structured using entities and components:

  • We would create FireTowerIceTower, and HealTower entities. More entities could be created for any more tower types you want to add later on.
  • We'd also create a BasicTower component that would contain health, size, strength, etc.
  • To handle the healing of your towers within a certain radius, we'd add a Healing component.
  • Targeting component would contain the code needed to target incoming enemies.

Using GameplayKit and this structure you would then have a unique entity type for each tower type in your game. To each individual entity, you can add the desired components that you want. For example:

  • Your FireTower and IceTower entities would each have a BasicTower and Targeting component linked to it.
  • Your HealTower entity would have both a BasicTower and a Healing component.

As you can see, by using an entity- and component-based structure, your game model is now a lot simpler and more versatile. Your targeting logic only needs to be written once and only links to the entities that it needs to. Likewise, your basic tower data can still be easily shared between all of your towers without bulking up all of your other common functionality.

Another great thing about this entity- and component-based structure is that components can be added to and removed from entities whenever you want. For example, if you wanted your Heal towers to be disabled under certain conditions, you could simply remove the Healing component from your entity until the right conditions are met. Likewise, if you wanted one of your Fire towers to gain a temporary healing ability, you could just add a Healing component to your FireTower entity for a specific amount of time.

Now that you are comfortable with the concept of an entity- and component-based game model structure, let's create one within our own game. In Xcode's File Inspector, find the Entities folder within your project. For convenience, there are already three entity classes for you, but you are now going to create a new entity from scratch.

Choose File > New > File... or press Command-N to create a new class. Make sure to select the Cocoa Touch Class template from the iOS > Source section. Name the class Player and make it a subclass of GKEntity.

Creating a player entity

You will see that immediately upon opening your new file Xcode will display an error. To fix this, add the following import statement below the existing import UIKit statement:

Go back to PlayerNode.swift and add the following property to the PlayerNode class:

Next, navigate to the Components folder in your Xcode project and create a new class just as you did before. This time, name the class FlashingComponent and make it a subclass of GKComponent as shown below.

Creating a flashing component

The component you've just created is going to handle the visual flashing of our blue dot when it is hit by a red dot and is in its invulnerable state. Replace the contents of FlashingComponent.swift with the following:

The implementation simply keeps a reference to an SKNode object and repeats fade in and fade out actions in sequence as long as the component is active.

Go back to GameScene.swift and add the following code somewhere within the didMoveToView(_:) method:

We create a FlashingComponent object and set it up to perform its flashing on the player's dot. The last line then adds the component to the entity to keep it active and executing.

Build and run your app. You will now see that your blue dot slowly fades in and out repeatedly.

Fading blue dot

Before moving on, delete the code that you just added from the didMoveToView(_:) method. Later, you will be adding this code back but only when your blue dot enters its invulnerable state.

3. State Machines

In GameplayKit, state machines provide a way for you to easily identify and perform tasks based on the current state of a particular object. Drawing from the earlier tower defense example, some possible states for each tower could include Active, Disabled, and Destroyed. One major advantage of state machines is that you can specify which states another state can move to. With the three example states mentioned above, using a state machine, you could set the state machine up so that:

  • a tower can become Disabled when Active and vice versa
  • a tower can become Destroyed when either Active or Disabled
  • a tower can not become Active or Disabled once it has been Destroyed

In the game for this tutorial, we are going to keep it very simple and only have a normal and invulnerable state.

In your project's State Machine folder, create two new classes. Name them NormalState and InvulnerableState respectively, with both being a subclass of the GKState class.

Creating an invulnerable state class

Replace the contents of NormalState.swift with the following:

The NormalState class contains the following:

  • It implements a simple initializer to keep a reference to the current player's node.
  • It has an implementation for the isValidNextState(_:) method. This method's implementation returns a boolean value, indicating whether or not the current state class can move to the state class provided by the method parameter.
  • The class also includes an implementation for the didEnterWithPreviousState(_:) callback method. In the method's implementation, we check if the previous state was the InvulnerableState state and, if true, remove the flashing component from the player's entity.

Now open InvulnerableState.swift and replace its contents with the following:

The InvulnerableState class is very similar to the NormalState class. The main difference is that upon entering this state you add the flashing component to the player's entity rather than removing it.

Now that your state classes are both complete, open PlayerNode.swift again and add the following lines to the PlayerNode class:

This code snippet adds a new property to the PlayerNode class and implements a convenience method to go back to the normal state.

Now open GameScene.swift and, at the end of the didMoveToView(_:) method, add the following two lines:

In these two lines of code, we create a new GKStateMachine with the two states and tell it to enter the NormalState.

Finally, replace the implementation of the handleContactWithNode(_:) method of the GameScene class with the following implementation:

When the player's blue dot collides with a red enemy dot, the player will enter the InvulnerableState state for five seconds and then revert back to the NormalState state. We also check what the current state of the player is and only perform any enemy-related logic if it is the NormalState state.

Build and run your app one last time, and move around the map until you find a red dot. When you collide with the red dot, you will see that your blue dot enters its invulnerable state and flashes for five seconds.

Entering the invulnerable state

Conclusion

In this tutorial, I introduced you to two of the major aspects of the GameplayKit framework, entities and components, and state machines. I showed you how you can use entities and components to structure your game model and keep everything organized. Using components is a very easy way to share functionality between objects in your games.

I also showed you the basics of state machines, including how you can specify which states a particular state can transition to as well as executing code when a particular state is entered.

Stay tuned for the second part of this series where we are going to take this game to another level by adding in some artificial intelligence, better known as AI. The AI will enable enemy dots to target the player and find the best path to reach the player.

As always, if you have any comments or questions, leave them in the comments below.

Tags:

Comments

Related Articles