メインコンテンツまでスキップ

ビジュアル条件ツリー

イベントアクションが実行されるべきかどうかを制御するために、複雑なブール論理を視覚的に構築します。直感的なインターフェースを通じて洗練された実行時チェックを作成—コーディング不要。

Visual Condition Tree


🎯 概要

ビジュアル条件ツリーは、イベントアクションを実行する前に実行時条件を評価するロジックゲートシステムです。

解決する問題

従来のアプローチ(分散したロジック):

// ロジックがスクリプトに埋め込まれ、変更が困難
if (damageInfo.amount > 20 &&
(damageInfo.isCritical || damageInfo.type == DamageType.Fire) &&
playerController.Health < 50 &&
gameManager.IsInCombat()) {
// アクションを実行
}

ビジュアルアプローチ:

Visual Condition Tree


例を構築

イベント: OnPlayerDamaged

イベント型:

  • GameEvent<GameObject, DamageInfo> (GameObject sender)
  • GameEvent<PlayerStats, DamageInfo> (カスタムsender)

データ構造:

// ダメージ型列挙
public enum DamageType {
Physical,
Fire,
Void
}

// ダメージ情報ペイロード
public class DamageInfo {
public float amount; // ダメージ値
public bool isCritical; // クリティカルヒットフラグ
public DamageType type; // ダメージタイプ
public Vector3 hitPoint; // 衝撃位置
public string attacker; // 攻撃者名
}

// カスタムsender型(GameObjectの代替)
public class PlayerStats {
public string playerName;
public int level;
public int factionId;
}

目標: 特定の条件下でプレイヤーが重大なダメージを受けたときに警告エフェクトをトリガー。


主な利点

機能利点
🎨 ビジュアル構築デザイナーがコードなしでロジックを作成
🚀 高性能Expression Treeにコンパイル(ゼロリフレクション)
🔄 再利用可能同じ条件がすべてのイベントアクションに適用
🧪 ライブテストインスペクターで値を調整し、結果をすぐに確認
🔒 型安全型互換性を自動検証

🏗️ ツリー構造

条件ツリーは2つのノードタイプから構築されます:

Node Types

🌳 グループノード

AND/ORロジックを使用して複数の条件を結合します。

ロジックタイプ:

すべての子条件がTRUEと評価される必要があります。

ビジュアル: 🟢 緑色の枠線

用途: 「すべての要件を満たす必要がある」

ロジック切り替え: ロジックボタン(AND/OR)をクリックして切り替えます。

ネスト: グループは他のグループを含むことができます—無制限の深さで複雑なロジックを構築。

ノードを追加: 各グループの**+ Conditionまたは+ Group**ボタンを使用します。


⚖️ 比較ノード

比較ノードは、イベントロジックの基本的な構成要素です。各ノードは、イベントアクションが続行すべきかどうかを判断するために、単一のブールチェック(True/False)を実行します。

🏗️ 比較の構造

すべてのノードは標準的な三部構造に従っており、複雑なロジックを一目で読みやすくしています。

[ 🟦 ソース(左オペランド) ] [ 演算子 ] [ 🟧 ターゲット(右オペランド) ]

実用例: ヒットが十分に強力な場合にのみトリガーするイベントを想像してください: Argument.amount > 20.0

  • 🔍 ソース: Argument.amount — GameEvent<float>によって渡される生のダメージ値
  • 📐 演算子: > — 論理ルール(より大きい)
  • 🎯 ターゲット: 20.0 — 比較する定数閾値または別の変数

👓 表示モード

エディターUIはニーズに適応し、可読性精密制御のバランスを取ります。

表示モードビジュアルスタイル最適な用途...
📖 折りたたみ要約テキスト(例: Health < 50)複雑なロジックチェーンの概要。
🛠️ 展開詳細エディター(ドロップダウン、フィールド)特定のパラメータとソースの変更。
操作ヒント

任意の比較ブロックをクリックするだけで、これら2つのビュー間を切り替えられます。これにより、設定に深く入る能力を保持しながら、ワークスペースをクリーンに保つことができます。


📝 構造設定

📌 ソース

🧬 イベント引数(データペイロード)

Argumentシステムにより、イベントのデータペイロードを詳しく調べて、条件とアクションのための特定の値を抽出できます。

利用可能性

データアクセスは型付きイベント専用です: GameEvent<T>またはGameEvent<TSender, TArgs>

🔢 シングルパラメータイベント

シグネチャ: GameEvent<DamageInfo>

イベントが単一のオブジェクトを運ぶ場合、オブジェクト自体またはそのパブリックメンバーのいずれかにアクセスできます。

データ構造例:

📦 (this Argument)      ➔ 完全なDamageInfoオブジェクト
├── 🔢 amount ➔ float
├── ✅ isCritical ➔ bool
├── 🏷️ type ➔ DamageType (Enum)
├── 📍 hitPoint ➔ Vector3
└── 👤 attacker ➔ string

👥 Senderイベント(コンテキスト認識)

Senderイベントは、データアクセスのための2つの異なるルートを提供します: Sender(誰)とArgument(何)。

🎮 ケースA: GameObject Sender

シグネチャ: GameEvent<GameObject, DamageInfo>

ルートパス例データ型
SenderSender.Transform.positionVector3
ArgumentArgument.amountfloat

ビジュアル階層:

👥 Sender
├── 🆔 tag ➔ string
├── 🟢 activeSelf ➔ bool
└── 📐 Transform
├── 📍 position ➔ Vector3
└── 📂 childCount➔ int
🔢 Argument
├── 🔢 amount ➔ float
└── ✅ isCritical ➔ bool
🛡️ ケースB: カスタムC# Sender(高度)

シグネチャ: GameEvent<PlayerStats, DamageInfo>

🚀 なぜ特別か: 従来のシステムとは異なり、GameObjectに縛られません。疎結合でロジック優先のアーキテクチャのために、任意のPure C#クラスをSenderとして使用できます。

  • 💎 純粋なロジック — MonoBehaviourでないクラスで動作します。
  • 🌐 ネットワーク対応 — PlayerDataまたはNetworkAgent同期に最適。
  • 🤖 AIエージェント — シーン依存なしで内部状態を追跡。

🧭 深いプロパティアクセス

精密ナビゲーション。 高性能リフレクションで5レベルの深さまでネストされた構造をナビゲート。

例: 方向チェック

  • パス: Argument.hitPoint.normalized.x
  • 条件: > 0.5
  • 結果: 🎯 「ヒットは右側から来ました。」

ブレッドクラムロジック: Argument (DamageInfo) ➔ hitPoint (Vector3) ➔ normalized (Vector3) ➔ x (float)


📋 サポートされている型

システムは以下の型を自動的にサポートします:

カテゴリサポートされている型
プリミティブint、float、double、long、bool、string
数学Vector2、Vector3、Quaternion、Color
UnityGameObject、Transform、Component参照
ロジックEnum(ドロップダウン付き)、[Serializable] C#クラス
プロのヒント: カスタムクラス

[Serializable]クラス内の任意のパブリックフィールドまたはプロパティは、ディープリンクピッカーで自動的に公開されます。

📌 演算子

📐 利用可能な演算子

数値(6)

数値用(int、float、double、long):

演算子シンボル
等しい==Health == 100
等しくない!=Health != 0
より大きい>Damage > 20
より小さい<Health < 50
以上>=Level >= 10
以下<=Shield <= 0

自動変換: 互換性のある数値型は自動的に変換されます(int ↔ float)。


文字列(4)

テキスト値用:

演算子シンボル
等しい==Name == "Hero"
等しくない!=Tag != "Enemy"
で始まるStarts WithName Starts With "Player_"
で終わるEnds WithFile Ends With ".png"
含むContains (⊃)Message Contains "error"

⚠️ 大文字小文字を区別: "Hero" ≠ "hero"


Enumサポート

ドロップダウン選択付きの完全な列挙サポート!

:

public enum DamageType { Physical, Fire, Void }

条件ツリー内:

ソース: Argument.type (DamageType)
演算子: ==
ターゲット: DamageType.Fire (ドロップダウンにPhysical/Fire/Voidを表示)

リストと共に:

Argument.type In List [Fire, Void]
結果: typeがDamageType.FireまたはDamageType.Voidの場合TRUE

サポートされている演算子: ==!=In List (∈)


コレクション(1)

リスト/配列メンバーシップをチェック:

演算子シンボル目的
In List値がリストに存在するかをチェック

構造:

ソース: 単一値
演算子: In List (∈)
ターゲット: リスト/配列(一致する型)

:

Argument.attacker In List ["Dragon", "Demon", "Lich"]
Player.Level In List [10, 20, 30, 40, 50]
Argument.type In List [Fire, Void]
📌 ターゲット

🧬 イベント引数(データペイロード)

ソースと同じ

ソースと同様に、具体的な詳細についてはソースの関連する構成説明を参照してください


🎨 型検証

システムは型の互換性を自動的に検証します。

有効な比較:

✅ int == int
✅ float > int (自動変換)
✅ string Contains string
✅ DamageType == Fire (enum)
✅ int In List<int>

無効な比較:

❌ string > int (互換性のない型)
❌ bool Contains string (無意味)
❌ float In List<string> (型の不一致)

ビジュアルフィードバック: 互換性のない型に赤いアウトライン+警告テキスト。


🧩 Boolメソッド vs ビジュアルツリー

条件を構築する2つのアプローチ—それぞれをいつ使用するか?

アプローチ1: Boolメソッド

最適: 複雑な複数ステップロジック。

:

public bool IsInDangerZone() {
bool lowHealth = Health < 20;
bool noShield = Shield == 0;
bool hasEnemies = Physics.OverlapSphere(
transform.position, 10f, enemyLayer
).Length > 0;

return lowHealth && noShield && hasEnemies;
}

ツリー内: Player.IsInDangerZone() == true

長所:

  • 複雑さをカプセル化
  • Physics、raycastsを使用可能
  • ユニットテスト可能
  • コードの再利用可能

短所:

  • C#コーディングが必要
  • デザイナーが変更不可

アプローチ2: ビジュアルツリー

最適: デザイナーが制御すべきシンプルなチェック。

:

長所:

  • コーディング不要
  • デザイナーフレンドリー
  • ビジュアル表現
  • 迅速な反復

短所:

  • Physics/アルゴリズムを使用不可
  • 大きなツリーは複雑化

ハイブリッドアプローチ(推奨)

最適な結果のために両方を組み合わせます:

ガイドライン:

  • ビジュアルツリー: 閾値、enum、シンプルなプロパティ
  • Boolメソッド: Physicsクエリ、複雑なアルゴリズム、クロスシステムチェック

🔄 ドラッグ&並べ替え

実行順序を変更: 任意の条件の左端にあるハンドル(☰)をドラッグします。

順序が重要な理由:

ANDグループ: 順序は結果に影響しません(すべてが合格する必要がある)。

ORグループ: 順序は短絡評価に影響します(最初のTRUEで停止)。

最適化例:

❌ 遅い:
ORグループ
├─ ExpensivePhysicsCheck() ← 最初に実行(遅い!)
└─ SimpleBoolCheck ← 実行されない可能性

✅ 速い:
ORグループ
├─ SimpleBoolCheck ← 最初に実行(速い!)
└─ ExpensivePhysicsCheck() ← 必要な場合のみ

より良いパフォーマンスのために、ORグループで安価なチェックを最初に配置します。


🚀 パフォーマンス

コンパイルプロセス

一度だけのコスト(シーンロード):

ビジュアルツリー → Expression Tree → ILコード → コンパイル済みラムダ

実行時実行:

イベント発火 → コンパイル済みラムダを呼び出し → TRUE/FALSEを返す

ベンチマーク: 複雑なネストされた条件が約0.001ms(1マイクロ秒)で実行されます。


なぜ速いのか

ゼロリフレクション: 手書きC#のような直接コンパイルアクセス。

Expression Tree: システムが初期化時に最適化されたILコードを生成します。

❌ 従来: GetComponent() + GetField() + Invoke() チェックごと
✅ このシステム: コンパイル済みラムダを介した直接プロパティアクセス

結果: フレームごとに数百のイベントが発火しても無視できるオーバーヘッド。


🧹 ツリー管理

  • 有効/無効: すべての条件をバイパスするためにチェックボックスを切り替えます(常にTRUE)。

  • ツリーをリセット: 「Reset Tree」ボタンをクリックして、すべてのノードをクリアして最初からやり直します。

  • 折りたたみ/展開: 比較ブロックをクリックして、要約ビューと詳細ビュー間を切り替えます。


❓ トラブルシューティング

条件が常にFalseを返す

チェックリスト:

  • ✅ 「Enable Conditions」トグルがチェックされているか?
  • ✅ 赤い型不一致警告はあるか?
  • ✅ シーンタイプ参照はまだ有効か(破棄されていない)?
  • ✅ boolメソッドは期待値を返すか?(Debug.Logを追加)

プロパティがドロップダウンにない

イベント引数の場合:

  • publicフィールドまたはプロパティである必要がある
  • サポートされている型である必要がある

シーンタイプの場合:

  • GameObjectがエディター時にシーンに存在する必要がある
  • Componentが有効である必要がある
  • プロパティがpublicである必要がある
  • メソッドは: boolを返す、ゼロパラメータ、public、インスタンス(staticではない)

実行時オブジェクトの場合: シーンタイプの代わりにイベント引数を使用。


変更が保存されない

一般的な原因:

  • 複数のBehaviorウィンドウが開いている(重複を閉じる)
  • 編集中のスクリプトコンパイル(完了まで待つ)
  • UnityがSerializedPropertyの変更を適用しなかった(閉じる前に待つ)

📖 使用される場所

ビジュアル条件ツリーシステムは2つのコンテキストで表示されます:

1. イベントビヘイビアGame Event Behavior

イベントアクションが実行されるかどうかを制御:

イベント発火 → 条件をチェック → アクションを実行/スキップ

2. フローノード → フローノード構成 (将来のドキュメント)

フローノードが実行されるかどうかを制御:

フローがノードに到達 → 条件をチェック → ノードを実行/スキップ

両方ともまったく同じ条件ツリーシステムを使用します。


ベストプラクティス

シンプルなチェック: 閾値、enum、基本比較にはビジュアルツリーを使用

複雑なロジック: Physics、アルゴリズム、複数ステップチェックにはBoolメソッドを使用

最適なアプローチ: 両方を組み合わせる—シンプルにはビジュアル、複雑にはメソッド

パフォーマンス: 短絡最適化のためにORグループで安価なチェックを最初に配置