Skip to main content

ListenerAction Script Protocol

Overview

ListenerAction scripts run custom code when a State Machine listener event fires. Unlike Listeners (which are function signatures for addListener()), ListenerAction is a script type that you attach to state machine listener events.

When to use ListenerAction scripts:

  • Custom side-effects when a state machine listener triggers
  • Logging, analytics, or audio playback on listener events
  • Complex logic that needs the pointer event data from the trigger

Type Definition

export type ListenerAction<T> = {
init: ((self: T, context: Context) -> boolean)?,
perform: (self: T, pointerEvent: PointerEvent) -> (),
} & T

Protocol Methods

FunctionSignatureRequiredDescription
init(self: T, context: Context): booleanNoOne-time initialization
perform(self: T, pointerEvent: PointerEvent): ()YesRuns when the listener fires

perform(self, pointerEvent)REQUIRED

Called when the state machine listener event fires. Receives the PointerEvent that triggered the listener, providing access to position and pointer ID.

function perform(self: MyAction, pointerEvent: PointerEvent)
print("Listener triggered at:", pointerEvent.position.x, pointerEvent.position.y)
end

init(self, context) — Optional

Called once when the action is created. Use for setup and accessing the ViewModel.

function init(self: MyAction, context: Context): boolean
local vm = context:viewModel()
-- Setup logic
return true
end

Template

export type MyAction = {
clickCount: number,
}

function perform(self: MyAction, pointerEvent: PointerEvent)
self.clickCount += 1
print("Click #" .. tostring(self.clickCount)
.. " at (" .. tostring(pointerEvent.position.x)
.. ", " .. tostring(pointerEvent.position.y) .. ")")
end

return function(): ListenerAction<MyAction>
return {
clickCount = 0,
perform = perform,
}
end

How to Apply

  1. Create a ListenerAction script in the Assets panel
  2. In the State Machine editor, select a Listener
  3. Add the script as an action on the listener
  4. The perform function will run each time the listener triggers

Key Differences from Node Scripts

FeatureNode ScriptListenerAction
Lifecycleinit, advance, update, drawinit, perform
RenderingHas draw() and Renderer accessNo rendering
When it runsEvery frame (advance/draw)Only when listener fires
Pointer dataVia pointerDown/pointerMove handlersVia perform parameter

Practice Exercise

Exercise 1: Click Counter Action ⭐

Premise

ListenerAction scripts are event-driven. They run only when the listener fires, making them ideal for analytics, counters, and side-effects.

Goal

By the end of this exercise, you will increment a counter in perform() and print an ANSWER: line after 3 listener events.

Starter Code

export type ClickCounterAction = {
clicks: number,
}

function perform(self: ClickCounterAction, pointerEvent: PointerEvent)
-- TODO 1: Increment self.clicks

-- TODO 2: Print click position
-- print("click at", pointerEvent.position.x, pointerEvent.position.y)

-- TODO 3: When clicks reaches 3, print "ANSWER: clicks=3"
end

return function(): ListenerAction<ClickCounterAction>
return {
perform = perform,
clicks = 0,
}
end

Assignment

  1. Attach the script as a listener action in a state machine Listener
  2. Trigger the listener 3 times
  3. Copy the ANSWER: line from Console

Verify Your Answer

Verify Your Answer

Knowledge Check

Q:Which callback is required in ListenerAction scripts?
Q:Where do you attach a ListenerAction script?

See Also: Listeners, Script Types Overview, PointerEvent

Next Steps