Add a New Collectible Component

You first learned about components in Create Your First Component. With components, the focus is on what a “thing” does versus what it is. You might believe that this focus lets you off the hook for thinking about what things are, but that isn’t the case; you still need to consider how you’ll be using your components.

In Val’s Revenge, there are three kinds of collectible items (types):

images/UsingStatesAndStateMachines/collectibles.png

Each type serves a useful purpose:

  • Key: Collect keys to unlock dungeon doors
  • Food: Collect food to increase Val’s health (up to her maximum)
  • Treasure: Collect treasure for bragging rights

Similar to what you did in Creating an Animation Component, you’ll create a single file to hold the collectible component and the corresponding collectible struct that defines the collectible properties.

Creating the Collectible Struct

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

 import​ ​SpriteKit
 import​ ​GameplayKit
 
 struct​ ​Collectible​ {
 let​ type: ​GameObjectType
 
 let​ collectSoundFile: ​String
 let​ destroySoundFile: ​String
 
 let​ canDestroy: ​Bool
 
 init​(type: ​GameObjectType​, collectSound: ​String​,
  destroySound: ​String​, canDestroy: ​Bool​ = ​false​) {
 self​.type = type
 
 self​.collectSoundFile = collectSound
 self​.destroySoundFile = destroySound
 
 self​.canDestroy = canDestroy
  }
 }

This code sets up the new Collectible struct and its initializer with a default value for the canDestroy property. In addition to this property, the struct also includes properties for the collectible type and the collected and destroyed sound filenames—in other words, what sounds the player will hear when Val picks up or destroys a collectible.

Before you finish this new component, you need to set up the collectible game objects. Before moving on to the next section, save the file.

Adding the Collectible Game Objects

Switch to the GameObjects.swift file. Inside the GameObjectType enum, and below the line that reads case goblin, add the following code:

 // Collectibles
 case​ key
 case​ food
 case​ treasure

These are the three collectible types you’ll be using in Val’s Revenge.

Next, you’ll use the Collectible struct to set up and store the collectible settings for each type. In the GameObject struct, add the following code below the Skeleton struct:

 struct​ ​Key​ {
 let​ collectibleSettings = ​Collectible​(type: .key,
  collectSound: ​"key"​,
  destroySound: ​"destroyed"​)
 }
 
 struct​ ​Food​ {
 let​ collectibleSettings = ​Collectible​(type: .food,
  collectSound: ​"food"​,
  destroySound: ​"destroyed"​,
  canDestroy: ​true​)
 }
 
 struct​ ​Treasure​ {
 let​ collectibleSettings = ​Collectible​(type: .treasure,
  collectSound: ​"treasure"​,
  destroySound: ​"destroyed"​)
 }

This code configures the collectible settings for the three different collectible types; this includes which sound file to use when the player either picks up or destroys a collectible item. (In Add the Resources, you added some project resources; some of those resources included the sound files you’re using here.)

Scroll up a little, and just above the Goblin struct, add the following code:

 static​ ​let​ key = ​Key​()
 static​ ​let​ food = ​Food​()
 static​ ​let​ treasure = ​Treasure​()

This code creates the static instances of your three collectible types, making it possible to refer to them later so that you can retrieve their individual settings.

Now scroll back down, and below the forAnimationType(_:) method, add the following new method:

 static​ ​func​ ​forCollectibleType​(_ type: ​GameObjectType​?) -> ​Collectible​? {
 switch​ type {
 case​ .key:
 return​ ​GameObject​.key.collectibleSettings
 case​ .food:
 return​ ​GameObject​.food.collectibleSettings
 case​ .treasure:
 return​ ​GameObject​.treasure.collectibleSettings
 default​:
 return​ ​nil
  }
 }

This static method, like the forAnimationType(_:) method before it, grabs the settings for the specified collectible type.

Finally, below the line that reads:

 static​ ​let​ defaultAnimationType = ​GameObjectType​.skeleton.rawValue

add the following code:

 static​ ​let​ defaultCollectibleType = ​GameObjectType​.key.rawValue

This code sets the default collectible type, making it easier to maintain your code, especially if you want to change the default collectible type later. With everything in place, you’re ready to create the collectible component so that you can define the collectibles in the game and on the map.

Creating the Collectible Component

Switch back to the CollectibleComponent.swift file and add the following code below (and outside of) the Collectible struct:

 // MARK: - COMPONENT CODE STARTS HERE
 
 class​ ​CollectibleComponent​: ​GKComponent​ {
 
 @GKInspectable​ ​var​ collectibleType: ​String​ =
 GameObject​.defaultCollectibleType
 @GKInspectable​ ​var​ value: ​Int​ = 1
 
 private​ ​var​ collectSoundAction = ​SKAction​()
 private​ ​var​ destroySoundAction = ​SKAction​()
 private​ ​var​ canDestroy = ​false
 
 }

Here, you’re creating the necessary properties for the collectible component. Notice that two of these properties are inspectable, meaning you’ll have access to them in the Scene Editor.

Get More Control in the Scene Editor

images/aside-icons/tip.png

In Val’s Revenge, the canDestroy property is not something you can modify on the fly while you’re building your scene. You can, however, change this behavior by changing the code to use a @GKInspectable property instead. There is no right or wrong here as to which properties you expose in the Scene Editor—it’s really just a matter of what makes the most sense for your game.

Next, inside the new CollectibleComponent class, add the following code:

 override​ ​func​ ​didAddToEntity​() {
 guard​ ​let​ collectible =
 GameObject​.​forCollectibleType​(​GameObjectType​(rawValue: collectibleType))
 else​ {
 return
  }
 
  collectSoundAction =
 SKAction​.​playSoundFileNamed​(collectible.collectSoundFile,
  waitForCompletion: ​false​)
 
  destroySoundAction =
 SKAction​.​playSoundFileNamed​(collectible.destroySoundFile,
  waitForCompletion: ​false​)
 
  canDestroy = collectible.canDestroy
 }
 
 func​ ​collectedItem​() {
  componentNode.​run​(collectSoundAction, completion: {
 self​.componentNode.​removeFromParent​()
  })
 }
 
 func​ ​destroyedItem​() {
 if​ canDestroy == ​true​ {
  componentNode.​run​(destroySoundAction, completion: {
 self​.componentNode.​removeFromParent​()
  })
  }
 }
 
 override​ ​class​ ​var​ supportsSecureCoding: ​Bool​ {
 true
 }

Here, the didAddToEntity() method preloads two sound actions using the filenames stored in the collectSoundFile and destroySoundFile properties. You preload these actions so that there’s no delay when you first play the sounds (which happens in the collectedItem() and destroyedItem() methods).

At this point, you might be tempted to start adding the collectible items to your map. While I appreciate your enthusiasm, you’re missing two key components (no pun intended): a way to add physics bodies to your nodes, and a way to handle contacts. Because, as of right now, there’s no way for the player to collect keys, food, and treasure, much less open any doors.

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

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