ControllerRegistry
Properties
shareFirstControllerWithRemote(boolean) — Default true - the first browser shares playerIndex 0 with the firstcombineRemoteInputs(boolean) — Default false - each distinct remote/gamepad gets its own stable
Constructor
new ControllerRegistry(): ControllerRegistryInstance Methods
assignPlayerIndex(): integer
Assigns the lowest free player index to a browser controller connection. With shareFirstControllerWithRemote (default), the first browser claims index 0, shared with the first remote/gamepad regardless of connection order (see assignPlayerIndexForRemoteId); only a second browser onward gets its own index starting at 1.
Returns
integer
releasePlayerIndex(playerIndex: integer): void
Frees a player index previously returned by assignPlayerIndex(), e.g. when its connection closes - a later assignPlayerIndex() call may reuse it.
Parameters
playerIndex(integer) — the player index to release
Returns
void
assignPlayerIndexForRemoteId(remoteId: string): integer
Looks up (or lazily assigns) a stable playerIndex for one physical remote/gamepad, identified by roUniversalControlEvent.GetRemoteID() (e.g. "30:1"). Never released - a physical remote has no disconnect signal to key a release off, unlike a browser controller's WebSocket close. With shareFirstControllerWithRemote (default), the first remote/gamepad claims index 0 even if a browser already occupies it too (see assignPlayerIndex). With combineRemoteInputs, every remote/ gamepad is forced onto index 0 instead.
Parameters
remoteId(string) — the string from roUniversalControlEvent.GetRemoteID()
Returns
integer
updateFromMessage(
playerIndex: integer,
buttons: object,
sticks: object,
custom: object,
): Array.<string>
Updates one connection's raw state from a decoded {buttons, sticks, custom} message. Returns the button names whose boolean flipped since the last update, for Game to synthesize press/release GameInput events. The message is untrusted - a non-boolean button or non-numeric stick axis is coerced to a safe default (false / 0.0) rather than propagating whatever ParseJson happened to hand back.
Parameters
playerIndex(integer)buttons(object) — name -> dynamic (expected boolean)sticks(object) — name -> {x, y} (each dynamic, expected numeric)custom(object) — opaque, passed through untouched
Returns
Array.<string>
isButtonHeld(playerIndex: integer, buttonName: string): boolean
Parameters
playerIndex(integer) — which player's connection to readbuttonName(string) — which button to check
Returns
boolean
getHeldTimeMs(playerIndex: integer, buttonName: string): integer
Parameters
playerIndex(integer) — which player's connection to readbuttonName(string) — which button to check
Returns
integer
getButtonNames(playerIndex: integer): Array.<string>
Parameters
playerIndex(integer)
Returns
Array.<string>
getStick(
playerIndex: integer,
stickName: string,
): BGE.Math.Vector
Parameters
playerIndex(integer) — which player's connection to readstickName(string) — the stick's name, as sent in the message'ssticksmap
Returns
BGE.Math.Vector
getCustomPayload(playerIndex: integer): object
Parameters
playerIndex(integer)
Returns
object
setRemoteDpad(playerIndex: integer, x: float, y: float): void
Records one physical remote/gamepad's current d-pad-derived {x, y}, used by BGE.Controller.ControlMap.getAxis() when no controller stick is bound (or it's neutral) for this player.
Parameters
playerIndex(integer)x(float) — 1..1y(float) — 1..1
Returns
void
getRemoteDpad(playerIndex: integer): BGE.Math.Vector
Parameters
playerIndex(integer)
Returns
BGE.Math.Vector
setRemoteEvent(playerIndex: integer, event: dynamic): void
Caches the last Bluetooth-gamepad roUniversalControlEvent seen for this player, so getRemoteAnalogStick() can keep reading its live GetValue() every frame even on a frame with no new event for this remote (a stick held at a nonzero deflection produces no new roUniversalControlEvent - see specs/2026-09-15-multi-controller-input-design.md). A non-gamepad event (e.g. keyboard-sim) is ignored here rather than overwriting an already-cached gamepad event - under combineRemoteInputs, where both share one playerIndex, caching indiscriminately would let a keyboard press evict the gamepad's slot, and a held-but-unchanging analog stick has no later event of its own to reclaim it.
Parameters
playerIndex(integer)event(dynamic) — the roUniversalControlEvent, passed as dynamic since GetValue() isn't part of its typed interface
Returns
void
getRemoteAnalogStick(
playerIndex: integer,
stick?: string,
): BGE.Math.Vector
Parameters
playerIndex(integer)stick(string, optional, default: "\"1\"") — "2" reads the right stick (RIGHT_X/RIGHT_Y); any other value (including the default "1") reads the left stick - matching bindAxis()'s own stick-name convention
Returns
BGE.Math.Vector