連載
SwiftのGameplayKitでAIに追いかける、避ける、逃げる処理を追加するには:ゲームの「敵」キャラで分かる「人工知能」の作り方(2)(3/4 ページ)
iPhone向け鬼ごっこアプリを作りながら人工知能(AI)について学んでいく連載。今回は、SwiftのGameplayKitでAIの敵キャラに、追いかける、避ける、逃げる処理を追加する方法を解説する。
AIに「逃げる」動きを実装
次は、AIに「逃げる」動きを実装します。通常の鬼ごっこのルールからは外れますが、鬼を倒すことのできる味方を追加しようと思います。鬼が味方から逃げる処理実装を通して、AIに「逃げる」動作を追加します。
味方キャラを追加する
最初に画面上に味方を配置します。GameSceneを以下のように修正してください。
class GameScene: SKScene { // 省略 var sweeper = SKShapeNode(circleOfRadius: 10) // 今回追加 var sweeperAgent = GKAgent2D() // 今回追加 override func didMove(to view: SKView) { player.fillColor = UIColor(red: 0.93, green: 0.96, blue: 0.00, alpha: 1.0) player.physicsBody = SKPhysicsBody(circleOfRadius: 10) addChild(player) // ここから今回追加 sweeper.fillColor = UIColor(red: 255, green: 0.65, blue: 0.02, alpha: 1.0) sweeper.physicsBody = SKPhysicsBody(circleOfRadius: 10) addChild(sweeper) sweeperAgent.maxAcceleration = 50 sweeperAgent.maxSpeed = 80 sweeperAgent.delegate = self agentSystem.addComponent(sweeperAgent) // ここまで今回追加 createObstacles() setCreateEnemyTimer() physicsWorld.gravity = CGVector() } // 省略 }
画面上に味方(緑の丸)を追加しました。
味方が鬼を追いかける処理、味方と鬼が衝突したら鬼が消える処理
次は、「味方が鬼を追いかける処理」「味方と鬼が衝突したら鬼が消える処理」を実装します。GameSceneを以下のように修正してください。
class GameScene: SKScene { // 省略 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), GKGoal(toAvoid: obstacles, maxPredictionTime: 10) ], andWeights: [NSNumber(value: 1), NSNumber(value: 50)]) agentSystem.addComponent(anemyAgent) enemyAgents.append(anemyAgent) let goals = enemyAgents.map { GKGoal(toSeekAgent: $0) } // 今回追加 sweeperAgent.behavior = GKBehavior(goals: goals + [GKGoal(toAvoid: obstacles, maxPredictionTime: 10)]) // 今回追加 } 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 !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 } } // ここから今回追加 for (i, enemy) in enemies.enumerated() { let dx = enemy.position.x - sweeper.position.x let dy = enemy.position.y - sweeper.position.y if sqrt(dx*dx + dy*dy) < enemy.frame.width / 2 + sweeper.frame.width / 2 + 5 { enemy.removeFromParent() enemies.remove(at: i) agentSystem.removeComponent(enemyAgents[i]) enemyAgents.remove(at: i) let goals = enemyAgents.map { GKGoal(toSeekAgent: $0) } sweeperAgent.behavior = GKBehavior(goals: goals + [GKGoal(toAvoid: obstacles, maxPredictionTime: 10)]) 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)) } if let agent = agent as? GKAgent2D, agent == sweeperAgent { sweeper.position = CGPoint(x: CGFloat(agent.position.x), y: CGFloat(agent.position.y)) // 今回追加 } } }
これで味方が鬼を追いかけるようになりました。
鬼が味方から逃げる処理(GameplayKitを使う場合)
最後に、鬼が味方から逃げるようにします。GameSceneのcreateEnemyメソッドを以下のように修正します。
class GameScene: SKScene { // 省略 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), GKGoal(toAvoid: obstacles, maxPredictionTime: 10), GKGoal(toFleeAgent: sweeperAgent) // 今回追加 ], andWeights: [NSNumber(value: 5), NSNumber(value: 50), NSNumber(value: 1)]) // 今回修正 agentSystem.addComponent(anemyAgent) enemyAgents.append(anemyAgent) let goals = enemyAgents.map { GKGoal(toSeekAgent: $0) } sweeperAgent.behavior = GKBehavior(goals: goals + [GKGoal(toAvoid: obstacles, maxPredictionTime: 10)]) } // 省略 }
今回は下記の修正をすることで、鬼が味方キャラから逃げるようになりました。
anemyAgent.behavior = GKBehavior(goals: [ GKGoal(toSeekAgent: playerAgent), GKGoal(toAvoid: obstacles, maxPredictionTime: 10), GKGoal(toFleeAgent: sweeperAgent) ], andWeights: [NSNumber(value: 5), NSNumber(value: 50), NSNumber(value: 1)])
今回追加したのは4行目の「GKGoal(toFleeAgent: sweeperAgent)」という箇所です。このGKGoal(振る舞いのルール)は「特定のAgentから逃げる」というルールになります。引数にsweeperAgent(味方キャラ)を渡すことで、鬼に「味方キャラから逃げる」という振る舞いを追加しました。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- iOS 9の最新機能で自動ルート検索を簡単にゲームに組み込む
iPhoneゲームをSwift言語で作成してみたいという初心者向けにiOSのゲームフレームワークを使った作り方を一から解説する入門連載。今回は、敵の動きを改善する。GameplayKitのPathfindingを使ったルートの自動探索の使い方についてSpriteKitのSKActionでの実装と比べて解説。 - iOS 10 SDKの新機能SiriKit、音声認識、iMessage拡張を自作アプリに生かすには
iOS 10で開発者にAPIが解放されたと話題の音声アシスタント「Siri」。その実態はどんなものなのか。SiriKit、Speech/Messages Frameworkの使い方と併せて、簡単なアプリを作りながら解説します。 - スマホ世代でも分かるMacの基本的な使い方&Xcodeをインストールする手順
本連載では、これからプログラミングやiPhoneアプリ開発を始めてみたい方を対象に、開発に必要な知識を基礎から解説していきます。今回は、プログラミングを学び始める前に、まずはMacの基本的な使い方を学び、Xcodeをインストールし、Playgroundを起動してみましょう。