2018年5月5日土曜日

GKAgent2D を使ってみた

概要

GKAgent2D は SpriteKit 上で CPU 用の AI を作成することができるクラスです
特定のオブジェクトを追いかけたり、特定のオブジェクトを近づけたりできます
今回は最低限の使い方を紹介します

環境

  • macOS 10.13.4
  • Xcode 9.3 (9E145)

プロジェクト作成

Game 用のプロジェクトを作成しましょう
「Integrate GameplayKit」はチェックしないで OK です
gkagent2d_1.png

エージェント作成

まずエージェントを作成しましょう
今回は 2 つのエージェントを定義しますがいくつ定義しても OK です

class GameScene: SKScene {

    var agent1 = GKAgent2D()
    var agent2 = GKAgent2D()

    override func didMove(to view: SKView) {
        agent1.position = vector_float2(x: 0, y: 0)
        agent2.position = vector_float2(x: Float(self.frame.maxX), y: Float(self.frame.maxY))
    }
}

GKAgent2D クラスのオブジェクトを生成します
生成したらポジションを設定します
今回は真ん中と画面の右端を設定し、右端のエージェントが真ん中のエージェントに移動するようにします

ゴールと行動を設定

続いてエージェントの行動を設定します
didMove に続けて以下を追加しましょう

agent2.behavior = GKBehavior(goals: [GKGoal(toSeekAgent: agent1)], andWeights: [100])

行動 (GKBehavior) は指定するゴール (GKGoal) を元にどうエージェントが移動するかを設定します
今回は agent2 が agent1 に近づく (シークする) ように行動するので GKGoal(toSeekAgent: agent1) をゴールに指定し GKBehavior を設定します

エージェント用の delegate メソッドを追加

エージェントが移動した際にコールされる delegte メソッドを追加します
クラスの外に extension を使って GameScene の拡張を行います

extension GameScene: GKAgentDelegate {
    func agentDidUpdate(_ agent: GKAgent) {
        if let agent2 = agent as? GKAgent2D {
            print(agent2.position)
        }
    }
}

GKAgentDelegate を継承し agentDidUpdate を実装しましょう
今回は agent2 が移動するので引数の agent には agent2 の情報が渡ってきます
とりあえず print しましょう
本来ここでは SKSpriteNode とエージェントがリンクするようにノードの移動や再描画を行います

delegate メソッドが定義できたら didMove に続けて以下を追記しましょう

agent2.delegate = self

これで agent2 が移動するたびに先ほどの agentDidUpdate が呼ばれるようになります

エージェントを管理するコンポーネントを作成

エージェントは専用のコンポーネントに入れて管理する必要があります
フィールドに以下を追加しましょう

var agentSystem = GKComponentSystem(componentClass: GKAgent2D.self)

これに先ほどの agent2 を入れていきます
didMove に以下を追記しましょう

agentSystem.addComponent(agent2)

この agentSystem を操作することで agent2 を操作します

エージェントの更新処理

agentDidUpdate が呼ばれるように agentSystem を更新します
GameScene 内に update を定義しましょう
そして以下のように agentSystem を更新します

override func update(_ currentTime: TimeInterval) {
    let delta = prevTime == 0 ? 0 : currentTime - prevTime
    prevTime = currentTime
    agentSystem.update(deltaTime: delta)
}

prevTime はフィールドに必要なので追加しておきましょう

var prevTime: TimeInterval = 0

deltaTime は前回の更新からどれくらい経過したかを指定します
おそらくこの間隔が大きければ大きいほどエージェントの動きが大雑把になるんだと思います
今回は GameScene 側の update が呼ばれる度に agentSystem.update もコールしているのでかなり細かくエージェントの動きを追従することができるようになります

動作確認

これで起動してみましょう
今回は特に SKSpriteNode などを配置していないのでコンソール画面で挙動を確認します
gkagent2d_2.gif

起動すると agent2 の座標が次々と流れていきます
よく見ると agent1 の座標 (0, 0) に向かって移動しているのがわかると思います

最後に

GKAgent2D を使ってエージェントを動かしてみました
今回は追従するような動きでしたが他にも GKGoal にいろいろとメソッドが用意されている組み合わせることで複雑な動きもできると思います

今回検証していないですが気になった点としては

  • スピードをどう調整するのか
  • エージェントが移動する導線上に壁などを設置することはできるのか

あたりは気になりました
GKAgent2D は SKSpriteNode の世界とは別の世界になると思います
なので物理エンジンを使って衝突判定をする場合にはエージェント側の動きも衝突を考慮する必要があるのではと思いました
この辺りは細かい検証ができていないのでやってみないと何とも言えません

参考サイト

0 件のコメント:

コメントを投稿