【GameplayKit】効率的にゲームをつくるためのフレームワーク②

#前回までの記事
[効率的にゲームをつくるためのフレームワーク①](http://qiita.com/chino_tweet/items/bb16dfdbfdfe2e3a00f2)では、GameplayKitの概要を説明してサンプルプロジェクトの準備をしています。

#この記事の概要
エンティティにジオメトリコンポーネントを実装します
エンティティは`GKEntity`クラスのオブジェクトで、各種コンポーネントの器となることによって、その機能を実装します。
`GKComponent`クラスのオブジェクトであるコンポーネントは、エンティティの特定的な見た目や振る舞いの役割をします。
つまり、コンポーネントはエンティティに縛られることなく自由に割り振ることができます。

ゲームシーンに配置されている`SCNNode`オブジェクトである各色ボックスノードと、`GKEntity`オブジェクトである各色エンティティを混同しないように注意したい。

一つのコンポーネントは、一つのボックスノードを参照している。
このコンポーネントをエンティティに追加することで、シーンにあるボックスノードを操作する。

#手順
大まかな作業は以下の通りです。
ジオメトリコンポーネントを定義
エンティティにジオメトリコンポーネントを実装
ビルド

##ジオメトリコンポーネントクラスを定義
メニューバー「File > New > File... > iOS > Source > Swift File」を選択してファイルを新規作成する。（GeometryComponent.swift）
SceneKitとGameplayKitをimportする。
GKComponentクラスを定義する

```ruby:GeometryComponent.swift
import SceneKit
import GameplayKit

class GeometryComponent: GKComponent {

}
```

`SCNNode`型の`geometryNode`プロパティを定義する。
イニシャライザを実装する。

```ruby:GeometryComponent.swift
class GeometryComponent: GKComponent {
    
    let geometryNode: SCNNode
    
    init(geometryNode: SCNNode) {
        self.geometryNode = geometryNode
        super.init()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
```

GeometryComponentクラスのイニシャライザは、ゲームシーンの生成時に呼び出されます。
このイニシャライザで引数として受ける`SCNNode`オブジェクトの各色ボックスノードを、自身のメンバプロパティとして参照します。この引数geometryNodeには、シーンに配置した各色ボックスが渡ってきます。
個々のコンポーネントは、互いにどのエンティティに追加されているか関知しません。
また、required init?(coder)がないとエラーになるので、実装しておきます。

##ボックスにジオメトリコンポーネントを適用
GameクラスにGameplayKitをインポートする。
エンティティのファクトリメソッドを実装する。

```ruby:Game.swift
import GameplayKit

class Game: NSObject, SCNSceneRendererDelegate {
    let scene = SCNScene(named: "GameScene.scn")!

    override init() {
        super.init()        
    }

    func makeBoxEntity(forNodeWithName name: String) -> GKEntity {
        let node = scene.rootNode.childNode(withName: name, recursively: false)
        guard let boxNode = node else { fatalError("no exist \(name) node.") }
        
        let geometryComponent = GeometryComponent(geometryNode: boxNode)
        let box = GKEntity()
        box.addComponent(geometryComponent)
        
        return box
    }
}
```

エンティティオブジェクト`box`を新たに生成して、コンポーネントオブジェクトを追加しています。コンポーネントには、ボックスノードを参照しています。
イニシャライザでファクトリメソッドを呼び出す。

```ruby:Game.swift
class Game: NSObject, SCNSceneRendererDelegate {
    let scene = SCNScene(named: "GameScene.scn")!

    override init() {
        super.init()        
        setUpEntities()
    }

    func setUpEntities() {
        let redBoxEntity = makeBoxEntity(forNodeWithName: "redBox")
        let yellowBoxEntity = makeBoxEntity(forNodeWithName: "yellowBox")
        let greenBoxEntity = makeBoxEntity(forNodeWithName: "greenBox")
        let blueBoxEntity = makeBoxEntity(forNodeWithName: "blueBox")
        let purpleBoxEntity = makeBoxEntity(forNodeWithName: "purpleBox")
                
    }

    func makeBoxEntity(forNodeWithName name: String) -> GKEntity {
        let node = scene.rootNode.childNode(withName: name, recursively: false)
        guard let boxNode = node else { fatalError("no exist \(name) node.") }
        
        let geometryComponent = GeometryComponent(geometryNode: boxNode)
        let box = GKEntity()
        box.addComponent(geometryComponent)
        
        return box
    }
}
```

ファクトリメソッド`makeBoxEntity`では、ジオメトリコンポーネントのオブジェクト`geometryComponent`を生成して、エンティティにジオメトリコンポーネントを追加しています。

エンティティを追跡管理するために、メンバプロパティの配列を宣言して格納する。

```ruby:Game.swift
import SceneKit
import GameplayKit

class Game: NSObject {
    
    let scene = SCNScene(named: "GameScene.scn")!

    var entities = [GKEntity]()
    
    override init() {
        super.init()
        setUpEntities()
    }
    
    func setUpEntities() {
        let redBoxEntity    = makeBoxEntity(forNodeWithName: "redBox")
        let yellowBoxEntity = makeBoxEntity(forNodeWithName: "yellowBox")
        let greenBoxEntity  = makeBoxEntity(forNodeWithName: "greenBox")
        let blueBoxEntity   = makeBoxEntity(forNodeWithName: "blueBox")
        let purpleBoxEntity = makeBoxEntity(forNodeWithName: "purpleBox")
        
        entities = [redBoxEntity, yellowBoxEntity, greenBoxEntity, blueBoxEntity, purpleBoxEntity]
    }
    
    func makeBoxEntity(forNodeWithName name: String) -> GKEntity {
        let node = scene.rootNode.childNode(withName: name, recursively: false)
        guard let boxNode = node else { fatalError("no exist \(name) node.") }
        
        let geometryComponent = GeometryComponent(geometryNode: boxNode)
        let box = GKEntity()
        box.addComponent(geometryComponent)
        
        return box
    }   
}
```

##ビルド
いったん、ビルドしてジオメトリコンポーネントが正しくアタッチされているかを確認する。
ジオメトリコンポーネントでは、まだ何もしていないので正常にビルドができても変化はありません。

#続く
[効率的にゲームをつくるためのフレームワーク③](http://qiita.com/chino_tweet/items/7ff689c8d6c992a048e0)に続きます。