Visual Condition Tree
Build boolean logic visually to control whether an event's actions run. You compose group nodes and comparison nodes against the event argument, 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.Build boolean logic visually to control whether event actions should execute. You set up runtime checks through the editor, with no coding required.

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:

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β
| Feature | Benefit |
|---|---|
| π¨ Visual Building | Designers create logic without code |
| π High Performance | Compiles to Expression Trees (zero reflection) |
| π Reusable | Same condition applies to all event actions |
| π§ͺ Live Testing | Tweak values in Inspector, see results immediately |
| π Type-Safe | Auto-validates type compatibility |
Tree Structureβ
The condition tree is built from two node types:

Group Nodesβ
Combine multiple conditions using AND/OR logic.
Logic Types:
- AND Logic
- OR Logic
All child conditions must evaluate to TRUE.
Visual: π’ Green border
Use: "Must satisfy ALL requirements"
Any child condition can evaluate to TRUE.
Visual: π Orange border
Use: "Satisfy ANY requirement"
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 Mode | Visual Style | Best For... |
|---|---|---|
| π Collapsed | Summary Text (e.g., Health < 50) | Quick overview of complex logic chains. |
| π οΈ Expanded | Detailed Editor (Dropdowns, Fields) | Modifying specific parameters and sources. |
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
- Scene Type
- Random Type
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.
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
| Root | Path Example | Data Type |
|---|---|---|
| Sender | Sender.Transform.position | Vector3 |
| Argument | Argument.amount | float |
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:
| Category | Supported Types |
|---|---|
| Primitives | int, float, double, long, bool, string |
| Math | Vector2, Vector3, Quaternion, Color |
| Unity | GameObject, Transform, Component references |
| Logic | Enums (with dropdowns), [Serializable] C# Classes |
Any public Field or Property in a [Serializable] class is automatically exposed in the deep-link picker.
Scene Typeβ
Access runtime data from GameObjects or Components in the scene.
How to Useβ
Step 1: Drag GameObject or Component from Hierarchy into the object field.
Step 2: Click "Select Property..." to browse available members.
GameObject Exampleβ
Drag PlayerController GameObject:
π¦ GameObject (Instance)
ββ π¦ (this GameObject) β The reference itself
ββ β
activeSelf β bool
ββ π€ tag β string
ββ π’ layer β int
π Transform (Component)
ββ π position β Vector3
ββ π localScale β Vector3
ββ π childCount β int
π§© PlayerController (Script)
ββ π’ Health β float
ββ π‘οΈ Shield β float
ββ π
Level β int
ββ β
HasFireResistance β bool
β
ββ β‘ IsInDangerZone() β bool (Method)
ββ β‘ IsCriticallyWounded() β bool (Method)
Usage:
Player.Health < 50 β Health check
Player.Level >= 10 β Level requirement
Player.IsInDangerZone() == true β Complex check via method
Bool Method Supportβ
Zero-parameter methods returning bool appear in the dropdown!
Example:
// In your component
public bool IsInDangerZone() {
return Health < 20 && Shield == 0 && !IsInvincible;
}
In Condition Tree:
Player.IsInDangerZone() == true
This encapsulates complex logic in a single method call instead of building it visually.
Component Exampleβ
Drag GameManager Component:
ποΈ GameManager (Global System)
ββ π CurrentState β GameState (Enum)
ββ π CurrentWave β int
ββ π
DifficultyLevel β int
β
ββ β‘ IsInCombat() β bool (Method)
ββ β‘ IsHardMode() β bool (Method)
Usage:
GameManager.CurrentState == Playing
GameManager.IsInCombat() == true
GameManager.DifficultyLevel >= 3
Important Limitationβ
β οΈ Scene Type requires objects to exist at scene load time.
β
Works: Objects in scene hierarchy (exist at initialization)
β Fails: Runtime-instantiated objects (don't exist yet)
Solution: Use Event Argument for runtime objects
Random Typeβ
Purpose: Generate random values at execution time.
Two Modesβ
Mode 1: Range
Generate random number within bounds.

Configuration:
- Min: Lower bound
- Max: Upper bound
- Integer: Check for whole numbers, uncheck for decimals
Mode 2: List
Pick random item from predefined values.

Configuration:
- Data Type: Choose type (int, float, string, bool, etc.)
- List Items: Add/remove values with +/- buttons
Use Casesβ
Critical Hit Chance:
Random(0~100) > 90 β 10% chance
Damage Variance:
Random(0~10) β Add random bonus damage
Dynamic Events:
Random List[Easy, Normal, Hard] β Randomize difficulty
π Operator
Available Operatorsβ
Numeric (6)
For numbers (int, float, double, long):
| Operator | Symbol | Example |
|---|---|---|
| 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:
| Operator | Symbol | Example |
|---|---|---|
| Equals | == | Name == "Hero" |
| Not Equals | != | Tag != "Enemy" |
| Starts With | Starts With | Name Starts With "Player_" |
| Ends With | Ends With | File Ends With ".png" |
| Contains | Contains (β) | 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:
| Operator | Symbol | Purpose |
|---|---|---|
| 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
- Scene Type
- Random Type
- Constant
Event Argument (Data Payload)β
Works the same as Source. See the Source section above for configuration details.
Scene Typeβ
Works the same as Source. See the Source section above for configuration details.
Random Typeβ
Works the same as Source. See the Source section above for configuration details.
Constantβ
Fixed comparison value.
Note: Only available as Target (right side), not Source.
Two Modesβ
Mode 1: Single Value
Enter one fixed value.

Data Types: Int, Float, Double, String, Bool
Mode 2: List
Define multiple acceptable values (for "In List" operator).

Configuration:
- Data Type: Type for all list items
- + / -: Add/remove items
Use Casesβ
Thresholds:
Health < 50.0
Exact Matches:
Name == "Hero"
Multiple Values:
Type In List [Fire, Void, Lightning]
Target supports one additional type: Constant (not available as a Source).
Some operators restrict target type:
-
Numeric operators (
>,<, etc.) require single values -
"In List" operator requires list types
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
Hybrid Approach (Recommended)β
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β
Compilation Processβ
One-time cost (scene load):
Visual Tree β Expression Tree β IL Code β Compiled Lambda
Runtime execution:
Event Fires β Call Compiled Lambda β Return TRUE/FALSE
Benchmark: Complex nested conditions execute in ~0.001ms (1 microsecond).
Why It's Fastβ
Zero Reflection: Direct compiled access like hand-written C#.
Expression Trees: System generates optimized IL code at initialization.
β Traditional: GetComponent() + GetField() + Invoke() per check
β
This System: Direct property access via compiled lambda
Result: Negligible overhead even with hundreds of events firing per frame.
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 Visual Condition Tree system appears in two contexts:
1. Event Behaviors β Game Event Behavior
Controls whether event actions execute:
Event Fires β Check Conditions β Execute/Skip Actions
2. Flow Nodes β Flow Node Configuration (future documentation)
Controls whether flow nodes execute:
Flow Reaches Node β Check Conditions β Execute/Skip Node
Both use the exact same condition tree system.
Simple Checks: Use Visual Tree for thresholds, enums, basic comparisons
Complex Logic: Use Bool Methods for Physics, algorithms, multi-step checks
Optimal Approach: Combine bothβvisual for simple, methods for complex
Performance: Put cheap checks first in OR groups for short-circuit optimization