「Rule Systems」とは、今回のような複数の条件を管理するときに使える機能です。条件1つ1つをオブジェクトとして扱い、それらを使ってパラメータの計算が行えます。
条件をオブジェクトとして扱うので、条件の使い回しや条件の動的な変更が行えることが利点です。
それでは、「経過時間を元に鬼の速度を変える」処理を「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以上の値を扱えないためです。
今回は「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(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) } }
今回は、前回までの「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.