Tag Grammar Reference

One syntax is used for targets (what an action animates), conditions (gates), and filters, across every field: component tags, actor tags, proximity Enter/Leave, an Action's Target, and a group's Gates list. Learn it once.

Note

This page is the full reference. Quick Start shows it in use; The Model explains what a tag ultimately triggers.

Two principles

  1. Each character has one job, in every field. A token reads the same whether it sits in a Target or a Condition, only the field it lives in changes what it's for. A mangled token (a stray space, a wrong character) lands on an invalid token the parser and the panel's chips reject, never on a different valid meaning.

  2. Selection and execution are separate. @ = % # pick which component/actor. A trailing ^ picks whose IC runs the verb. They compose independently.

Selection: which thing

Prefix

Points at

@Name

a component named Name on the current actor

@=Tag

a component tagged =Tag on the current actor (= = "by tag" modifier on @)

%Tag

an external actor (anywhere in the level) tagged =Tag: actors are always addressed by tag

#Special

logic-resolved: #Self #Parent #Root #Actor #Carry

(nothing)

this IC itself (only valid before a :State)

Bones: / (only after @)

A trailing /Bone on an @ hop targets that bone of a Poseable Mesh instead of the component itself:

Form

Means

@DoorMesh

the component DoorMesh, moved as a whole

@DoorMesh/hinge_01

the bone hinge_01 on DoorMesh: the mesh itself stays put

  • / is a bone delimiter only under @. A plain DoorMesh/hinge_01 (no @) is not a valid address and is rejected as unparseable.

  • The target must be a UPoseableMeshComponent. A skeletal mesh is driven by its AnimInstance and fights a bone pose: see Bones: addressing a rig like a component.

  • The bone is driven in component space; anything skinned beneath it follows.

  • Deltas layer onto the bone's captured @Zero pose, not the world origin.

Execution: whose IC runs it (the ^ suffix)

A trailing ^ on a hop means "hand the rest of the address to that element's own IC" (recursively). With no ^, the IC that ran it resolves the whole path and drives the resolved components directly, with its own keyframes.

Form

Means

%Lift

our IC drives Lift's component(s) directly

%Lift^

hand off to Lift's own IC: it runs its interaction

@Door

our IC drives my Door component

@Door^

my Door is a child-actor with its own IC → hand off to it

^ is execution-only: it appears on targets, never in a condition (a condition only reads state, it never delegates).

Chaining: where ^ sits

Walk the path with @/=. Drop a %Actor in to jump to an external actor. Place ^ after whichever hop's IC should take over. Everything to the right of a ^ becomes the sub-target that IC resolves next.

Token

Reads as

%Lift@Panel

external Lift, its Panel component: our IC drives it

%Lift@Panel^

...hand off to Panel's own IC

%Lift^@Panel

hand off to Lift's IC, which then targets its Panel

%Lift^@Platform^@Motor

Lift's IC → its Platform's IC → that Platform's Motor

@Platform^

(editing Lift) my own child Platform → its IC

State: :

Append :State to any address to test / filter by resting state:

Form

Means

:State

this IC is in State (no address = self)

@Lock:Off

my Lock component is in Off

%Lift:Locked

the external actor =Lift is in Locked (any component, or its IC)

%Lift@Platform:Docked

Lift's Platform component is in Docked

:!State / @Lock:!Off

not in that state (! in the state slot = negate: unambiguous there; ! is "flow" only at a token's very start)

Phase: has it arrived, or is it still moving?

A state is only recorded once the animation reaches its keyframe. Mid-move a target holds no state at all, so a plain test reads a sliding drawer as still closed and lets a colliding move start. Mark the phase to say what counts:

Form

Means

@Drawer:Open

moving toward Open, or already there (the default)

@Drawer:=Open

fully Open, the animation has finished

@Drawer:~Open

on its way to Open, not there yet

The unmarked form is the forgiving one: a collision gate usually means "is it open or about to be", and that is the easy thing to type.

Markers combine with negation, the ! coming first:

!OpenY @Drawer2 ?@Drawer3:!Open
                └──────┬───────┘
   open Drawer2 only if Drawer3 is neither open nor opening

Use := for "the move is complete", ex: only allowing a lift to be called once its doors have fully shut.

Filter vs gate: same :State, two roles

  • In a target → filter. %Lights:On drives only the =Lights actors currently On. It narrows the set, it does not stop execution.

  • In a ? token → gate. ?@Lock:Off doesn't select anything, it decides whether the whole interaction runs.

Gate: ? (inline precondition, tags only)

Form

Means

?<address>:State

run this interaction only if the predicate holds

?<address>:!State

run only if not

?<address>:=State

only if it has fully reached that state

?<address>:~State

only if it is still moving toward it

!Open @Door ?@Lock:Off
└─┬─┘ └──┬─┘ └───┬────┘
 flow  target   gate: run "Open" on @Door only if @Lock is Off

For a group-level gate instead of a tag-level one, use the group's own Gates list: same address grammar, a trailing :!State negates that one entry. See Gates: locks & conditions.

Flow & label: start of a verb token

Prefix

Flow

!Verb

parallel: runs on its own

>Verb

choice: offered as an option to pick

+Verb

extension: adds onto the previous interaction

Verb/Label

/ splits the run verb from its display label

The verb names a Cycle or Group, resolved by whichever IC ends up running it (the IC that ran it, or a delegated one via ^).

Wildcards

* globs inside a name, ex: @Door* hits DoorHandle, DoorHandle2. ? is not a wildcard here, it is the gate prefix.

Worked examples

# Targets: selection (@ @= % #) + optional ^ (delegate)
@Door                    my Door component (our IC)
@Door^                   my Door child-actor → its own IC
@=Handle                 my component tagged =Handle
@Door*                   my components whose name starts "Door"
#Self                    the component the tag sits on
%Lift                    external Lift's components, driven by our IC
%Lift^                   external Lift → its own IC (it runs its interaction)
%Lift@Panel              external Lift's Panel component (by name), our IC
%Lift@=Rail              external Lift's component tagged =Rail, our IC
%Lift@Panel^             ...that Panel's own IC
%Lift^@Panel             Lift's IC, targeting its Panel
%Lights:On               only the =Lights actors currently On (filter)

# Conditions (no ^: reads never delegate)
:Open                    this IC is in "Open"
@Lock:Off                my Lock component is Off
@Bay:Docked              my child Bay's state (the cross-IC read is handled for you)
%Vault:Sealed            the external Vault is Sealed
%Lock@Bolt:Locked        external Lock's Bolt component is Locked

# Full interaction tags
!Open @Door                    open my Door (parallel)
!Raise %Lift^                  tell the external Lift to raise itself
>Unlock @Door ?@Key:Held       offer "Unlock" only while @Key is Held
!Toggle @Fan/Spin              run "Toggle", show "Spin" in the UI
!Open @Door ?@Lock:!Off        open only if the lock is NOT Off
!OpenY @Drawer2 ?@Drawer3:!Open   open only if Drawer3 is neither open nor opening
!Call @Lift ?@Doors:=Shut         call the lift only once its doors have FULLY shut

Where the prompt appears: $X,Y,Z

An interaction point sits on its component's pivot. A pivot is a setup convenience, not somewhere a player aims: a door built from a base-pivoted cube puts its prompt on the floor.

Two things fix that, and you usually need neither.

Automatic. With no offset token, the point aims at the component's visual center instead of its pivot. This is on by default (bAutoCenterPoints on the Interactive Component), so a base-pivoted door prompts at door height with no extra setup at all. Turn it off for a project that already places its pivots where the prompt should sit.

Explicit. A $X,Y,Z token places the point yourself, and always wins over auto-centering:

!Toggle $0,0,90          the point sits 90cm above the component origin
!Toggle $,,90            the same: an omitted axis is zero
!Toggle $-5,0,40         negative values are fine

The offset is local to the component, not world space, so it follows the part as it moves. On a door, a world-space offset stays behind as the door swings.

It is rotated by the component but never scaled by it. The numbers are always centimeters, so $0,0,90 is 90cm no matter how the mesh is scaled. MovaCore's cubes are 1cm and scaled up to size: a 2m door is a cube at scale 200, and a scaled offset lands 180 meters away.

Several points on one component

A $ token also starts a new point, so one mesh can offer more than one place to interact without an invisible child component per spot:

!Open $0,0,10  !Lock $0,0,90

That is one component with two independent points: a handle low down and a lock higher up, each with its own prompt and its own action. The offset binds to the action it follows, which is how it reads.

The same action in several places

Repeat the offset with no new verb between and the action repeats with it:

!Toggle $0,0,90 $90,0,0

One verb, two places to reach it, ex: either end of a push bar.

A tag list with no $ behaves as before, producing a single point.

You rarely type this

The IC Control Panel offers every token from a searchable [...] picker (The IC Control Panel) and validates each one with a color-coded chip: amber with a "did you mean" suggestion when a token resolves to nothing.