SwiftのGameplayKitでAIに追いかける、避ける、逃げる処理を追加するには:ゲームの「敵」キャラで分かる「人工知能」の作り方(2)(2/4 ページ)
iPhone向け鬼ごっこアプリを作りながら人工知能(AI)について学んでいく連載。今回は、SwiftのGameplayKitでAIの敵キャラに、追いかける、避ける、逃げる処理を追加する方法を解説する。
AIに「避ける」動作を追加する(GameplayKitを使わない場合)
次は本項の目的である、AIへの「避ける」動作の追加を行います。最初にGameplayKitを使わない場合の実装方法を考えてみましょう。
例として下のような配置のときの鬼の動きを検討します。
鬼を動かす流れとしては、「鬼からプレイヤーへの最短経路を計算」→「それに沿って鬼を動かす」という流れになります。「それに沿って鬼を動かす」部分についてはSKActionで実装できるので、「鬼からプレイヤーへの最短経路を計算」について詳しく見ていきます。
「鬼からプレイヤーへの最短経路を計算」の方法としては以下のような手順が考えられます。
- 障害物に沿った点を配置する
- 1.で作った点・プレイヤー・鬼をつなぐ
- 2.で作成した経路から最短経路を探す
1. 障害物に沿った点を配置する
まずは鬼の通るルート候補を作ります。そのために障害物に沿って以下のような点を配置します。ここで配置した点が鬼の通過する座標になります。
2. 1で作った点・プレイヤー・鬼をつなぐ
次は、先ほど作った点・プレイヤー・鬼を繋いで線を作ります。この線が鬼からプレイヤーへの最短経路候補になります。
この時障害物と重なる線は作らないようにします。
3. 2で作成した経路から最短経路を探す
最後に作成した最短経路候補の中から最短経路を探します。一番単純な方法としては、全てのルートの距離を計算して最短距離を導く方法が考えられます。
普通に実装すると無限ループするため、「一度通った道は通らない」などのルールを追加する必要があります。
以上が障害物があるときの鬼の最短ルートの取得方法です。実装もやや難解な上、パフォーマンスチューニングの手間も掛かりそうです。
しかしGameplayKitの「Agents, Goals, and Behaviors」を使えばこれらの処理を簡単に実現できます。次項ではGameplayKitを使った場合の実装方法を解説します。
AIに「避ける」動作を追加する(GameplayKitを使った場合)
まずは「Agents, Goals, and Behaviors」で使うための障害物情報を作成します。GameScene.swiftのcreateObstaclesメソッドを以下のように置き換えてください。
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]()
var obstacles = [GKCircleObstacle]() // 今回追加
// 省略
// ここから今回置き換え
func createObstacles() {
guard let viewFrame = view?.frame else {
return
}
while obstacles.count < 5 {
let point = CGPoint(
x: CGFloat(arc4random_uniform(UInt32(viewFrame.width))) - viewFrame.width / 2,
y: CGFloat(arc4random_uniform(UInt32(viewFrame.height))) - viewFrame.height / 2)
let radius = Float(arc4random_uniform(50) + 50)
// 障害物かPlayerが衝突していたら設置しない
let isObstacleOverlapped = obstacles.contains {
let dx = (Float(point.x) - $0.position.x)
let dy = (Float(point.y) - $0.position.y)
if sqrt(dx*dx + dy*dy) < $0.radius + radius {
return true
}
return false
}
let dx = point.x - player.position.x
let dy = point.y - player.position.y
let isPlayerOverlapped = sqrt(dx*dx + dy*dy) < CGFloat(radius) + player.frame.width
if isObstacleOverlapped || isPlayerOverlapped {
continue
}
let obstacleNode = SKShapeNode(circleOfRadius: CGFloat(radius))
obstacleNode.fillColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1.0)
obstacleNode.position = point
obstacleNode.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(radius))
obstacleNode.physicsBody?.pinned = true
addChild(obstacleNode)
let obstacle = GKCircleObstacle(radius: radius)
obstacle.position = float2(x: Float(point.x), y: Float(point.y))
obstacles.append(obstacle)
}
}
// ここまで今回置き換え
// 省略
}
障害物が配置できたので、AIに障害物を避けるルールを追加します。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 = 200 // 今回修正、加速度を上げることで障害物を避けやすくする
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)
}
// 省略
}
これで鬼が障害物を回避して移動するようになりました。
障害物を避ける処理はcreateEnemyメソッドの下記の部分です。
anemyAgent.behavior = GKBehavior(goals: [
GKGoal(toSeekAgent: playerAgent),
GKGoal(toAvoid: obstacles, maxPredictionTime: 10),
], andWeights: [NSNumber(value: 1), NSNumber(value: 50)])
anemyAgentが鬼の動きを管理するクラスで、それにGKBehaviorという振る舞いを表すクラスを渡しています。そうすることで、鬼(anemyAgent)はGKBehaviorに沿った動きをするようになります。
GKBehaviorがどのような振る舞いをするかは、「GKGoal」というクラスで設定します。今回はプレイヤーを追いかける「GKGoal(toSeekAgent: playerAgent)」というルールと、障害物を避ける「GKGoal(toAvoid: obstacles, maxPredictionTime: 10)」を設定したことで、障害物を避けつつプレイヤーを追いかけるようになりました。
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を起動してみましょう。






