GameObject Condition
GameObject-based conditions are the recommended way to create custom conditions in State Designer unless profiling shows you need ECS-specific optimization. They are simple to write, easy to debug, and use a familiar callback-based API. A GameObject condition derives from Opsive.StateDesigner.Runtime.Conditions.Condition Conditions are attached to transitions. They do not run behavior directly like actions. Instead, they return a boolean from IsValid() to tell the transition whether it can trigger.
GameObject Condition API
A custom condition can implement the following callbacks:
/// <summary> /// Callback when the state machine is initialized. /// </summary> public virtual void OnAwake() /// <summary> /// Callback when the state machine is started. /// </summary> public virtual void OnStateMachineStarted() /// <summary> /// Callback when the condition starts. /// </summary> public virtual void OnStart() /// <summary> /// Is the condition valid? If it is valid then the transition will occur. /// </summary> /// <returns>True if the condition is valid.</returns> public abstract bool IsValid() /// <summary> /// Callback when the condition stops. /// </summary> public virtual void OnEnd() /// <summary> /// Callback when the state machine is stopped. /// </summary> /// <param name="pause">Is the state machine paused?</param> public virtual void OnStateMachineStopped(bool pause) /// <summary> /// Callback when the state machine is destroyed. /// </summary> public virtual void OnDestroy()
Physics Callbacks
Physics callbacks are opt-in for efficiency. If your condition needs one, override the corresponding receive property with true:
- ReceiveCollisionEnterCallback
- ReceiveCollisionExitCallback
- ReceiveCollisionEnter2DCallback
- ReceiveCollisionExit2DCallback
- ReceiveTriggerEnterCallback
- ReceiveTriggerExitCallback
- ReceiveTriggerEnter2DCallback
- ReceiveTriggerExit2DCallback
- ReceiveControllerColliderHitCallback
Then implement the matching callback method (OnTriggerEnter, OnCollisionEnter, etc.).
Save/Load API
Conditions support runtime save/load:
/// <summary> /// Specifies the type of reflection that should be used to save the action. /// </summary> /// <param name="index">The index of the sub-action.</param> public virtual MemberVisibility GetSaveReflectionType(int index) /// <summary> /// Returns the current action state. /// </summary> /// <param name="world">The DOTS world.</param> /// <param name="entity">The DOTS entity.</param> /// <returns>The current action state.</returns> public virtual object Save(World world, Entity entity) /// <summary> /// Loads the previous action state. /// </summary> /// <param name="saveData">The previous action state.</param> /// <param name="world">The DOTS world.</param> /// <param name="entity">The DOTS entity.</param> public virtual void Load(object saveData, World world, Entity entity)
MemberVisibility options:
- MemberVisibility.All: Save public and private fields via reflection.
- MemberVisibility.Public: Save public and serialized private fields via reflection.
- MemberVisibility.None: No reflection-based save. Implement Save and Load manually.
Example: HasEnteredTrigger
This condition returns true after a matching trigger is entered.
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using Opsive.Shared.Utility;
using Opsive.StateDesigner.Runtime.Conditions;
using Unity.Entities;
using UnityEngine;
[NodeDescription("Returns true after a matching trigger is entered.")]
public class HasEnteredTrigger : Condition
{
[Tooltip("Optional tag filter. Leave empty to accept any trigger.")]
[SerializeField] protected SharedVariable<string> m_Tag;
private bool m_EnteredTrigger;
protected override bool ReceiveTriggerEnterCallback => true;
/// <summary>
/// Callback when the condition starts.
/// </summary>
public override void OnStart()
{
m_EnteredTrigger = false;
}
/// <summary>
/// Is the condition valid?
/// </summary>
public override bool IsValid()
{
return m_EnteredTrigger;
}
/// <summary>
/// Trigger enter callback.
/// </summary>
protected override void OnTriggerEnter(Collider other)
{
if (!string.IsNullOrEmpty(m_Tag.Value) && !other.gameObject.CompareTag(m_Tag.Value)) {
return;
}
m_EnteredTrigger = true;
}
/// <summary>
/// Save manually.
/// </summary>
public override MemberVisibility GetSaveReflectionType(int index)
{
return MemberVisibility.None;
}
public override object Save(World world, Entity entity)
{
return m_EnteredTrigger;
}
public override void Load(object saveData, World world, Entity entity)
{
m_EnteredTrigger = (bool)saveData;
}
}