Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

前回までの記事

効率的にゲームをつくるためのフレームワーク①では、GameplayKitの概要を説明してサンプルプロジェクトの準備をしています。

この記事の概要

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

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

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

手順

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

ジオメトリコンポーネントクラスを定義

メニューバー「File > New > File... > iOS > Source > Swift File」を選択してファイルを新規作成する。(GeometryComponent.swift)
SceneKitとGameplayKitをimportする。
GKComponentクラスを定義する

GeometryComponent.swift
import SceneKit
import GameplayKit

class GeometryComponent: GKComponent {

}

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

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をインポートする。
エンティティのファクトリメソッドを実装する。

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を新たに生成して、コンポーネントオブジェクトを追加しています。コンポーネントには、ボックスノードを参照しています。
イニシャライザでファクトリメソッドを呼び出す。

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を生成して、エンティティにジオメトリコンポーネントを追加しています。

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

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
    }   
}

ビルド

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

続く

効率的にゲームをつくるためのフレームワーク③に続きます。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away