iOS GameplayKitのRule SystemsでゲームAIプログラミングはどう変わるのか:ゲームの「敵」キャラで分かる「人工知能」の作り方(3) (2/2 ページ)
iPhone向け鬼ごっこアプリを作りながら人工知能(AI)について学んでいく連載。今回は、さらにゲーム性を持たせるため「経過時間によって鬼の移動速度が変わる」という修正を条件式を使って行い、その条件式をRule Systemsで管理する。
条件式を「Rule Systems」で管理する
「Rule Systems」とは、今回のような複数の条件を管理するときに使える機能です。条件1つ1つをオブジェクトとして扱い、それらを使ってパラメータの計算が行えます。
条件をオブジェクトとして扱うので、条件の使い回しや条件の動的な変更が行えることが利点です。
「Rule Systems」を使った、条件の設定
それでは、「経過時間を元に鬼の速度を変える」処理を「Rule Systems」を使って置き換えてみます。GameSceneを下のように修正してください。
class GameScene: SKScene { // 省略 let system = GKRuleSystem() // 今回追加 override func didMove(to view: SKView) { // 省略 system.add([ GKRule(predicate: NSPredicate(format: "modulus:by:($time, 30) >= 25"), assertingFact: NSString(string: "enemySpeedRate"), grade: 0.3), GKRule(predicate: NSPredicate(format: "$time >= 60"), assertingFact: NSString(string: "enemySpeedRate"), grade: 0.1), GKRule(predicate: NSPredicate(format: "$time >= 120"), assertingFact: NSString(string: "enemySpeedRate"), grade: 0.1), GKRule(predicate: NSPredicate(format: "$time >= 180"), assertingFact: NSString(string: "enemySpeedRate"), grade: 0.1), ]) } // 省略 }
GKRuleSystemクラスのインスタンス作成と、GKRuleSystemへのGKRuleの追加を行っています。
GKRuleSystemクラスはルールを管理するクラスです。このクラスに条件式や値を渡してパラメータを算出します。
GKRuleは1つ1つの条件式に相当します。下の例ですと「time値が180以上のときにenemySpeedRateパラメータに0.1を追加する」という意味になります。
GKRule(predicate: NSPredicate(format: "$time >= 180"), assertingFact: NSString(string: "enemySpeedRate"), grade: 0.1),
パラメータ算出処理
ここまでで、条件の設定を行いました。最後に設定した条件を元にしたパラメータ算出処理を実装します。
GameSceneのupdateメソッドを下記のように修正してください。
class GameScene: SKScene { // 省略 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)) let time = Int(currentTime - startTime) timeLabel.text = "\(time)秒" // 今回修正ここから // var enemySpeedRate: Float = 1.0 // if time % 30 >= 25 { // enemySpeedRate += 3.0 // } // if time >= 60 { // enemySpeedRate += 1.0 // } // if time >= 120 { // enemySpeedRate += 1.0 // } // if time >= 180 { // enemySpeedRate += 4.0 // } system.state["time"] = time system.reset() system.assertFact(NSString(string: "enemySpeedRate"), grade: 0.1) system.evaluate() let enemySpeedRate = system.grade(forFact: NSString(string: "enemySpeedRate")) * 10 // 今回修正ここまで enemyAgents.forEach { $0.maxAcceleration = 30 * enemySpeedRate $0.maxSpeed = 20 * enemySpeedRate } } }
これで「Rule Systems」を使って置き換えは完了です。続けて今回追加した下5行について説明していきます。
system.state["time"] = time system.reset() system.assertFact(NSString(string: "enemySpeedRate"), grade: 0.1) system.evaluate() let enemySpeedRate = system.grade(forFact: NSString(string: "enemySpeedRate")) * 10
1行目と3行目は値のセットとパラメータの初期化です。セットした値を使って4行目でenemySpeedRateを計算しています。そのまま計算すると前回の結果を引き継いでしまうので、2行目で計算結果をリセットしています。
最後に5行目で計算結果を取得しています。最後に「x10」としているのは、gradeが1.0以上の値を扱えないためです。
コラム「条件式をGKRuleインスタンスとして扱うメリット」
今回は「Rule Systems」を使って、if文で書かれた条件式をGKRuleインスタンスとして扱いました。本稿では敵は1種類でしたが、もし敵の種類が増えて条件式を使い回したいときには「Rule Systems」を使うメリットが大きくなります。
例えば敵が3種類いて、「敵1は25〜30秒で動きが速くなる」「敵2は1分ごとに速くなる」「敵3は敵1と敵2の両方の性質を持っている」とします。よくある実装方法だと、敵1・敵2・敵3のそれぞれに対応するクラスを作ってそれぞれのクラスに条件式を記述します。しかし「Rule Systems」で条件式をGKRuleインスタンスとして扱っていれば、「敵1と敵3には“25〜30秒で動きが速くなる”というGKRuleインスタンスを付与、敵2と敵3には“1分ごとに速くなる”というGKRuleインスタンスを付与する」といった具合に条件式の使い回しを行えます。
このように敵の種類が増え、加えて条件式が複雑になってくると「Rule Systems」の利点が大きくなります。
GKRuleはサブクラスを作って複雑な条件にすることもできる
GKRuleはブロックで判定式を渡したりサブクラスを作ったりすることもできるので、複雑な条件にも対応可能です。
GKRule(blockPredicate: { system in guard let time = (system.state["time"] as? NSNumber)?.floatValue else { return false } return time >= 10 }, action: { system in system.assertFact(NSString(string: "enemySpeedRate"), grade: 0.3) }) class MyRule: GKRule { override func evaluatePredicate(in system: GKRuleSystem) -> Bool { guard let time = (system.state["time"] as? NSNumber)?.floatValue else { return false } return time >= 10 } override func performAction(in system: GKRuleSystem) { system.assertFact(NSString(string: "enemySpeedRate"), grade: 0.3) } }
次回は、3Dゲームにおける「Agents, Goals, and Behaviors」の使い方
今回は、前回までの「GKAgent2D」「GKGoal」「GKBehavior」に加えて、「GKRule」「GKRuleSystem」を使って、さらにリアリティーのある動きをゲームAIに加えることができました。今回まででiOS GameplayKitの「Agents, Goals, and Behaviors」でどんなことができるかが大体理解できたかと思います。
今回のソースは、こちらからダウンロードできます。
さて、次回は最終回です。これまでは2Dゲームを題材にしてきましたが、3Dゲームにおける「Agents, Goals, and Behaviors」の使い方を見ていきます。これまで紹介した「GKAgent2D」が「GKAgent3D」に変わり、SpriteKitも3Dゲームのための「SceneKit」に変わりますが、これまで紹介した使い方の応用として見てください。
筆者紹介
杉本裕樹
田町のベンチャーで働くエンジニア。
仕事ではiPhoneアプリの開発やRailsを使ったWebサービス開発を行っている。最近のマイブームはUnityを使った3Dゲーム開発。
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を起動してみましょう。