最後に鬼の動きを「Agents, Goals, and Behaviors」を使った形に置き換えてみようと思います。「GameScene.swift」を下のように修正します。
import SpriteKit import GameplayKit class GameScene: SKScene { let player = SKShapeNode(circleOfRadius: 10) var enemies = [SKShapeNode]() var timer: Timer? var prevTime: TimeInterval = 0 var startTime: TimeInterval = 0 var isGameFinished = false // ここから今回追加分 let playerAgent = GKAgent2D() let agentSystem = GKComponentSystem(componentClass: GKAgent2D.self) var enemyAgents = [GKAgent2D]() // ここまで今回追加分 // 省略 func createEnemy() { let enemy = SKShapeNode(circleOfRadius: 10) enemy.position.x = size.width / 2 enemy.fillColor = UIColor(red: 0.94, green: 0.14, blue: 0.08, alpha: 1.0) enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.frame.width / 2) addChild(enemy) enemies.append(enemy) // ここから今回追加分 let anemyAgent = GKAgent2D() anemyAgent.maxAcceleration = 30 anemyAgent.maxSpeed = 70 anemyAgent.position = vector_float2(x: Float(enemy.position.x), y: Float(enemy.position.y)) anemyAgent.delegate = self anemyAgent.behavior = GKBehavior(goals: [ GKGoal(toSeekAgent: playerAgent), ]) agentSystem.addComponent(anemyAgent) enemyAgents.append(anemyAgent) // ここまで今回追加分 } override func update(_ currentTime: TimeInterval) { if prevTime == 0 { prevTime = currentTime startTime = currentTime } // ここから今回追加分 agentSystem.update(deltaTime: currentTime - prevTime) playerAgent.position = vector_float2(x: Float(player.position.x), y: Float(player.position.y)) // ここまで今回追加分 // 下は今回で削除 // if Int(currentTime) != Int(prevTime) { // enemies.forEach { enemy, _ in // enemy.removeAllActions() // // let path = CGMutablePath() // path.move(to: CGPoint()) // path.addLine(to: CGPoint(x: player.position.x - enemy.position.x, y: player.position.y - enemy.position.y)) // enemy.run(SKAction.follow(path, speed: 50.0)) // } // } if !isGameFinished { for enemy in enemies { let dx = enemy.position.x - player.position.x let dy = enemy.position.y - player.position.y if sqrt(dx*dx + dy*dy) < player.frame.width / 2 + enemy.frame.width / 2 { isGameFinished = true timer?.invalidate() let label = SKLabelNode(text: "記録:\(Int(currentTime - startTime))秒") label.fontSize = 80 label.position = CGPoint(x: 0, y: -100) addChild(label) break } } } prevTime = currentTime } } // ここから今回追加分 extension GameScene: GKAgentDelegate { func agentDidUpdate(_ agent: GKAgent) { if let agent = agent as? GKAgent2D, let index = enemyAgents.index(where: { $0 == agent }) { let enemy = enemies[index] enemy.position = CGPoint(x: CGFloat(agent.position.x), y: CGFloat(agent.position.y)) } } } // ここまで今回追加分
アプリを起動すると、鬼がプレイヤーを追い掛けてくるのが分かります。
今回は「GKAgent2D」「GKGoal」「GKBehavior」という3つのクラスを使いました。名前の通り、この3つが「Agents, Goals, and Behaviors」の主な要素です。
GKGoalは1つ1つのルールを表現するクラスです。今回は「GKGoal(toSeekAgent: playerAgent)」というplayerAgentに向かって動くようなものでした。逆にplayerAgentから逃げたり、特定の障害物を避けたりするものなどを作ることもできます。
そしてGKGoalをまとめるのがGKBehaviorクラスです。GKBehaviorは1つ以上のGKGoalを重み付け込みで管理します。今回はGKGoalが1つでしたが、次回以降では複数のGKGoalを使っていこうと思います。
GKAgent2Dはルールに基づいて移動するクラスです。GKAgent2DにGKBehaviorインスタンスを渡すことで、ルールに基づいた行動をするようになります。
今回は「Agents, Goals, and Behaviors」を使ったAIを作ってみましたがいかがでしたか。次回は障害物や味方キャラクターを追加して、さらに複雑な条件で動くAIを作ってみようと思います。
今回作成したコードは、こちらからダウンロードできます。
田町のベンチャーで働くエンジニア。
仕事ではiPhoneアプリの開発やRailsを使ったWebサービス開発を行っている。最近のマイブームはUnityを使った3Dゲーム開発。
Copyright © ITmedia, Inc. All Rights Reserved.