GameplayKit is a brand new framework introduced in iOS 9 that makes implementing many common tasks in your game much easier.
There are several different parts of GameplayKit, such as support for pathfinding, randomization, state machines, rule systems, and more.
In this GameplayKit tutorial, you’re going to focus on two parts of GameplayKit: its Entity-Component system, and Agents, Goals, and Behaviors.
These parts of GameplayKit help you organize your code in a different way that allows for more clean organization and code reuse, especially as your games become larger and larger.
Let’s dive in!
Introducing MonsterWars
In this GameplayKit tutorial, you will be working on a simple game called MonsterWars. Download the starter project, open it in Xcode, and build and run to check it out. You’ll see something like the following:
Right now, this game is just a shell with the UI elements added, but no gameplay. Here’s how the game will eventually work:
- You’ll get money every so often, which you can see in the upper left. You can use it to buy units, by tapping the buttons at the bottom.
- There are three enemy types: Quirk (fast and cheap), Zap (ranged attackers), and Munch (slow but has AOE chomp).
- If your units destroy the enemy’s castle, you win!
Take a look at the code to see what’s there. Once you feel comfortable, read on to learn more about the main subject of this GameplayKit tutorial: its Entity-Component system.
Why Do We Need an Entity-Component System?
When you’re making a game, you need to create objects to represent the entities in your games – like monsters, the player, bullets, and so on.
When you first get started, you might think the most logical thing is to create a base class called “Game Object” that contains the common code. Then you can create subclasses for Monsters, and maybe subclasses of Monsters for specific types of monsters.
For simple games, this works quite fine and is quite easy to program. However, as your game get larger and more complex, this architecture begins to cause some problems in practice.
For example – imagine you decided to implement MonsterWars with the following oriented architecture:
Here the Player
class represents the castles on either side, and the Monster
class represents the monsters, and has subclasses for each type of monsters. Monsters can have either ranged or melee attacks.
But what happens if you want to make the castles shoot? All of that code is in the Monster
class instead of the Player
class. That means you have two options:
- Rework the object hierarchy. You could switch around the object hierarchy – maybe make Player a subclass of Monster.
- Move the code to a common base class. Alternatively you could move the ranged properties and code up to a common base class. The common base class of Monster and Player is GameObject.
Both of these options are equally bad. This kind of thing happens a lot as you make your game, and before long you have one massive spaghetti class filled with code. That’s with the Entity-Component system is designed to solve!
What is an Entity-Component System?
There are two parts of an entity component system:
- Components: You make small components for each type of thing your game objects can do. For example, you might make a melee attack component, a ranged attack component, a health component, a movement component, and so on.
- Entities: Since you moved all of your game logic to small independent components, now your entities can just be a collection of components configured a certain way. For example, you can add a batch of components together to make a zap monster.
The easiest way to see how this works is to try it out. So enough theory for now – it’s time to dive in!
Your First Component
To get started, let’s create a component to represent a sprite on the screen. To do this in GameplayKit, you make a subclass of GKComponent
.
To do this, select your MonsterWars group in the Project Navigator, right click, select New Group, and name the group Components.
Then right click the Components group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file SpriteComponent.swift and click Create.
At this point, your project navigator should look like this:
Open SpriteComponent.swift and replace the contents with the following:
// 1 import SpriteKit import GameplayKit // 2 class SpriteComponent: GKComponent { // 3 let node: SKSpriteNode // 4 init(texture: SKTexture) { node = SKSpriteNode(texture: texture, color: SKColor.whiteColor(), size: texture.size()) } } |
Let’s review this section by section:
- To use GameplayKit, you must import it just like you do for SpriteKit.
- To create a GameplayKit component, simply subclass
GKComponent
. - This component will keep track of a sprite, so you declare a property for one here.
- This is a simple initializer that initializes the sprite based on a texture you pass in.
Your First Entity
In GameplayKit, GKEntity
is the class that represents an entity. You can add components to it, like the one you just created.
In my opinion, it’s often helpful to create a subclass of GKEntity
for each type of object that you add to the game. In this game for example, there are five types of objects: castles, quirk monsters, zap monsters, munch monsters, and lasers.
Your GKEntity subclass should be very simple, and usually just an initializer that adds the types of components you typically need for that type of object. It’s best not to have much more code than that in the entity; leave that to the components!
Let’s create an entity the castles. To do this, select your MonsterWars group in the Project Navigator, right click, select New Group, and name the group Entities.
Then right click the Entities group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file Castle.swift and click Create.
At this point, your project navigator should look like this:
Open Castle.swift and replace the contents with the following:
import SpriteKit import GameplayKit // 1 class Castle: GKEntity { init(imageName: String) { super.init() // 2 let spriteComponent = SpriteComponent(texture: SKTexture(imageNamed: imageName)) addComponent(spriteComponent) } } |
There are just two things to point out here:
- As I mentioned earlier, it’s often convenient to subclass
GKEntity
for each type of object in your game. The alternative is to create a baseGKEntity
and dynamically add the types of components you need; but often you want to have a “cookie cutter” for a particular type of object. That is what this is! - At this point, you add just one component to the entity – the sprite component you just created. You’ll be adding more components to this entity soon!
Now that you’ve created a component and an entity, you’re almost ready to add it into the game. But first, we need to create a helper class to help manage our entities.
The Entity Manager
In this section, you’re going to create a helper class to manage the entities you add to your game. It will keep a list of all the entities in the game, and have some helper methods for things like adding and removing entities.
Right-click your Entities group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file EntityManager.swift and click Create.
Open EntityManager.swift and replace the contents with the following:
import Foundation import SpriteKit import GameplayKit class EntityManager { // 1 var entities = Set<GKEntity>() let scene: SKScene // 2 init(scene: SKScene) { self.scene = scene } // 3 func add(entity: GKEntity) { entities.insert(entity) if let spriteNode = entity.componentForClass(SpriteComponent.self)?.node { scene.addChild(spriteNode) } } // 4 func remove(entity: GKEntity) { if let spriteNode = entity.componentForClass(SpriteComponent.self)?.node { spriteNode.removeFromParent() } entities.remove(entity) } } |
Let’s review this section by section:
- This class will keep a reference to all entities in the game, along with the scene.
- This is a simple initializer that stores the scene in the property you created.
- This is a helper function that you will call when you want to add an entity to your game. It adds it to the list of entities, and then check to see if the entity has a
SpriteComponent
. If it does, it adds the sprite’s node to the scene. - This is a helper function that you will call when you want to remove an entity from your game. This does the opposite of the
add(_:)
method; if the entity has aSpriteComponent
, it removes the node from the scene, and it also removes the entity from the list of entities.
You’ll be adding more methods to this helper class in the future, but this is a good start for now. First let’s get something showing up on the screen!
Adding Your Castles
Open GameScene.swift and add this property to the bottom of your list of properties:
var entityManager: EntityManager! |
This is to store an instance of the helper class you just created.
Next add this code to the bottom of didMoveToView(_:)
:
// 1 entityManager = EntityManager(scene: self) // 2 let humanCastle = Castle(imageName: "castle1_atk") if let spriteComponent = humanCastle.componentForClass(SpriteComponent.self) { spriteComponent.node.position = CGPoint(x: spriteComponent.node.size.width/2, y: size.height/2) } entityManager.add(humanCastle) // 3 let aiCastle = Castle(imageName: "castle2_atk") if let spriteComponent = aiCastle.componentForClass(SpriteComponent.self) { spriteComponent.node.position = CGPoint(x: size.width - spriteComponent.node.size.width/2, y: size.height/2) } entityManager.add(aiCastle) |
Let’s review this section by section:
- Creates an instance of the
EntityManager
helper class you created in the previous section. - Creates an instance of your
Castle
entity you created earlier to represent the human player. After creating the castle it retrieves the sprite component and positions it on the left hand side of the screen. Finally, it adds it to the entity manager. - Similar code to set up the AI player’s castle.
That’s it! Build and run and you’ll see your castles in the game:
Your Second Component
When you develop games with an entity-component system, all data that you need for your game objects must be stored in some kind of component.
One thing that you’ll need for this game is to keep track of which team an object belongs to – team 1 or team 2. That information doesn’t belong on your Sprite Component; you might want to have an entity that doesn’t belong to either team for example. Therefore, let’s create a new component for that.
Right-click your Components group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file TeamComponent.swift and click Create.
Open TeamComponent.swift and replace the contents with the following:
import SpriteKit import GameplayKit // 1 enum Team: Int { case Team1 = 1 case Team2 = 2 static let allValues = [Team1, Team2] func oppositeTeam() -> Team { switch self { case .Team1: return .Team2 case .Team2: return .Team1 } } } // 2 class TeamComponent: GKComponent { let team: Team init(team: Team) { self.team = team super.init() } } |
This is a fairly simple file, so I’ll just point out two things:
- This is an enumeration to keep track of the two teams in this game – team 1 and team 2. It also has a helper method to return the opposite team, which will come in handy later.
- This is a very simple component that simply keeps track of the team for this entity.
Now that you have this new component, let’s update your castle entity to use it. Open Castle.swift and modify the initializer to take the team as a parameter:
init(imageName: String, team: Team) { |
Then add this line to the bottom of init(_:team:)
:
addComponent(TeamComponent(team: team)) |
This adds your new component to the castle entity. Finally, open GameScene.swift and replace the line that initializes humanCastle
with the following:
let humanCastle = Castle(imageName: "castle1_atk", team: .Team1) |
Similarly, replace the line that initializes aiCastle
with the following:
let aiCastle = Castle(imageName: "castle2_atk", team: .Team2) |
Build and run the game. You shouldn’t notice any changes, but you have now successfully associated a new set of data to your entity which will come in handy later.
Your Third Component
Another piece of data you need to keep track of is each player’s current coins. In this game, since there’s a single castle on each side, you’ll think of the castle as the “commander” for each player and it will be a good place to store this information.
So let’s create a CastleComponent
that will store this. Right-click your Components group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file CastleComponent.swift and click Create.
Open CastleComponent.swift and replace the contents with the following:
import SpriteKit import GameplayKit class CastleComponent: GKComponent { // 1 var coins = 0 var lastCoinDrop = NSTimeInterval(0) override init() { super.init() } // 2 override func updateWithDeltaTime(seconds: NSTimeInterval) { super.updateWithDeltaTime(seconds) // 3 let coinDropInterval = NSTimeInterval(0.5) let coinsPerInterval = 10 if (CACurrentMediaTime() - lastCoinDrop > coinDropInterval) { lastCoinDrop = CACurrentMediaTime() coins += coinsPerInterval } } } |
This component is a little different than the others, so let’s review this in more detail.
- This stores the number of coins in the castle, and the last time coins were earned.
- This method will be called on each frame of the game. Note this is not called by default; you have to do a little bit of setup to get this to happen, which you’ll do shortly.
- This is a bit of code to spawn coins periodically.
Like you did before, you need to add this new component to your Castle entity. To do this, open Castle.swift and add this to the bottom of init(_:team:)
:
addComponent(CastleComponent()) |
Next, you need to add the code I mentioned earlier to get your updateWithDeltaTime(_:)
method to be called. To do this, open EntityManager.swift and add this new property to the top of the class:
lazy var componentSystems: [GKComponentSystem] = { let castleSystem = GKComponentSystem(componentClass: CastleComponent.self) return [castleSystem] }() |
Think of GKComponentSystem
as a class that stores a collection of components. Here, you create a GKComponentSystem
to keep track of all of the CastleComponent
instances in your game.
You then put the GKComponentSystem
that stores component systems into an array. Right now it’s the only thing in the array, but you’ll be adding more to this later.
You’ll see the purpose behind doing this soon.
First, add this to the end of add(_:)
:
for componentSystem in componentSystems { componentSystem.addComponentWithEntity(entity) } |
Here whenever you add a new entity, you add it to each of the component systems in your array (right now, it only contains the castle component system). Don’t worry – if your entity does not contain a castle component, nothing will happen.
Next, add this to the end of remove(_:)
:
toRemove.insert(entity) |
Note that instead of removing the entity directly from the component system, you add it to a toRemove
set, so you can remove it later. This is because later in this tutorial you will end up in a situation where you want to remove an entity while you are enumerating the objects in a component system, and in Swift you cannot modify a collection while you are iterating it.
The compiler will complain that the toRemove
set does not exist, so add this property to the top of the class:
var toRemove = Set<GKEntity>() |
Finally, add this new method to the bottom of the class:
func update(deltaTime: CFTimeInterval) { // 1 for componentSystem in componentSystems { componentSystem.updateWithDeltaTime(deltaTime) } // 2 for curRemove in toRemove { for componentSystem in componentSystems { componentSystem.removeComponentWithEntity(curRemove) } } toRemove.removeAll() } |
Let’s review this section by section:
-
Here you loop through all the component systems in the array and call
updateWithDeltaTime(_:)
on each one. This causes each component system to callupdateWithDeltaTime(_:)
on each component in their system in turn.This actually demonstrates the whole purpose and benefit of using
GKComponentSystem
. The way this is set up, components are updated one system at a time. In games, it’s often convenient to have precise control over the ordering of the processing of each system (physics, rendering, etc). - Here you loop through anything in the
toRemove
array and remove those entities from the component systems.
There’s one last helper method to add to this file. Add this method to the bottom of the class:
func castleForTeam(team: Team) -> GKEntity? { for entity in entities { if let teamComponent = entity.componentForClass(TeamComponent.self), _ = entity.componentForClass(CastleComponent.self) { if teamComponent.team == team { return entity } } } return nil } |
This is a helper method that loops through all of the entities in the game, and looks to see any entities that have both a TeamComponent
and a CastleComponent
– which should be the two castles in the game. It then checks to see if the team matches the passed in parameter, and then returns that.
Basically, this is a handy method to get the castle for a particular team.
Let’s hook this up to the game scene now. Open GameScene.swift and add this code to the bottom of update(_:)
:
entityManager.update(deltaTime) if let human = entityManager.castleForTeam(.Team1), humanCastle = human.componentForClass(CastleComponent.self) { coin1Label.text = "\(humanCastle.coins)" } if let ai = entityManager.castleForTeam(.Team2), aiCastle = ai.componentForClass(CastleComponent.self) { coin2Label.text = "\(aiCastle.coins)" } |
This calls the update(_:)
method you just wrote on the entity manager. It then finds the castle (and castle component) for each team, and updates the labels with the current coin values from each component.
Build and run, and see the money begin to roll in!
Your Second Entity
Next, let’s modify the game so you can spawn Quirk monsters. Of course, you’ll need a new entity for that to keep the “cookie cutter” configuration in one centralized spot.
Right-click your Entities group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file Quirk.swift and click Create.
Open Quirk.swift and replace the contents with the following:
import SpriteKit import GameplayKit class Quirk: GKEntity { init(team: Team) { super.init() let texture = SKTexture(imageNamed: "quirk\(team.rawValue)") let spriteComponent = SpriteComponent(texture: texture) addComponent(spriteComponent) addComponent(TeamComponent(team: team)) } } |
This is very similar to how you set up the castle entity and you should understand everything in this class at this point.
Now it’s time to create an instance of the Quirk entity. Last time, you created the Castle entity directly in GameScene
, but this time you’ll move the code to spawn a Quick monster into EntityManager
. It will be nice to have this in a centralized spot later.
To do this, open EntityManager.swift and add this method to the bottom of the class:
func spawnQuirk(team: Team) { // 1 guard let teamEntity = castleForTeam(team), teamCastleComponent = teamEntity.componentForClass(CastleComponent.self), teamSpriteComponent = teamEntity.componentForClass(SpriteComponent.self) else { return } // 2 if teamCastleComponent.coins < costQuirk { return } teamCastleComponent.coins -= costQuirk scene.runAction(SoundManager.sharedInstance.soundSpawn) // 3 let monster = Quirk(team: team) if let spriteComponent = monster.componentForClass(SpriteComponent.self) { spriteComponent.node.position = CGPoint(x: teamSpriteComponent.node.position.x, y: CGFloat.random(min: scene.size.height * 0.25, max: scene.size.height * 0.75)) spriteComponent.node.zPosition = 2 } add(monster) } |
Let’s review this section by section:
- Monsters should be spawned near their team’s castle. To do this, you need the position of the castle’s sprite, so this is some code to look up that information in a dynamic way.
- This checks to see if there are enough coins to spawn the monster, and if so subtracts the appropriate coins and plays a sound.
- This is the code to create a Quick entity and position it near the castle (at a random y-value).
Finally, open GameScene.swift and add this to the end of quirkPressed()
:
entityManager.spawnQuirk(.Team1) |
Build and run, and tap away to spawn some monsters!
Agents, Goals, and Behaviors
So far, the quirk monsters are just sitting right there doing nothing. But that’s no good – this game needs movement!
Luckily, GameplayKit comes with a set of classes collectively known as “agents, goals, and behaviors” that makes moving objects in your game in complex ways super easy. Here’s how it works:
GKAgent2D
is a subclass ofGKComponent
that handles moving objects in your game. You can set different properties on it like max speed, acceleration, and so on, and theGKBehavior
to use.GKBehavior
is a class that contains a set ofGKGoals
, representing how you would like your objects to move.GKGoal
represents a movement goal you might have for your agents – for example to move toward another agent.
So basically, you configure these objects and add the GKAgent
component to your class, and GameplayKit will move everything for you from there!
GKAgent2D
doesn’t move your sprites directly, it just updates its own position appropriately. You need to write a bit of glue code to match up the sprite position with the GKAgent
position.Let’s try this out, starting by creating the behavior and goals. Right-click your Components group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file MoveBehavior.swift and click Create.
Open MoveBehavior.swift and replace the contents with the following:
import GameplayKit import SpriteKit // 1 class MoveBehavior: GKBehavior { init(targetSpeed: Float, seek: GKAgent, avoid: [GKAgent]) { super.init() // 2 if targetSpeed > 0 { // 3 setWeight(0.1, forGoal: GKGoal(toReachTargetSpeed: targetSpeed)) // 4 setWeight(0.5, forGoal: GKGoal(toSeekAgent: seek)) // 5 setWeight(1.0, forGoal: GKGoal(toAvoidAgents: avoid, maxPredictionTime: 1.0)) } } } |
There’s a lot of new stuff here, so let’s review this section by section:
- You create a
GKBehavior
subclass here so you can easily configure a set of movement goals. - If the speed is less than 0, don’t set any goals as the agent should not move.
- To add a goal to your behavior, you use the
setWeight(_:forGoal:)
method. This allows you to specify a goal, along with a weight of how important it is – larger weight values take priority. In this instance, you set a low priority goal for the agent to reach the target speed. - Here you set a medium priority goal for the agent to move toward another agent. You will use this to make your monsters move toward the closest enemy.
- Here you set a high priority goal to avoid colliding with a group of other agents. You will use this to make your monsters stay away from their allies so they are nicely spread out.
Now that you’ve created your behavior and goals, you can set up your agent. Right-click your Components group, select New File.., select the iOS\Source\Swift File template, and click Next. Name the new file MoveComponent.swift and click Create.
Open MoveComponent.swift and replace the contents with the following:
import SpriteKit import GameplayKit // 1 class MoveComponent : GKAgent2D, GKAgentDelegate { // 2 let entityManager: EntityManager // 3 init(maxSpeed: Float, maxAcceleration: Float, radius: Float, entityManager: EntityManager) { self.entityManager = entityManager super.init() delegate = self self.maxSpeed = maxSpeed self.maxAcceleration = maxAcceleration self.radius = radius print(self.mass) self.mass = 0.01 } // 4 func agentWillUpdate(agent: GKAgent) { guard let spriteComponent = entity?.componentForClass(SpriteComponent.self) else { return } position = float2(spriteComponent.node.position) } // 5 func agentDidUpdate(agent: GKAgent) { guard let spriteComponent = entity?.componentForClass(SpriteComponent.self) else { return } spriteComponent.node.position = CGPoint(position) } } |
There’s lots of new stuff here as well, so let’s review this section by section:
- Remember that
GKAgent2D
is a subclass ofGKComponent
. You subclass it here so customize its functionality. Also, you implementGKAgentDelegate
– this is how you’ll match up the position of the sprite with the agent’s position. - You’ll need a reference to the
entityManger
so you can access the other entities in the game. For example, you need to know about your closest enemy (so you can seek to it) and your full list of allies (so you can spread apart from them). GKAgent2D
has various properties like max speed, acceleration, and so on. Here you configure them based on passed in parameters. You also set this class as its own delegate, and make the mass very small so objects respond to direction changes more easily.- Before the agent updates its position, you set the position of the agent to the sprite component’s position. This is so that agents will be positioned in the correct spot to start. Note there’s some funky conversions going on here – GameplayKit uses
float2
instead ofCGPoint
, gah! - Similarly, after the agent updates its position
agentDidUpdate(_:)
is called. You set the sprite’s position to match the agent’s position.
You still have a bit more to do in this file, but first you need to add some helper methods. Start by opening EntityManager.swift and add these new methods:
func entitiesForTeam(team: Team) -> [GKEntity] { return entities.flatMap{ entity in if let teamComponent = entity.componentForClass(TeamComponent.self) { if teamComponent.team == team { return entity } } return nil } } func moveComponentsForTeam(team: Team) -> [MoveComponent] { let entities = entitiesForTeam(team) var moveComponents = [MoveComponent]() for entity in entities { if let moveComponent = entity.componentForClass(MoveComponent.self) { moveComponents.append(moveComponent) } } return moveComponents } |
entitiesForTeam(_:)
returns all entities for a particular team, and moveComponentsForTeam(_:)
returns all move components for a particular team. You’ll need these shortly.
Switch back to MoveComponent.swift and add this new method:
func closestMoveComponentForTeam(team: Team) -> GKAgent2D? { var closestMoveComponent: MoveComponent? = nil var closestDistance = CGFloat(0) let enemyMoveComponents = entityManager.moveComponentsForTeam(team) for enemyMoveComponent in enemyMoveComponents { let distance = (CGPoint(enemyMoveComponent.position) - CGPoint(position)).length() if closestMoveComponent == nil || distance < closestDistance { closestMoveComponent = enemyMoveComponent closestDistance = distance } } return closestMoveComponent } |
This is some code to find the closest move component on a particular team from the current move component. You will use this to find the closest enemy now.
Add this new method to the bottom of the class:
override func updateWithDeltaTime(seconds: NSTimeInterval) { super.updateWithDeltaTime(seconds) // 1 guard let entity = entity, let teamComponent = entity.componentForClass(TeamComponent.self) else { return } // 2 guard let enemyMoveComponent = closestMoveComponentForTeam(teamComponent.team.oppositeTeam()) else { return } // 3 let alliedMoveComponents = entityManager.moveComponentsForTeam(teamComponent.team) // 4 behavior = MoveBehavior(targetSpeed: maxSpeed, seek: enemyMoveComponent, avoid: alliedMoveComponents) } |
This is the update loop that puts it all together.
- Here you find the team component for the current entity.
- Here you use the helper method you wrote to find the closest enemy.
- Here you use the helper method you wrote to find all your allies’s move components.
- Finally, you reset the behavior with the updated values.
Almost done; just a few cleanup items to do. Open EntityManager.swift and update the line that sets up the componentSystems
property as follows:
lazy var componentSystems: [GKComponentSystem] = { let castleSystem = GKComponentSystem(componentClass: CastleComponent.self) let moveSystem = GKComponentSystem(componentClass: MoveComponent.self) return [castleSystem, moveSystem] }() |
Remember, this is necessary so that your updateWithDeltaTime(_:)
method gets called on your new MoveComponent
.
Next open Quirk.swift and modify your initializer to take the entityManager
as a parameter:
init(team: Team, entityManager: EntityManager) { |
Then add this to the bottom of init(_:entityManager:)
:
addComponent(MoveComponent(maxSpeed: 150, maxAcceleration: 5, radius: Float(texture.size().width * 0.3), entityManager: entityManager)) |
This creates your move component with some values that work well for the quick Quirk monster.
You need a move component for the castle too – this way they can be one of the agents considered for the “closest possible enemy”. To do this, open Castle.swift and modify your initializer to take the entityManager
as a parameter:
init(imageName: String, team: Team, entityManager: EntityManager) { |
Then add this to the bottom of init(_:team:entityManager:)
:
addComponent(MoveComponent(maxSpeed: 0, maxAcceleration: 0, radius: Float(spriteComponent.node.size.width / 2), entityManager: entityManager)) |
Finally, move to EntityManager.swift and inside spawnQuirk(_:)
, modify the line that creates the Quirk
instance as follows:
let monster = Quirk(team: team, entityManager: self) |
Also open GameScene.swift and modify the line in didMoveToView(_:)
that creates the humanCastle
:
let humanCastle = Castle(imageName: "castle1_atk", team: .Team1, entityManager: entityManager) |
And similarly for aiCastle
:
let aiCastle = Castle(imageName: "castle2_atk", team: .Team2, entityManager: entityManager) |
Build and run, and enjoy your moving monsters:
Congratulations! At this point you have a good understanding of how to use the new Entity-Component system in GameplayKit, along with using Agents, Goals, and Behaviors for movement.
Where to Go From Here?
Here is the finished example project from this GameplayKit tutorial.
At this point, you can repeat the process described here to add more components to your game – for example a component to deal melee damage, a component to fire lasers, a component to display a health bar, etc.
I thought about extending the tutorial to show how to do all this, but this tutorial has gone on long enough. So instead, I created a project for the complete game that you can download and take a look at for reference.
If you want to learn more about GameplayKit, you should check out our book 2D iOS & tvOS Games by Tutorials:
In this book we’ll teach you everything you need to know to make great games for iOS & tvOS – from physics, to tile maps, to particle systems, and even how to make your games “juicy” with polish and special effects.
I hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!
The post GameplayKit Tutorial: Entity-Component System, Agents, Goals, and Behaviors appeared first on Ray Wenderlich.