Modify the Monster Generator

In Build a Monster Generator, you set up a monster generator component that spawns monsters based on its set up, such as how many monsters per generator and how often they spawn. When a generator spawns a monster, you fire off an action that moves the new monster slightly to the right, stopping a few pixels away from the generator.

As you might have guessed, monsters who don’t move pose very little threat or challenge for Val—she is a warrior after all, and she deserves better. To solve this problem, you’ll set up some goals and behaviors to help the monsters track Val, but only when she has keys in her pocket. You’ll know when she does thanks to the state machine you set up in the previous chapter.

But moving monsters still won’t be enough of a challenge for Val, so you’ll also set up pathfinding on the Key collectible, making it move around the scene on a set path.

With all of these new challenges ahead, Val (and the player) will need a moment to get ready for battle. In other words, you don’t want the monsters to start spawning right away. Instead, you’ll wait until the player starts the game. As it turns out, you don’t know when the player has actually started the game, so you’ll need to work on fixing that first.

Setting up the Main Game States and State Machine

As you learned in Chapter 12, Using States and State Machines, states and state machines help you track the status (or state) of something. In this case, you want to track when the player starts the game. Sure, you could use a boolean flag to track whether the game is in progress, but because you have some fancy new knowledge about states and state machines, let’s put it to good use.

To begin, open the valsrevenge project in Xcode.

In the Project Navigator, select the States group and create a new file (N) using the iOS Swift File template.

Name the new file MainGameStates.swift and replace the contents of the file with the following code:

 import​ ​GameplayKit
 
 class​ ​PauseState​: ​GKState​ {
 
 override​ ​func​ ​isValidNextState​(_ stateClass: ​AnyClass​) -> ​Bool​ {
 return​ stateClass == ​PlayingState​.​self
  }
 }
 
 class​ ​PlayingState​: ​GKState​ {
 
 override​ ​func​ ​isValidNextState​(_ stateClass: ​AnyClass​) -> ​Bool​ {
 return​ stateClass == ​PauseState​.​self
  }
 }

Here, you’re creating two new states; you’ll use these to keep track of when the player starts the game.

Switch to the GameScene.swift file and add a new property, placing it above the lastUpdateTime property:

 let​ mainGameStateMachine = ​GKStateMachine​(states: [​PauseState​(),
 PlayingState​()])

This code creates the main game state machine, setting it up to use the two states you just created.

Next, you need to set the state machine’s initial state. In the didMoveTo(_:) method, add the following code at the top:

 mainGameStateMachine.​enter​(​PauseState​.​self​)

With the initial game state set, you’re ready to add the code that controls when to start and stop the generators.

Starting and Stopping the Generators

Switch to the GeneratorComponent.swift file and add a new property:

 var​ isRunning = ​false

You’ll use this property to track when the generator is actively spawning monsters.

Next, create two new method stubs, placing them above the spawnMonsterEntity() method:

 func​ ​startGenerator​() {
 
 }
 
 func​ ​stopGenerator​() {
 
 }

You’ll use these two methods to start and stop the generators.

Because you don’t want the generators to immediately spawn monsters, move all of the code from the didAddToEntity() method into the new startGenerator() method. Also, set the isRunning property to true. The updated method looks like this:

 func​ ​startGenerator​() {
» isRunning = ​true
 
 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"​)
 }

The highlighted line is the “new” code where you set the isRunning property to true. The rest of the code is from the didAddToEntity() method, which should now be empty.

Next, update the stopGenerator() method to match the following:

 func​ ​stopGenerator​() {
  isRunning = ​false
  componentNode.​removeAction​(forKey: ​"spawnMonster"​)
 }

Here, you’re setting the isRunning property back to false and removing the action responsible for spawning monsters.

Finally, add an override for the update(deltaTime:) method, placing it below the spawnMonsterEntity() method:

 override​ ​func​ ​update​(deltaTime seconds: ​TimeInterval​) {
 if​ ​let​ scene = componentNode.scene ​as?​ ​GameScene​ {
 switch​ scene.mainGameStateMachine.currentState {
 case​ ​is​ ​PauseState​:
 if​ isRunning == ​true​ {
 stopGenerator​()
  }
 case​ ​is​ ​PlayingState​:
 if​ isRunning == ​false​ {
 startGenerator​()
  }
 default​:
 break
  }
  }
 }

This code overrides the component’s update(deltaTime:) method, which is a per-frame update, and uses the main game state machine’s current state to decide when it should spawn monsters. Remember, generators won’t spawn monsters until the player starts the game. So, now you need a way to start the game.

For simplicity, you’ll start the game when the player touches the scene, regardless of the touch location. Another option would be to add a “Play” or “Start” button, but for this example, simply touching the scene works.

Switch back to the GameScene.swift file and locate the touchDown(atPoint:) method. Add the following line of code at the top:

 mainGameStateMachine.​enter​(​PlayingState​.​self​)

Build and run the game, but don’t immediately “start” the game—wait for a moment before touching the scene. Notice how the monsters won’t start spawning until after the first touch.

Thanks to the new state machine, you’ll have a much easier time knowing when the game is in play. Now it’s time to get those monsters thinking for themselves.

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

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