Skip to content

RGScript Events And Callback Captures

RGScript events use the shared ReGame event lane. State changes first, then the authoritative object emits an event. GameObject.on(...) and named on... helpers return an EventController that can pause, resume, or cancel that specific subscription.

Registering And Emitting

rgscript
class OrderDisplay extends Component {
let orderTarget: GameObject
let orderSubscription: EventController

function _ready() {
    orderTarget = GameObject.Find("OrderTarget")
    orderSubscription = orderTarget.on("order.changed", (orderId: string) => {
        console.log("Order changed: " + orderId)
    })
}

function requestRefresh() {
    orderTarget.trigger("order.changed", "order-42")
}
}

trigger(...) invokes ordinary listeners synchronously. A nested event may run before the emitting method resumes. Script fields are authoritative during that nested call: an inner listener's field write remains visible to the outer method, while a later explicit outer write still wins.

Do not make gameplay correctness depend on listener ordering. If several systems need an ordered workflow, represent its phases as explicit state and events.

Capturing Helper Parameters And Locals

Arrow callbacks are persistent closures. When a callback is registered inside a helper, RGScript captures every referenced helper parameter and local value at that registration:

rgscript
function watchCustomer(customer: GameObject, queueLabel: string) {
    let source: string = "cashier"
    customer.on("order.completed", (orderId: string) => {
        console.log(queueLabel + ":" + source + ":" + orderId)
        if (customer != null && customer.exists()) {
            customer.trigger("customer.seatRequested", orderId)
        }
    })
}

The callback keeps customer, queueLabel, and source after watchCustomer(...) returns. Each registration receives an independent capture environment, even when the same helper registers the same callback more than once.

Capture behavior matches GDScript:

  • Scalars are captured by value when registration happens. Reassigning the helper variable afterward does not change the captured value.
  • Arrays and records retain their normal reference identity. Mutating their contents is visible through other references to the same container.
  • GameObjects use safe object identity. A captured reference does not make a destroyed scene object live again. Check exists() before reading other properties or calling methods when destruction is possible.
  • Assigning a captured scalar inside one invocation changes only that invocation's callback-local slot; it does not rewrite the stored capture.
  • Variables declared inside the callback are recreated for every invocation.

Script fields are not closure captures. They remain fields on that script instance and are shared by its methods and listeners.

Payloads Versus Captures

Use captures for stable registration context, such as the customer or station a listener belongs to. Use the event payload for information belonging to one emission, such as an order id, amount, collision participant, or UI event.

Passing authoritative identity in a typed payload is often the clearest design for handoffs between systems. It is a design choice, not a workaround for callback lifetime.

Lifecycle And Cleanup

rgscript
function stopWatching() {
    if (orderSubscription != null) {
        orderSubscription.cancel()
        orderSubscription = null
    }
}

Cancelling a subscription releases its captured environment immediately. Owner-bound subscriptions are also cancelled when their script slot is replaced, their owning object is destroyed, or the runtime scene is cleared by Stop or Reload. Pausing retains the subscription and its captures so it can be resumed.

Long-lived engine or global subscriptions should still keep their controller in explicit owner state and cancel it during teardown. Do not create unowned global listeners.

Errors And Recursion

Synchronous A → B → A event cycles are allowed, but unbounded cycles stop at the RGScript recursion boundary and report the recent event chain instead of exhausting the native stack. A callback error unwinds its local and capture slots before another listener runs.

Platform Behavior

Capture, ordering, cancellation, and owner-cleanup semantics are implemented in the shared RGScript runtime and are the same on macOS, Windows, Android, and iOS. Platform event adapters must enter the same canonical event lane rather than maintaining a separate callback store.

ReGame engine documentation