Documentation index for AI agents (llms.txt) A Markdown version of every page is available: request the page's source under /docs/, follow the "alternate" link in this page's head, or start from /llms.txt.
Visual Script logo Scripting API

Custom Conditions#

A Condition derives from Condition and implements a synchronous method that returns bool. The editor discovers the compiled type and adds it to Conditions lists.

A minimal Condition#

This Condition compares a Game Object Signal's tag with a String Signal:

using System;
using UnityEngine;
using VisualScript.Icons;
using VisualScript.Runtime;

[Title("Game Object Tag")]
[Description("Compares a Game Object tag with another value")]
[Category("Game Object/Tag")]

[Parameter("Game Object", "The Game Object targeted")]
[Parameter("Tag", "The value to compare against")]

[Image(typeof(IconTag), ColorTheme.Type.TextLight)]

[Serializable]
public class ConditionMyGameObjectTag : Condition
{
    [SerializeField] private GetGameObject m_GameObject = new GetGameObject();
    [SerializeField] private GetString m_Tag = new GetString();

    protected override string GetTitle(GameObject source) => $"{this.m_GameObject} Tag = {this.m_Tag}";

    protected override bool Run(Args args)
    {
        GameObject gameObject = this.m_GameObject.Get(args);
        string tag = this.m_Tag.Get(args);

        return gameObject != null && gameObject.CompareTag(tag);
    }
}

Run returns true when the resolved object exists and its tag matches m_Tag. The attributes, serialized Signals, and title follow the same shape as an Instruction, with a synchronous bool Run(Args) contract.

Conditions must be pure#

The caller evaluates Run whenever it needs an answer. This can happen several times per frame, including every frame inside While or Wait Until.

Never change state in a Condition

A Condition that increments a counter, spawns an object, or writes a variable produces results based on evaluation frequency. The caller controls that frequency, so the resulting state is unpredictable.

Use an Instruction for operations that change state.

The inversion toggle negates the result after calling Run. It doesn't prevent any side effects inside Run.

Keep it cheap#

Because Conditions can be evaluated every frame, choose an operation whose cost fits that frequency:

Cheap Expensive
Comparing values Searching the scene
Reading a component Physics queries
Reading a variable Iterating a large collection
String equality Allocating

Use an expensive Condition only when the check requires it. Add a [Note] that identifies the cost so designers avoid evaluating it every frame through Wait Until.

Null safety#

Signals resolve to null when an object was destroyed, a variable was never set, or a lookup found nothing.

A Condition should treat that as false rather than throwing. Guard the resolved value before running the remaining check, which is omitted here:

protected override bool Run(Args args)
{
    GameObject gameObject = this.m_GameObject.Get(args);
    if (gameObject == null) return false;

    // …
}

The null guard returns a valid Condition result and keeps the containing Event running.

Condition or Bool Signal?#

Both return a boolean result. Choose based on where designers need to use the check:

Write a When
Bool Signal The value is useful anywhere a boolean is — inside other Signals, as a field on a component, stored in a variable.
Condition The check reads more clearly as a guard in a Conditions list.

Prefer the Signal when both forms read well. A Bool Signal composes, can be stored, supports bindings, and still works as a guard through the Condition that wraps it.

Checklist#

  • [Serializable], derived from Condition.
  • Run is pure — no state changes anywhere.
  • null results in false, not an exception.
  • Evaluation cost fits the expected frequency, with expensive operations documented in [Note].
  • GetTitle reads as a statement: Target has tag Enemy.

Where to go next#

Visual Script logoVisual Script © Catsoft Works 2026. All rights reserved.