しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

【GameplayKit】GKRuleSystemで複雑な条件を管理する

GKRuleSystemという複数の条件を管理する機能を使ってみました。

使い方は下の通りです。

  1. GKRuleSystemにGKRuleを追加する
  2. GKRuleSystemにパラメータをセットする
  3. GKRuleSystemの評価メソッド(evaluate)を呼ぶ
  4. 評価の結果を取得する

コードは下の通りです。
評価の結果は、GKRuleの引数のgradeの数字が返ってきます。
system.addがルールの追加、system.state["value1"] = 0がパラメータのセット、system.evaluate()が評価メソッドの呼び出し、system.grade(forFact: NSString(string: "fact1"))が結果の取得になります。

let system = GKRuleSystem()
system.add([
    GKRule(predicate: NSPredicate(format: "$value1 = 0"), assertingFact: NSString(string: "fact1"), grade: 0.9),
    ])
system.state["value1"] = 0
system.reset()
system.evaluate()
print(system.grade(forFact: NSString(string: "fact1"))) // → 0.9

条件式がfalseの場合、結果は0になります。

let system = GKRuleSystem()
system.add([
    GKRule(predicate: NSPredicate(format: "$value1 = 0"), assertingFact: NSString(string: "fact1"), grade: 0.9),
    ])
system.state["value1"] = 1 // → 今回は1をセット
system.reset()
system.evaluate()
print(system.grade(forFact: NSString(string: "fact1"))) // → 0.0

GKRuleは複数渡す事ができます。
複数の条件を満たす場合、結果は全ての条件の和になります。

let system = GKRuleSystem()
system.add([
    GKRule(predicate: NSPredicate(format: "$value1 = 0"), assertingFact: NSString(string: "fact1"), grade: 0.1),
    GKRule(predicate: NSPredicate(format: "$value2 = 1"), assertingFact: NSString(string: "fact1"), grade: 0.2),
    ])
system.state["value1"] = 0
system.state["value2"] = 1
system.reset()
system.evaluate()
print(system.grade(forFact: NSString(string: "fact1"))) // → 0.3

しかし結果は1.0以上にはならないので注意が必要です。
和が1.0以上の場合は、1.0が返ってきます。

let system = GKRuleSystem()
system.add([
    GKRule(predicate: NSPredicate(format: "$value1 = 0"), assertingFact: NSString(string: "fact1"), grade: 1.1),
    GKRule(predicate: NSPredicate(format: "$value2 = 1"), assertingFact: NSString(string: "fact1"), grade: 2.2),
    ])
system.state["value1"] = 0
system.state["value2"] = 1
system.reset()
system.evaluate()
print(system.grade(forFact: NSString(string: "fact1"))) // → 1.0

GKRuleの初期化のassertingFactをretractingFactに変えると、条件を満たした時にgradeを減算するようになります。
これも和の時と同じように0.0以下にはなりません。

let system = GKRuleSystem()
system.add([
    GKRule(predicate: NSPredicate(format: "$value1 = 0"), assertingFact: NSString(string: "fact1"), grade: 0.5),
    GKRule(predicate: NSPredicate(format: "$value2 = 1"), retractingFact: NSString(string: "fact1"), grade: 0.2),
    ])
system.state["value1"] = 0
system.state["value2"] = 1
system.reset()
system.evaluate()
print(system.grade(forFact: NSString(string: "fact1"))) // → 0.3

GKRuleはNSPredicateではなくブロックで評価をする事もできます。

GKRule(blockPredicate: { system in return true }, action: { system in print("条件を満たした") })

GKRuleのサブクラスを作って、そこに評価式を書くこともできます。
複雑な条件の時はこれを使うのも良さそうです。

class MyRule: GKRule {
    override func evaluatePredicate(in system: GKRuleSystem) -> Bool {
        return true
    }
    
    override func performAction(in system: GKRuleSystem) {
        print("条件を満たした")
    }
}