Skip to main content

Client interactions

Register client-only pointer behaviors for map sprites. Use interactions for hover popovers, selection feedback, drag previews, cursor changes, and other CanvasEngine overlays. Nothing is sent to the server unless a behavior calls ctx.action(...).

Mental Model

interactions is a client-side layer attached to rendered map sprites. It does not change gameplay state by itself. Use it for:
  • hover popovers
  • selection states
  • cursor changes
  • local highlights and drag previews
  • hitbox-based pointer filtering
  • explicit pointer-driven actions
Do not use it as the authority for gameplay. When an interaction must change the world, call ctx.action(...) and validate the request on the server.

Registering A Behavior

target can be:
  • a sprite/event id
  • an event name
  • a sprite _type, such as "event" or "player"
  • "*" for every sprite
  • a function receiving { client, target, sprite }
The returned function unregisters the behavior:

From A Client Module

Use interactions.setup() when registering behaviors from a module:
For simple lists, interactions.use is also accepted:

Hover Popover

Register a CanvasEngine component as an overlay for an existing event:
The component receives:
  • target / sprite: the RPGJS client sprite
  • state: { hovered, pressed, selected, dragging, data, overlays }
  • bounds: the default visual bounds
  • hitboxBounds: the gameplay hitbox bounds
  • graphicBounds: the rendered graphic bounds
  • pointer: the client pointer helper
  • client: the current RpgClientEngine
In CanvasEngine components, values returned by defineProps() are prop accessors. Read the sprite with sprite() or target() before accessing its fields. Component bounds are local to the sprite, so bounds() can be used directly to draw overlays attached to that sprite. Example:
The popover is local only. Hovering the guard does not call the server.

Client-Only Selection

selectable() stores selection state locally. It does not send an action.
The overlay component can read state().selected:

Explicit Server Action

Call ctx.action(...) only when the pointer gesture is meant to perform gameplay. This delegates to engine.processAction(...).
On the server, validate the request in the player input handler or a registered action. The client-sent eventId should be treated as intent, not authority.

Hitbox-Based Interactions

Use hitTest() to choose the clickable or draggable area. This is useful when a sprite graphic is larger than its gameplay body. Inside handlers and hitTest(), ctx.bounds() returns world-space bounds so it can be compared directly with ctx.pointer.world().
Available bounds:
  • ctx.bounds('hitbox'): RPGJS gameplay hitbox
  • ctx.bounds('graphic'): rendered graphic bounds
  • ctx.bounds(): default bounds, currently graphic-first
You can also implement custom areas:

Drag And Drop To A Tile

dragToTile() starts a local drag state and sends an action on drop.
The default payload is:
ctx.pointer.tile() returns:
Customize the payload with data:
Or handle the drop yourself:

Custom Drag Preview

For full control, use low-level handlers:
The overlay component can use any CanvasEngine primitive, including DOMContainer, Graphics, Sprite, or Text.

Low-Level Behavior API

A behavior can define these handlers:
Supported handler names:
  • pointerenter
  • pointerleave
  • pointerover
  • pointerout
  • pointerdown
  • pointerup
  • pointermove
  • click
  • dragstart
  • dragmove
  • drop
  • cancel

Interaction Context

Every handler receives ctx:

Helpers

RPGJS exports small helpers for common cases:
These helpers are only shortcuts. For project-specific UX, pass a behavior object directly to engine.interactions.use(...).

Network Rules

  • Pointer movement, hover, overlays, selection, drag previews, and cursor changes are client-only.
  • ctx.overlay.*, ctx.state.*, and ctx.select() do not send packets.
  • ctx.action(...) is the only interaction helper that sends an action to the server.
  • Server code must validate distance, permissions, target visibility, and map state before applying gameplay changes.