For many years, video game development has relied on the tenets of object-oriented design (OOD). Of the core features in OOD, the concepts of inheritance and polymorphism have been the most useful in this branch of software engineering. It makes sense to think of entities in our games as homogenous groups of objects; objects that we then write rules for in how they interact with each other. For example, thanks to inheritance, all objects in our game can be given the class name of GameObject
; they have functions we'll use throughout the game and then we can branch them off into child classes, such as Player
or Enemy
. We can then continue that thought process as we come up with more specific types of entities, be they objects such as Player
, different enemies, non-player characters (NPCs), or whatever makes sense for the game we are making. Calling a function on those objects, such as Shoot()
or Health()
, could be unique for each child of the parent class and thus we make use of polymorphism in OOD.
However, as mentioned in the previous chapter, although inheritance-based structuring is great for most software applications (including simple games), the unique needs and pairings of video game rules and entities cause inheritance-based structuring to break one of the rules of OOP. That rule is the reusability of our code. The solution to that problem is to separate the game objects and the game's rules into what's known as component-based structuring. Building games with this mentality can allow us to build unique objects, actions, and rules with the ability to not only shift them around throughout our single game project, but also to use them in other projects, cutting the overly customized structuring in which building a game via inheritance-based structuring can cause.
Apple's solution to this issue is the GameplayKit framework. GameplayKit is a completely independent framework that can be used with both SpriteKit or SceneKit games, as well as games written in low-level APIs, such as OpenGL and Metal. First announced for iOS 9 and Xcode 7 at WWDC15, GameplayKit takes the common methodologies and concepts used in game development for years and allows us to work on those aspects independently of what is being drawn on the screen. This framework doesn't handle what's drawn on the screen and is, thus, made specifically for the Model portion of MVC.
There are several game development concepts handled by GameplayKit, which we shall review in this chapter. These concepts are entities and components, state machines, agents, goals, behaviors, pathfinding, MinMaxAI, random sources, and rule systems.
We can think of entities as the objects in our game. They can be the player, an enemy character, an NPC, level decorations and backgrounds, or even the UI used to inform the player of their lives, power, and other stats. The entity is thought of as a container of components. Components are behaviors that dictate the appearance and actions of an entity. One might ask, "how is this any different from objects and functions?" The short answer is that objects and functions in inheritance-based design describe more of what our game objects are, while working with component-based structuring focuses more on what they do. As we deal with the classes and functionality of the GameplayKit framework, we will be able to get a better handle on this. In this framework, we'll see that entities and components are handled with the GKEntity
and GKComponent
classes, respectively.
If you are still a bit confused about component-based structuring, check back in our previous chapter where we went into this in a bit more detail. You can also visit the developer page about this design methodology here: https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/GameplayKit_Guide/EntityComponent.html.
Anyone familiar with Java or C# will understand the concept of an abstract class. The GKComponent
class is essentially an abstract class. Quoting from the speakers at WWDC: think of components as "little black boxes of functionality." Objects of the class GKEntity
are like our generic GameObject
class mentioned before. However, unlike the objects we've dealt with before, we typically don't add too much in the way of custom functionality to them (otherwise, we'd be leaning towards inheritance-based structuring).
We first create a game object and subclass it as a member of the GKEntity
type. For this example, let's just call our object class GameEntity
. Also, don't forget to import the GameplayKit API:
import GameplayKit class GameEntity : GKEntity
Again, our goal with an object of the type GKEntity
is to use it as a container for GKComponent
objects. Objects of the GKEntity
class inherit the following functions:
components
: This is a property that returns an array of the GKComponents
in our GKEntity
objectaddComponent(_:)
: This is a function that we use to add GKComponents
to our GKEntity
removeComponentForClass(_:)
: This removes a component for a class of GKEntity
objectsupdateWithDeltaTime(_:)
: This updates with the render/game loop for each component in the entity; this is what's known as per-entity updatingThe last function in the GKEntity
class, updateWithDeltaTime(_:)
, is one of the two mechanisms used by GameplayKit when dispatching updates to our game object's components. The update mechanisms are as follows:
updateWithDeltaTime(_:)
function of GKEntity
calls all of the entity objects' updateWithDeltaTime(_:)
functions and then dispatches that to all of the component's updateWithDeltaTime(_:)
functions.GKComponentSystem
to categorize groups of components and then call its updateWithDeltaTime(_:)
function to fire off updates specific to the class of component instances it manages. GKComponentSystem
doesn't make note of your game's entity/component hierarchy, so they can be used to efficiently update more complex games.For example, if we have a specific way we want to update player and enemy attacks, then we create an object of the GKComponentSystem
class, initiate it with the initWithComponentClass(_:)
function, and pass that class of components (in this case, Attack components) to that system. It's in that GKComponentSystem
object's updateWithDeltaTime(_:)
function that we specify the unique update logic for that group of components, independently of the entity updates. This can come in handy when dealing with enemy/NPC AI in more complex games.
Here's a visual look at these classes:
Don't worry if this is still somewhat confusing. Those of you who may be more familiar with the game engine Unity will have seen how components, such as rigid bodies, materials, and scripts, can be added to objects placed in a game scene. The concept here with iOS components and entities is not that much different. Each component in Unity and other engines can be its own independently working functionality.
Let's take a look at the code snippet provided during the WWDC15 conference, in both Objective-C and Swift, showing how we'd create entities, components, and component systems in our projects:
//Objective-C /* Make our archer */ GKEntity *archer = [GKEntity entity]; /* Archers can move, shoot, be targeted */ [archer addComponent: [MoveComponent component]]; [archer addComponent: [ShootComponent component]]; [archer addComponent: [TargetComponent component]]; /* Create MoveComponentSystem */ GKComponentSystem *moveSystem = [GKComponentSystem systemWithComponentClass:MoveComponent.class]; /* Add archer's MoveComponent to the system */ [moveSystem addComponent: [archer componentForClass:MoveComponent.class]]; //Swift /* Make our archer */ let archer = GKEntity() /* Archers can move, shoot, be targeted */ let moveComponent = MoveComponent() let shootComponent = ShootComponent() let targetComponent = TargetComponent() archer.addComponent(moveComponent) archer.addComponent(shootComponent) archer.addComponent(targetComponent) /* Create MoveComponentSystem */ let moveComponentSystem = GKComponentSystem(componentClass: MoveComponent.self) /* Add archer's MoveComponent to the system */ moveComponentSystem.addComponent(archer.componentForClass(MoveComponent.self)!)
What this code does is create our GKEntity
objects; in this case, the tower game example's archer character. Next it adds predefined GKComponent
objects via the addComponent(_:)
function. We also create a GKComponentSystem
object named moveComponentSystem
that will be used to update only movement type components. The archer's own moveComponent
class is added to this system with moveComponentSystem.addComponent(_:)
. Make a note of how the parameters passed through this object in addition to its initialization are class types of the component types denoted by the .class
or .self
properties, depending on which language we are writing our code in.
As of this publication, the componentForClass()
function might not be fully functional for the Swift programming language. So if the Swift implementation isn't working as expected for this and other GameplayKit object initializations, the Objective-C version of this code will need to be used and linked to your project via an Objective-C–Swift bridging file. This will more than likely be ironed out in future updates to Swift as Apple continues to move away from Objective-C as the main language of the platform. For more information on how to make this bridging file, check out this link: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html.
Apple provides us with a project named Maze
that uses these classes as well as some other concepts we'll be going over shortly.
Here's a link to the project that could help to give you an even better idea of entities and components:
https://developer.apple.com/sample-code/wwdc/2015/downloads/Maze.zip
Before we go over more specific code use related to GKEntity
and GKComponent
objects, we'll look into a game development concept that is best coupled with these objects, which is the concept of state machines.
18.188.98.148