Boxes/Game.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Handles logic controlling the scene. Primarily, it initializes the game's entities and components structure, and handles game updates. |
*/ |
import SceneKit |
import GameplayKit |
class Game: NSObject, SCNSceneRendererDelegate { |
// MARK: Properties |
/// The scene that the game controls. |
let scene = SCNScene(named: "GameScene.scn")! |
/** |
Manages all of the player control components, allowing you to access all |
of them in one place. |
*/ |
let playerControlComponentSystem = GKComponentSystem(componentClass: PlayerControlComponent.self) |
/** |
Manages all of the particle components, allowing you to update all of |
them synchronously. |
*/ |
let particleComponentSystem = GKComponentSystem(componentClass: ParticleComponent.self) |
/// Holds the box entities, so they won't be deallocated. |
var boxEntities = [GKEntity]() |
/// Keeps track of the time for use in the update method. |
var previousUpdateTime: TimeInterval = 0 |
// MARK: Initialization |
override init() { |
super.init() |
setUpEntities() |
addComponentsToComponentSystems() |
} |
/** |
Sets up the entities for the scene. It creates four entities with a |
factory method, but leaves the purple box entity for you to set up |
yourself. |
*/ |
func setUpEntities() { |
// Create entities with components using the factory method. |
let redBoxEntity = makeBoxEntity(forNodeWithName: "redBox") |
let yellowBoxEntity = makeBoxEntity(forNodeWithName: "yellowBox", withParticleComponentNamed: "Fire") |
let greenBoxEntity = makeBoxEntity(forNodeWithName: "greenBox", wantsPlayerControlComponent: true) |
let blueBoxEntity = makeBoxEntity(forNodeWithName: "blueBox", wantsPlayerControlComponent: true, withParticleComponentNamed: "Sparkle") |
// Create the box entity and grab its node from the scene. |
let purpleBoxEntity = GKEntity() |
let purpleBoxNode = scene.rootNode.childNode(withName: "purpleBox", recursively: false) |
// Create the purple box's geometry component, and add it to the entity. |
let geometryComponent = GeometryComponent(geometryNode: purpleBoxNode!) |
purpleBoxEntity.addComponent(geometryComponent) |
/* |
Experiment for yourself: |
Try creating and attaching a ParticleComponent and |
PlayerControlComponent for the purple box in the space below. |
*/ |
// Keep track of all the newly-created box entities. |
boxEntities = [ |
redBoxEntity, |
yellowBoxEntity, |
greenBoxEntity, |
blueBoxEntity, |
purpleBoxEntity |
] |
} |
/** |
Checks each box for components. If a box has a particle and/or player |
control component, it is added to the appropriate component system. |
Since the methods `jumpBoxes(_:)` and `renderer(_:)` use component |
systems to reference components, a component will not properly affect |
the scene unless it is added to one of these systems. |
*/ |
func addComponentsToComponentSystems() { |
for box in boxEntities { |
particleComponentSystem.addComponent(foundIn: box) |
playerControlComponentSystem.addComponent(foundIn: box) |
} |
} |
// MARK: Methods |
/** |
Causes each box controlled by an entity with a playerControlComponent |
to jump. |
*/ |
func jumpBoxes() { |
/* |
Iterate over each component in the component system that is a |
PlayerControlComponent. |
*/ |
for case let component as PlayerControlComponent in playerControlComponentSystem.components { |
component.jump() |
} |
} |
/** |
Updates every frame, and keeps components in the particle component |
system up to date. |
*/ |
func renderer(_: SCNSceneRenderer, updateAtTime time: TimeInterval) { |
// Calculate the time change since the previous update. |
let timeSincePreviousUpdate = time - previousUpdateTime |
// Update the particle component system with the time change. |
particleComponentSystem.update(deltaTime: timeSincePreviousUpdate) |
// Update the previous update time to keep future calculations accurate. |
previousUpdateTime = time |
} |
// MARK: Box Factory Method |
/** |
Creates box entities with a set of components as specified in the |
parameters. It uses default parameter values so parameters can be |
ommitted in the method call. The parameter particleComponentName is a |
string optional so its default parameter value can be nil. |
- Parameter name: The name of the box that this entity should manage. |
- Parameter wantsPlayerControlComponent: Whether or not this entity |
should be set up with a player control component. |
- Parameter particleComponentName: The name of the particle |
component entity should be set up with. |
- Returns: An entity with the set of components requested. |
*/ |
func makeBoxEntity(forNodeWithName name: String, wantsPlayerControlComponent: Bool = false, withParticleComponentNamed particleComponentName: String? = nil) -> GKEntity { |
// Create the box entity and grab its node from the scene. |
let box = GKEntity() |
guard let boxNode = scene.rootNode.childNode(withName: name, recursively: false) else { |
fatalError("Making box with name \(name) failed because the GameScene scene file contains no nodes with that name.") |
} |
// Create and attach a geometry component to the box. |
let geometryComponent = GeometryComponent(geometryNode: boxNode) |
box.addComponent(geometryComponent) |
// If requested, create and attach a particle component. |
if let particleComponentName = particleComponentName { |
let particleComponent = ParticleComponent(particleName: particleComponentName) |
box.addComponent(particleComponent) |
} |
// If requested, create and attach a player control component. |
if wantsPlayerControlComponent { |
let playerControlComponent = PlayerControlComponent() |
box.addComponent(playerControlComponent) |
} |
return box |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-13