Animate the Monsters

To help distinguish your monster generators from standard monsters, you’ll create an animation component, which you’ll use to animate the monsters. First you’ll create an extension to help build the textures. Then, you’ll build out the animation component and define the different types of animations.

Creating an Animation Extension

In Load the Textures, you built a small helper method to help load textures for animation. This time around, however, you’ll create an extension method for the SKTexture class instead. Extensions are generally preferred as they tend to be more reusable than helper methods that can easily get buried within the underbelly of your code.

In the Project Navigator, and inside the Extensions group, create a new file (N) using the iOS Swift Filetemplate. Name the new file SKTexture+LoadTextures.swift and replace its contents with the following:

 import​ ​SpriteKit
 
 extension​ ​SKTexture​ {
 static​ ​func​ ​loadTextures​(atlas: ​String​, prefix: ​String​,
  startsAt: ​Int​, stopsAt: ​Int​) -> [​SKTexture​] {
 
 var​ textureArray = [​SKTexture​]()
 let​ textureAtlas = ​SKTextureAtlas​(named: atlas)
 for​ i ​in​ startsAt...stopsAt {
 let​ textureName = ​"​​(​​prefix​​)(​i​)​​"
 let​ temp = textureAtlas.​textureNamed​(textureName)
  textureArray.​append​(temp)
  }
 
 return​ textureArray
  }
 }

This code should look familiar to you since you used something similar in Gloop Drop. In case it doesn’t (or if you forgot how it works), you’re essentially looping through the input values to build an array of textures that you’ll use for the animation.

Excellent, you now have a handy way of loading textures. It’s time to put this method to good use in a new animation component.

Creating an Animation Component

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

 import​ ​SpriteKit
 import​ ​GameplayKit
 
 // MARK: - COMPONENT CODE STARTS HERE
 
 class​ ​AnimationComponent​: ​GKComponent​ {
 
 override​ ​func​ ​didAddToEntity​() {
 
  }
 
 override​ ​class​ ​var​ supportsSecureCoding: ​Bool​ {
 true
  }
 }

This code sets you up with a base starting point for your new animation component.

However, before you get into the finer implementation details, let’s use a struct to define exactly what an animation is.

At the top of this file (and below the import statements), add the following:

 struct​ ​Animation​ {
 let​ textures: [​SKTexture​]
 var​ timePerFrame: ​TimeInterval
 
 let​ repeatTexturesForever: ​Bool
 let​ resizeTexture: ​Bool
 let​ restoreTexture: ​Bool
 
 init​(textures: [​SKTexture​],
  timePerFrame: ​TimeInterval​ = ​TimeInterval​(1.0 / 5.0),
  repeatTexturesForever: ​Bool​ = ​true​, resizeTexture: ​Bool​ = ​true​,
  restoreTexture: ​Bool​ = ​true​) {
 
 self​.textures = textures
 self​.timePerFrame = timePerFrame
 self​.repeatTexturesForever = repeatTexturesForever
 self​.resizeTexture = resizeTexture
 self​.restoreTexture = restoreTexture
  }
 }

Here, you’re setting up a handful of properties that define an animation; this includes properties like the texture array, the time per frame, and how often to repeat the animation. But before you can complete and use the animation component, you need to specify the different types of animation for your game objects.

Defining the Game Objects

Inside the Entities group, create another new file (N) using the iOS Swift File template. Name this new file GameObjects.swift and replace its contents with the following code:

 import​ ​SpriteKit
 import​ ​GameplayKit
 
 enum​ ​GameObjectType​: ​String​ {
 
 // Monsters
 case​ skeleton
 case​ goblin
 }
 
 struct​ ​GameObject​ {
 
 static​ ​let​ defaultGeneratorType = ​GameObjectType​.skeleton.rawValue
 static​ ​let​ defaultAnimationType = ​GameObjectType​.skeleton.rawValue
 
 static​ ​let​ skeleton = ​Skeleton​()
 static​ ​let​ goblin = ​Goblin​()
 
 struct​ ​Goblin​ {
 let​ animationSettings = ​Animation​(textures:
 SKTexture​.​loadTextures​(atlas: ​"monster_goblin"​,
  prefix: ​"goblin_"​, startsAt: 0, stopsAt: 1))
  }
 
 struct​ ​Skeleton​ {
 let​ animationSettings = ​Animation​(textures:
 SKTexture​.​loadTextures​(atlas: ​"monster_skeleton"​,
  prefix: ​"skeleton_"​, startsAt: 0, stopsAt: 1),
  timePerFrame: ​TimeInterval​(1.0 / 25.0))
  }
 }

This uses the new SKTexture extension to create some static properties that hold information about your game objects, including its animation type. As your game grows, you can add additional properties and objects here, keeping everything you need about your game objects in one place.

Below the code you just added, yet still inside the GameObject struct, add the following code:

 static​ ​func​ ​forAnimationType​(_ type: ​GameObjectType​?) -> ​Animation​? {
 switch​ type {
 case​ .skeleton:
 return​ ​GameObject​.skeleton.animationSettings
 case​ .goblin:
 return​ ​GameObject​.goblin.animationSettings
 default​:
 return​ ​nil
  }
 }

This code defines a static method that you can use to grab an object’s animation set up.

Switch back to the AnimationComponent.swift file and then add the following new property:

 @GKInspectable​ ​var​ animationType: ​String​ = ​GameObject​.defaultAnimationType

You can use this property to define the animation type, setting the default property to whatever string value you defined in the GameObjects.swift file—in this case, skeleton.

Scroll down a bit, and in the didAddToEntity() method, add the following code:

 guard​ ​let​ animation =
 GameObject​.​forAnimationType​(​GameObjectType​(rawValue: animationType)) ​else​ {
 return
 }
 
 let​ textures = animation.textures
 let​ timePerFrame = animation.timePerFrame
 let​ animationAction = ​SKAction​.​animate​(with: textures,
  timePerFrame: timePerFrame)
 
 if​ animation.repeatTexturesForever == ​true​ {
 let​ repeatAction = ​SKAction​.​repeatForever​(animationAction)
  componentNode.​run​(repeatAction)
 } ​else​ {
  componentNode.​run​(animationAction)
 }

This code uses the static method to retrieve and build the desired game object’s animation.

Notice the animationType inspectable property? If you recall, in the GeneratorComponent class, you had a monsterType inspectable property where you set its default string value to skeleton. Rather than hardcode that value (hardcoding should be avoided when possible), you can now update this property to use your new defaultGeneratorType that you created in the GameObject struct—again, keeping all of your game objects and related setup in one place.

Switch to the GeneratorComponent.swift file and replace the following line:

 @GKInspectable​ ​var​ monsterType: ​String​ = ​"skeleton"

with this one instead:

 @GKInspectable​ ​var​ monsterType: ​String​ = ​GameObject​.defaultGeneratorType

With this change, you make it much easier to change your default generator type in the future. For example, suppose you initially wanted skeleton monsters as the default monster type. You might create multiple components and/or other code where you’d hardcode skeleton for the value, as you previously did here. Then suppose, some time later in the future, you decide that a ghost monster would have been a better choice. In that case, you’d have to update all of those hardcoded values. However, by creating a single default type and using that instead, you only need to update your value in one spot.

Adding the Monster Generators to the Scene

All right, it’s time to get the monster generators added to the scene.

Save the GeneratorComponent.swift file and switch back to the GameScene.sks file. Remove the monster node and add two new Color Sprite nodes. Switch to the Attributes Inspector, and set up the two sprite nodes like so:

images/BuildingGamesWithEntitiesAndComponents/generators.png

Setting Up the Monster Generators

The final step is to add the components to each of the monster generator nodes. Val is a warrior at heart, so she’ll need quite a few monsters to give her a worthy challenge. Switch to the Components Inspector, and with the generator_goblin node selected, add the following components with the following settings:

images/BuildingGamesWithEntitiesAndComponents/components-goblin.png

Switch to the generator_skeleton node and add its components with the following settings:

images/BuildingGamesWithEntitiesAndComponents/components-skeleton.png

Build and run the project. You’ll notice that each monster generator spits out its monsters, each with its own health indicator. You’ll also see that the generators are now animated with blinking eyes.

images/BuildingGamesWithEntitiesAndComponents/build-end.png
..................Content has been hidden....................

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