Skip to main content

Visual Condition Tree

GES Β· Visual Workflow

Build a visual condition gate from event arguments, scene values, random rolls, and constants without writing code.

For developers who want runtime gating on their events and prefer a visual editor over hand-written if statements.

Add an AND or OR group to check runtime values before an action or flow step runs.

Visual Condition Tree

The group in the image contains separate comparison blocks. Open a block to choose the values and operator, then use the group to decide how those comparisons combine.


Overview​

The Visual Condition Tree is a logic gate system that evaluates runtime conditions before executing event actions.

The Problem It Solves​

Traditional Approach (scattered logic):

// Logic buried in scripts, hard to modify
if (damageInfo.amount > 20 &&
(damageInfo.isCritical || damageInfo.type == DamageType.Fire) &&
playerController.Health < 50 &&
gameManager.IsInCombat()) {
// Execute actions
}

Visual Approach:

Visual Condition Tree


Build an example​

Event: OnPlayerDamaged

Event Types:

  • GameObjectDamageInfoGameEvent (GameObject sender)
  • PlayerStatsDamageInfoGameEvent (Custom sender)

Data Structures:

// Damage type enumeration
public enum DamageType {
Physical,
Fire,
Void
}

// Damage information payload
public class DamageInfo {
public float amount; // Damage value
public bool isCritical; // Critical hit flag
public DamageType type; // Damage type
public Vector3 hitPoint; // Impact location
public string attacker; // Attacker name
}

// Custom sender type (alternative to GameObject)
public class PlayerStats {
public string playerName;
public int level;
public int factionId;
}

Goal: Trigger warning effects when player takes significant damage under specific conditions.


Key Benefits​

FeatureBenefit
🎨 Visual BuildingDesigners create logic without code
πŸš€ AOT-Safe RuntimeUses generated condition evaluators on IL2CPP
πŸ”„ ReusableSame condition applies to all event actions
πŸ§ͺ Live TestingTweak values in Inspector, see results immediately
πŸ”’ Type-SafeAuto-validates type compatibility

Tree Structure​

The condition tree is built from two node types:

Node Types

Group Nodes​

Combine multiple conditions using AND/OR logic.

Logic Types:

All child conditions must evaluate to TRUE.

Visual: 🟒 Green border

Use: "Must satisfy ALL requirements"

Toggle Logic: Click the logic button (AND/OR) to switch.

Nesting: Groups can contain other groupsβ€”build complex logic with unlimited depth.

Add Nodes: Use + Condition or + Group buttons on each group.


Comparison Nodes​

Comparison Nodes are the fundamental building blocks of your event logic. Each node performs a single boolean check (True/False) to determine if the event actions should proceed.

Anatomy of a Comparison​

Every node follows a standard tripartite structure, making complex logic easy to read at a glance.

[ 🟦 Source(Left Operand) ] [ Operator ] [ 🟧 Target (Right Operand) ]

Practical Example: Imagine an event that only triggers if a hit is powerful enough: Argument.amount > 20.0

  • πŸ” Source: Argument.amount β€” The raw damage value passed by the SingleGameEvent
  • πŸ“ Operator: > β€” The logical rule (Greater Than)
  • 🎯 Target: 20.0 β€” The constant threshold or another variable to compare against

View Modes​

The editor UI adapts to your needs, balancing readability with precision control.

View ModeVisual StyleBest For...
πŸ“– CollapsedSummary Text (e.g., Health < 50)Quick overview of complex logic chains.
πŸ› οΈ ExpandedDetailed Editor (Dropdowns, Fields)Modifying specific parameters and sources.
Interaction Hint

Simply Click on any comparison block to toggle between these two views. This allows you to keep your workspace clean while retaining the ability to deep-dive into settings.


Structure Configuration​

πŸ“Œ Source

Event Argument (Data Payload)​

The Argument system allows you to drill down into the event's data payload to extract specific values for conditions and actions.

Availability

Data access is exclusive to typed events: GameEvent<T> or GameEvent<TSender, TArgs>.

Single Parameter Events​

Signature: DamageInfoGameEvent

When an event carries a single object, you can access the object itself or any of its public members.

Data Structure Example:

πŸ“¦ (this Argument) βž” Full DamageInfo object
β”œβ”€β”€ πŸ”’ amount βž” float
β”œβ”€β”€ βœ… isCritical βž” bool
β”œβ”€β”€ 🏷️ type βž” DamageType (Enum)
β”œβ”€β”€ πŸ“ hitPoint βž” Vector3
└── πŸ‘€ attacker βž” string

Sender Events (Context-Aware)​

Sender events provide two distinct roots for data access: Sender (Who) and Argument (What).

Case A: GameObject Sender​

Signature: GameObjectDamageInfoGameEvent

RootPath ExampleData Type
SenderSender.Transform.positionVector3
ArgumentArgument.amountfloat

Visual Hierarchy:

πŸ‘₯ Sender
β”œβ”€β”€ πŸ†” tag βž” string
β”œβ”€β”€ 🟒 activeSelf βž” bool
└── πŸ“ Transform
β”œβ”€β”€ πŸ“ position βž” Vector3
└── πŸ“‚ childCountβž” int
πŸ”’ Argument
β”œβ”€β”€ πŸ”’ amount βž” float
└── βœ… isCritical βž” bool
Case B: Custom C# Sender (Advanced)​

Signature: PlayerStatsDamageInfoGameEvent

πŸš€ Why it's special: Unlike traditional systems, you are not tied to GameObjects. Use any Pure C# Class as a sender for decoupled, logic-first architecture.

  • πŸ’Ž Pure Logic β€” Works with non-MonoBehaviour classes.
  • 🌐 Network Ready β€” Ideal for PlayerData or NetworkAgent sync.
  • πŸ€– AI Agents β€” Track internal state without scene dependencies.

Deep Property Access​

Precision Navigation. Navigate nested structures up to 5 levels deep with high-performance reflection.

Example: Directional Check

  • Path: Argument.hitPoint.normalized.x
  • Condition: > 0.5
  • Result: 🎯 "Hit came from the right side."

Breadcrumb Logic: Argument (DamageInfo) βž” hitPoint (Vector3) βž” normalized (Vector3) βž” x (float)


Supported Types​

The system automatically supports the following types:

CategorySupported Types
Primitivesint, float, double, long, bool, string
MathVector2, Vector3, Quaternion, Color
UnityGameObject, Transform, Component references
LogicEnums (with dropdowns), [Serializable] C# Classes
Pro Tip: Custom Classes

Any public Field or Property in a [Serializable] class is automatically exposed in the deep-link picker.

πŸ“Œ Operator

Available Operators​

Numeric (6)

For numbers (int, float, double, long):

OperatorSymbolExample
Equals==Health == 100
Not Equals!=Health != 0
Greater>Damage > 20
Less<Health < 50
Greater Or Equal>=Level >= 10
Less Or Equal<=Shield <= 0

Auto-Conversion: Compatible numeric types convert automatically (int ↔ float).


String (5)

For text values:

OperatorSymbolExample
Equals==Name == "Hero"
Not Equals!=Tag != "Enemy"
Starts WithStarts WithName Starts With "Player_"
Ends WithEnds WithFile Ends With ".png"
ContainsContains (βŠƒ)Message Contains "error"

⚠️ Case-sensitive: "Hero" β‰  "hero"


Enum Support

Full enumeration support with dropdown selection!

Example:

public enum DamageType { Physical, Fire, Void }

In Condition Tree:

Source: Argument.type (DamageType)
Operator: ==
Target: DamageType.Fire (dropdown shows Physical/Fire/Void)

With Lists:

Argument.type In List [Fire, Void]
Result: TRUE if type is DamageType.Fire OR DamageType.Void

Supported Operators: ==, !=, In List (∈)


Collection (1)

Check list/array membership:

OperatorSymbolPurpose
In List∈Check if value exists in list

Structure:

Source: Single value
Operator: In List (∈)
Target: List/Array (matching type)

Examples:

Argument.attacker In List ["Dragon", "Demon", "Lich"]
Player.Level In List [10, 20, 30, 40, 50]
Argument.type In List [Fire, Void]
πŸ“Œ Target

Event Argument (Data Payload)​

Same as Source

Works the same as Source. See the Source section above for configuration details.


Type Validation​

The system automatically validates type compatibility.

Valid Comparisons:

βœ… int == int
βœ… float > int (auto-converts)
βœ… string Contains string
βœ… DamageType == Fire (enum)
βœ… int In List<int>

Invalid Comparisons:

❌ string > int (incompatible types)
❌ bool Contains string (meaningless)
❌ float In List<string> (type mismatch)

Visual Feedback: Red outline + warning text on incompatible types.


Bool Methods vs Visual Tree​

Two approaches to building conditionsβ€”when to use each?

Approach 1: Bool Methods​

Best for: Complex multi-step logic.

Example:

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;
}

In Tree: Player.IsInDangerZone() == true

Pros:

  • Encapsulates complexity
  • Can use Physics, raycasts
  • Unit testable
  • Code reusable

Cons:

  • Requires C# coding
  • Designers can't modify

Approach 2: Visual Tree​

Best for: Simple checks designers should control.

Example:

Pros:

  • No coding needed
  • Designer-friendly
  • Visual representation
  • Quick iteration

Cons:

  • Can't use Physics/algorithms
  • Large trees get complex

Combine both for optimal results:

Guideline:

  • Visual Tree: Thresholds, enums, simple properties
  • Bool Methods: Physics queries, complex algorithms, cross-system checks

Drag & Reorder​

Change execution order: Drag the handle (☰) on the left edge of any condition.

Why Order Matters:

AND Groups: Order doesn't affect result (all must pass).

OR Groups: Order affects short-circuit evaluation (stops at first TRUE).

Optimization Example:

❌ Slow:
OR Group
β”œβ”€ ExpensivePhysicsCheck() ← Runs first (slow!)
└─ SimpleBoolCheck ← May never run

βœ… Fast:
OR Group
β”œβ”€ SimpleBoolCheck ← Runs first (fast!)
└─ ExpensivePhysicsCheck() ← Only if needed

Put cheap checks first in OR groups for better performance.


Performance​

Bake and Execution Process​

Editor bake

Visual Tree β†’ Stable Condition ID β†’ Generated Evaluator Registry

Runtime execution:

Event Fires β†’ Resolve Baked Evaluator β†’ Return TRUE/FALSE

Condition Execution and Build Checks​

Generated Registry: Editor assigns stable condition IDs and generates evaluator registration source before Player Build

Build Checks: Missing or stale bake data stops Player Build and lists the assets and properties that need baking again

Unbaked Conditions: Mono and Editor can evaluate them directly, IL2CPP Player reports an error and returns false


Tree Management​

  • Enable/Disable: Toggle checkbox to bypass all conditions (always TRUE).

  • Reset Tree: Click "Reset Tree" button to clear all nodes and start fresh.

  • Collapse/Expand: Click comparison blocks to toggle between summary and detail views.


Troubleshooting​

Conditions Always Return False​

Checklist:

  • βœ… Is "Enable Conditions" toggle checked?
  • βœ… Are there red type mismatch warnings?
  • βœ… Are Scene Type references still valid (not destroyed)?
  • βœ… Do bool methods return expected values? (add Debug.Log)

Property Not in Dropdown​

For Event Arguments:

  • Must be public field or property
  • Must be supported type

For Scene Types:

  • GameObject must exist in scene at Editor time
  • Component must be enabled
  • Property must be public
  • Methods must: return bool, zero parameters, public, instance (not static)

For Runtime Objects: Use Event Argument instead of Scene Type.


Changes Not Saving​

Common Causes:

  • Multiple Behavior Windows open (close duplicates)
  • Script compilation during editing (wait for completion)
  • Unity didn't apply SerializedProperty changes (wait before closing)

Where It's Used​

The condition in Game Event Behavior applies to actions bound through the manager.

Event Fires β†’ Check Conditions β†’ Execute/Skip Actions

A FlowGraph node checks its condition when the flow reaches that node.

Flow Reaches Node β†’ Check Conditions β†’ Execute/Skip Node

Receiver Action Conditions​

Starting in GES 2.3.0, each Receiver can check a separate condition in its Inspector. A failed check skips that component's On Triggered actions. Other listeners on the event continue to respond according to their own rules.

  1. Select the GameObject with the Receiver. Assign Game Event in Event Reference, then add UnityEvent callbacks in Event Action.
  2. Under Action Condition, select Enable Conditions in Execution Gate. Use + Condition to add a comparison or + Group to add a group.
  3. Click AND or OR to change a group's logic. Click a colored condition block to edit Source Value, Operator, and Target Value. Drag the handle at the start of the row to reorder it within the group.

Event Action uses Unity's native callback list with a typed UnityEvent label. Use Clear All to remove all bound actions.

ReceiverValues available to its condition tree
Parameterless GameEventReceiverScene values, constants, and random values
Single-argument Receiver, such as Int32GameEventReceiverScene values, constants, random values, the incoming argument, and its members
Sender + Args ReceiverScene values, constants, random values, the sender and argument, and their members

Set an Int32GameEventReceiver condition to Event Argument > 5, then raise the event with 3 and 8. This Receiver invokes its actions only for 8. Another Receiver with conditions disabled responds to both raises.

Conditions start disabled, so existing components retain their configured actions after an upgrade. An enabled empty tree passes. Generated and custom typed Receivers inherit the runtime gate without regenerating component code.

Save the scene or prefab and run Bake AOT Conditions before building for IL2CPP. The build checks Receiver conditions for missing or stale bake data.