Build a Monster Generator

The monster generator is what you’ll use to periodically spawn new monsters of a certain type at specific locations within the dungeon.

In the Project Navigator, select the Components group, and inside that group, create a new file (N) using the iOS Swift File template. Name the new file GeneratorComponent.swift and replace its contents with the following code:

 import​ ​SpriteKit
 import​ ​GameplayKit
 
 class​ ​GeneratorComponent​: ​GKComponent​ {
 
 override​ ​func​ ​didAddToEntity​() {
 
  }
 
 override​ ​class​ ​var​ supportsSecureCoding: ​Bool​ {
 true
  }
 }

Because you won’t be overriding any of the other standard methods in this component, you can omit them.

Also, because you’ll be doing a lot with components—both in code and by way of the Scene Editor—it makes sense to create an extension to better manage the underlying node of the entity.

Creating a Component Extension

You may have noticed that in the health component you created in Create Your First Component, there was some duplicated code—specifically in regard to verifying that an underlying node exists. Rather than recreating the wheel in every component you make, you’ll create a GKComponent extension to handle it for you.

Inside the Extensions group, create a new file (N) using the iOS Swift File template. Name this new file GKComponent+Node.swift and replace its contents with the following code:

 import​ ​SpriteKit
 import​ ​GameplayKit
 
 extension​ ​GKComponent​ {
 var​ componentNode: ​SKNode​ {
 if​ ​let​ node = entity?.​component​(ofType: ​GKSKNodeComponent​.​self​)?.node {
 return​ node
  } ​else​ ​if​ ​let​ node = entity?.​component​(ofType:
 RenderComponent​.​self​)?.spriteNode {
 return​ node
  }
 
 return​ ​SKNode​()
  }
 }

Here, you’re creating an extension on the GKComponent class that returns either the node from the GKSKNodeComponent object or the node from the RenderComponent class. Thanks to this extension, it won’t matter if you’re referring to a component that’s been added through code or one that’s been added by way of the Scene Editor.

You can now modify the health component you made earlier to use this new componentNode property. In the Project Navigator, select the HealthComponent.swift file and update the didAddToEntity() method to match the following:

 override​ ​func​ ​didAddToEntity​() {
 if​ ​let​ healthMeter = ​SKReferenceNode​(fileNamed: ​"HealthMeter"​) {
  healthMeter.position = ​CGPoint​(x: 0, y: 100)
  componentNode.​addChild​(healthMeter)
 
 updateHealth​(0, forNode: componentNode)
  }
 }

Also, update the setupBar(at:tint:) method to match this:

 func​ ​setupBar​(at num: ​Int​, tint: ​SKColor​? = ​nil​) {
 if​ ​let​ health = componentNode.​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
  }
  }
 }

If you’d like, you can also remove or comment out the two empty override methods (willRemoveFromEntity() and update(deltaTime:)‘) since you won’t be needing or using them.

Getting Back to Your Generator

Switch back to the GeneratorComponent.swift file and add the following inspectable properties (remember, like other properties, these properties go inside the class below the declaration):

 @GKInspectable​ ​var​ monsterType: ​String​ = ​"skeleton"
 @GKInspectable​ ​var​ maxMonsters: ​Int​ = 10
 
 @GKInspectable​ ​var​ waitTime: ​TimeInterval​ = 5
 @GKInspectable​ ​var​ monsterHealth: ​Int​ = 3

You’ll use these new properties to set up the type and strength of the monsters the generator will spawn. These properties also determine how many monsters each generator will produce and how often it spits them out.

The next step is to write the method that creates each monster, so add the following new method to the GeneratorComponent class:

 func​ ​spawnMonsterEntity​() {
 let​ monsterEntity = ​MonsterEntity​(monsterType: monsterType)
 let​ renderComponent = ​RenderComponent​(imageNamed: ​"​​(​monsterType​)​​_0"​,
  scale: 0.65)
  monsterEntity.​addComponent​(renderComponent)
 
 if​ ​let​ monsterNode =
  monsterEntity.​component​(ofType: ​RenderComponent​.​self​)?.spriteNode {
  monsterNode.position = componentNode.position
  componentNode.parent?.​addChild​(monsterNode)
 
  monsterNode.​run​(​SKAction​.​moveBy​(x: 100, y: 0, duration: 1.0))
 
 let​ healthComponent = ​HealthComponent​()
  healthComponent.currentHealth = monsterHealth
  monsterEntity.​addComponent​(healthComponent)
  }
 }

Here, you’re creating a monster entity and then adding to it the two components, HealthComponent and RenderComponent, using the addComponent(_:) method of the GKEntity class. You’re also using an action to move the monster a little to the right after it spawns. You’ll eventually attach some AI logic to your monsters in Chapter 13, Planning Routes and Creating Believable AI to get them moving on their own, but for now, this move will get them out of the way of the generator.

Now that you have a method to spawn monsters, you can call that method when the generator component is added to an entity. However, because you want the generator to spawn multiple monsters, you’ll use a repeating action to call the new spawnMonsterEntity() method.

Add the following code in the didAddToEntity() method:

 let​ wait = ​SKAction​.​wait​(forDuration: waitTime)
 let​ spawn = ​SKAction​.run { [​unowned​ ​self​] ​in​ ​self​.​spawnMonsterEntity​() }
 let​ sequence = ​SKAction​.​sequence​([wait, spawn])
 
 let​ repeatAction: ​SKAction​?
 if​ maxMonsters == 0 {
  repeatAction = ​SKAction​.​repeatForever​(sequence)
 } ​else​ {
  repeatAction = ​SKAction​.​repeat​(sequence, count: maxMonsters)
 }
 
 componentNode.​run​(repeatAction!, withKey: ​"spawnMonster"​)

With this method, you’re using the value in the maxMonsters property to determine how many times to call the spawnMonsterEntity() method. Note, however, that if the value is 0, you call the spawnMonsterEntity() method indefinitely. (Although, you probably don’t want to make too many generators that continually spit out monsters. Resources are a valuable thing, and you don’t want to needlessly overload your player’s device.)

Save the file and then switch to the GameScene.sks file to open it in the Scene Editor.

Select the monster node and add your new generator component. For now, don’t worry about setting any of the values in this component. Because you’ve set up default values in the GeneratorComponent class, the component will automatically use the following settings:

  • monsterHealth: 3
  • maxMonsters: 10
  • monsterType: skeleton
  • waitTime: 5

Build and run the project. Notice that every five seconds, the monster generator spawns a new skeleton monster with a health of 3. After 10 monsters, the generator stops.

You’re making great progress, but the monster generators could use a little animation to help bring them to life and set them apart from the regular (soon to be roaming) monsters.

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

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