Create Your First Component

The first component you’ll make is a health component. The health component is a way to track the number of health points an entity has remaining.

To make a component using the GameplayKit framework, you need to subclass the GKComponent superclass (which is another way of referring to the base class or parent class).

Each component represents a single “trait” or “ability” that you can add to an entity, and an entity represents one of your game objects. The nice thing about components is that you can reuse and add them to any type of entity. For example, you can create a single health component that both your player and monsters can share.

Creating the Health Component

In the Project Navigator, below the Extensions group, create a new group (N) and name it Components.

Inside the new Components group, create a new file (N) using the iOS Swift File template. Name the new file HealthComponent.swift, and replace its contents with the following code:

 import​ ​SpriteKit
 import​ ​GameplayKit
 
 class​ ​HealthComponent​: ​GKComponent​ {
 
 override​ ​func​ ​didAddToEntity​() {
 
  }
 
 override​ ​func​ ​willRemoveFromEntity​() {
 
  }
 
 override​ ​func​ ​update​(deltaTime seconds: ​TimeInterval​) {
 
  }
 
 override​ ​class​ ​var​ supportsSecureCoding: ​Bool​ {
 true
  }
 }

This code represents a standard implementation of a GameplayKit component. Let’s have a closer look at your new health component.

The first two methods, didAddToEntity() and willRemoveFromEntity(), are useful for when you need to take some kind of action when adding or removing a component from an entity; this can include things like setting up the health component or altering the color of the player sprite as she gains or loses a power-up.

The third method, update(deltaTime:), is useful for when you need to make periodic updates to your entities and components. This method works in conjunction with the scene’s update method and is useful for when you want to update certain entities and/or components with specific game logic, such as simulating monster AI.

You can use two types of update patterns with entities and components: per-entity and per-component.

With per-entity updates, the entity is responsible for updating its components. You accomplish these updates by looping through all of the entities within a scene, calling the update(deltaTime:) method on each one. This, in turn, calls the update(deltaTime:) method on the attached components:

 // Update entities
 for​ entity ​in​ ​self​.entities {
  entity.​update​(deltaTime: dt)
 }

With per-component updates, you set up a component system using a GKComponentSystem object. The component system is then responsible for handling the updates. When you use a components system, you can target specific components and then update them in a deliberate and deterministic order rather than looping through the entities within a scene:

 // Set up the component system
 let​ system = ​GKComponentSystem​(componentClass: ​MonsterComponent​.​self​)
 
 // Set up the monster entity
 let​ monster = ​MonsterEntity​()
 
 // Either explicitly add a type of component to the system
 monster.​addComponent​(​GeneratorComponent​())
 
 // Or find a specific component inside of an entity, and add it that way
 system.​addComponent​(foundIn: monster)
 
 // Update the system
 system.​update​(deltaTime: dt)

Val’s Revenge uses a per-entity design pattern when updating the components.

The remaining code, which isn’t actually part of the GKComponent class, is a static Boolean value indicating whether or not this component supports secure coding. Because you’ll be using your components with archived scene files, you need to override the supportsSecureCoding type property and return a value of true. Failure to override this property will cause loading problems with your scenes, typically resulting in an empty gray screen.

When you first add a health component to an entity, you’ll need to set up and add the health meter reference node. The health meter reference node is one of the resource files you added in Add the Resources. For reference (no pun intended), the HealthMeter.sks file looks like this:

images/BuildingGamesWithEntitiesAndComponents/health-component.png

Adding the Health Meter Reference Node

With the HealthComponent.swift file open in the Source Editor, add the following code to the didAddToEntity() method:

 guard​ ​let​ node = entity?.​component​(ofType: ​GKSKNodeComponent​.​self​)?.node
 else​ {
 return
 }
 
 if​ ​let​ healthMeter = ​SKReferenceNode​(fileNamed: ​"HealthMeter"​) {
  healthMeter.position = ​CGPoint​(x: 0, y: 100)
  node.​addChild​(healthMeter)
 }

This code first checks that a SpriteKit node is present before attempting to add a child reference node to itself.

Save the HealthComponent.swift file, and then in the Project Navigator, select the GameScene.sks file to open it in the Scene Editor.

To prevent any accidental node movement, lock the Grass Tile Map and the Dungeon Tile Map.

In the Scene Graph View, select the player node. Click the button to show the inspectors and then switch to the Component Inspector, which looks like this:

images/BuildingGamesWithEntitiesAndComponents/component-inspector.png

The Component Inspector is where you can add and remove your components. When you add components to a node using the Scene Editor, Xcode automatically creates the GKSKNodeComponent object for you. This object manages the relationship between the node and the entity property of the node to which the component is attached.

Click the + button to reveal the list of available components. For now, there’s only one, the HealthComponent. Select the HealthComponent item from the list to add it to the player node, like this:

images/BuildingGamesWithEntitiesAndComponents/health-component-added.png

Build and run the project, you’ll see Val now has a health meter above her head:

images/BuildingGamesWithEntitiesAndComponents/player-health-no-settings.png

This is a great start, but there’s a lot more you need to do, such as configuring the health meter’s initial settings.

Configuring the Health Meter

You’ll eventually add monsters to your game, and those monsters will also require a health meter, so it makes sense to build the health component with shareability and flexibility in mind.

In the Project Navigator, select the HealthComponent.swift file to, once again, open it in the Source Editor.

Below (and inside) the class declaration, add the following code:

 @GKInspectable​ ​var​ currentHealth: ​Int​ = 3
 @GKInspectable​ ​var​ maxHealth: ​Int​ = 3

The @GKInspectable is an attribute you can use to expose a property within the Scene Editor. Here, you’re exposing two properties you can use to set the current health and maximum health for any node you attach this health component to, which gives you more flexibility with regard to the amount of health you can set up per entity.

Below the properties you just added, add the following two lines of code:

 private​ ​let​ healthFull = ​SKTexture​(imageNamed: ​"health_full"​)
 private​ ​let​ healthEmpty = ​SKTexture​(imageNamed: ​"health_empty"​)

Here, you’re setting up the two textures you’ll use to represent when a health bar is full versus when it’s empty.

Next, you will need a way to determine which texture to use depending on how much health an entity has remaining. For that, you can use a small helper method. Add the following new method below the didAddToEntity() method:

 func​ ​setupBar​(at num: ​Int​, tint: ​SKColor​? = ​nil​) {
 guard​ ​let​ node = entity?.​component​(ofType: ​GKSKNodeComponent​.​self​)?.node
 else​ {
 return
  }
 
 if​ ​let​ health = node.​childNode​(withName: ​".//health_​​(​num​)​​"​)
 as?​ ​SKSpriteNode​ {
 if​ currentHealth >= num {
  health.texture = healthFull
 if​ ​let​ tint = tint {
  health.color = tint
  health.colorBlendFactor = 1.0
  }
  } ​else​ {
  health.texture = healthEmpty
  health.colorBlendFactor = 0.0
  }
  }
 }

Once again, you’re verifying that a node exists, and then you’re using that node to find the corresponding health bar node within the reference node. You’re also making it possible to set a tint color for the health bar, providing even more flexibility.

Finally, add one more new method to the health meter component, placing it above the setupBar(at:tint:) method:

 func​ ​updateHealth​(_ value: ​Int​, forNode node: ​SKNode​?) {
  currentHealth += value
 
 if​ currentHealth > maxHealth {
  currentHealth = maxHealth
  }
 
 if​ ​let​ _ = node ​as?​ ​Player​ {
 for​ barNum ​in​ 1...maxHealth {
 setupBar​(at: barNum, tint: .cyan)
  }
  } ​else​ {
 for​ barNum ​in​ 1...maxHealth {
 setupBar​(at: barNum)
  }
  }
 }

You’ll use this function to update the health meter as needed, for example, when setting up the entity’s initial health points or when the health points decrease or increase during gameplay. Notice that you’re passing in .cyan as the tint color when the node is a Player node.

Now that you have this handy helper method and update routine in place, it’s time to put them to use. In the didAddToEntity() method, below the line that reads node.addChild(healthMeter), add the following line of code:

 updateHealth​(0, forNode: node)

For reference, the updated didAddToEntity() method looks like this:

 override​ ​func​ ​didAddToEntity​() {
 guard​ ​let​ node = entity?.​component​(ofType: ​GKSKNodeComponent​.​self​)?.node
 else​ {
 return
  }
 
 if​ ​let​ healthMeter = ​SKReferenceNode​(fileNamed: ​"HealthMeter"​) {
  healthMeter.position = ​CGPoint​(x: 0, y: 100)
  node.​addChild​(healthMeter)
 
 updateHealth​(0, forNode: node)
  }
 }

Excellent, the health bar will now update once this component is added to an entity. Save this file and then switch back to the GameScene.sks file.

Once again, in the Scene Graph View, select the player node and this time, notice that in the Components Inspector, you now have some new options:

images/BuildingGamesWithEntitiesAndComponents/player-health-with-settings.png

What you’re seeing are the inspectable properties you set up earlier in the HealthComponent class. For the player node, enter the number 3 for both the currentHealth and maxHealth properties. (You could also leave the maxHealth and currentHealth fields blank, letting them use their default values of 3; however, it’s generally best to be explicit when setting the component properties.)

Updating Components in the Scene Editor

images/aside-icons/warning.png

Working with components and their properties inside the Scene Editor is quite powerful; however, the integration between GameplayKit and the Scene Editor has a few glitches, especially when the underlying component changes or the value of one of its properties changes.

Should you find that components and nodes aren’t behaving as expected—for example, the value you set isn’t being recognized—try removing and re-adding the component or the node to resolve the problem.

Now that you have a fully customizable health component, it’s time to try it out with a monster.

Creating Your First Monster

Drag a new Color Sprite into the scene. In the Attributes Inspector, set the following options:

  • Name: monster
  • Texture: skeleton_0
  • Scale: (X: 0.75, Y: 0.75)

With the monster node still selected, switch to the Components Inspector and add a new HealthComponent. Set both the currentHealth and maxHealth property values to 2.

Build and run the project. Notice that Val and the Skeleton monster each have their own health component attached, and each with its own configuration:

images/BuildingGamesWithEntitiesAndComponents/custom-health-meters.png

Now that you have your first component set up and know how to add it to a node using the Scene Editor, you’re ready to find out how to add this same component to an entity through code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset