PlayersHandle
Since 0.7
The local players on the couch (meta.players) and the entity each drives.
Seating and unseating raise player-joined ({ player, device, pad? }), player-left, player-disconnected and player-reconnected ({ player }) on the event bus. A behaviour on a player's entity reads its own ctx.input exactly as a single player's does.
Signature
export interface PlayersHandle {
/** Every seated player, in number order. */
list(): LocalPlayer[]
/** How many players are seated. */
readonly count: number
/** The scene's player limit, 1 to 4; 1 for a scene without a players block. */
readonly max: number
/** Whether free devices may join now. Open in a lobby, closed during play. */
readonly joining: boolean
/** Open or close joining: a lobby opens it, a race closes it. Seated players stay seated. */
setJoining(open: boolean): void
/** Seat a device from a script (`join: "script"` scenes). The new number, or null when no seat was free. */
join(device: "keyboard" | "keyboard-left" | "keyboard-right"): number | null
join(device: "pad", pad: number): number | null
/** Seat a player with no device on this machine: a peer's, or one a test or tool drives by input frames. */
join(device: "remote"): number | null
/** Unseat a player; their devices are free to join again. */
leave(number: number): boolean
/** Drive `entity` with player `number`'s devices; null goes back to the scene's `spawn { for: "player", player }`. */
bind(number: number, entity: EntityHandle | string | null): boolean
/** The player whose devices drive `entity`, or 0. */
of(entity: EntityHandle | string): number
/** Player `number` (2 and up) reads its own input context; null follows player 1's again. */
setContext(number: number, name: string | null): boolean
/** The input context player `number` reads now, or null. */
context(number: number): string | null
}
Import: import type { PlayersHandle } from "@argus/api"
Members
| Member | Signature | Description |
|---|---|---|
list | list(): LocalPlayer[] | Every seated player, in number order. |
count | readonly count: number | How many players are seated. |
max | readonly max: number | The scene's player limit, 1 to 4; 1 for a scene without a players block. |
joining | readonly joining: boolean | Whether free devices may join now. Open in a lobby, closed during play. |
setJoining | setJoining(open: boolean): void | Open or close joining: a lobby opens it, a race closes it. Seated players stay seated. |
join | join(device: "keyboard" | "keyboard-left" | "keyboard-right"): number | null | Seat a device from a script (join: "script" scenes). The new number, or null when no seat was free. |
leave | leave(number: number): boolean | Unseat a player; their devices are free to join again. |
bind | bind(number: number, entity: EntityHandle | string | null): boolean | Drive entity with player number's devices; null goes back to the scene's spawn { for: "player", player }. |
of | of(entity: EntityHandle | string): number | The player whose devices drive entity, or 0. |
setContext | setContext(number: number, name: string | null): boolean | Player number (2 and up) reads its own input context; null follows player 1's again. |
context | context(number: number): string | null | The input context player number reads now, or null. |
Example
The tested fixture fixtures/recipes/collectible-and-goal/scenes/behaviors/collectible.ts.
import type { Behavior } from "@argus/api"
/**
* A coin the player collects by walking into it.
*
* It says so once, as `collected`, and leaves the world. Counting is somebody else's job —
* `goal-exit.ts` — so a coin never needs to know how many there are.
*
* The contact is `onTrigger`, which needs three things in the scene: a collider with
* `isTrigger: true` (the volume), a `trigger` component (what fires), and a body on the player —
* the character controller's capsule is one.
*/
interface State {
collected: boolean
}
interface Props {
/** Tag the toucher must carry. Default `player`. */
playerTag?: string
}
export default {
init(ctx) {
if (typeof ctx.state.collected !== "boolean") ctx.state.collected = false
},
onTrigger(ctx, trigger) {
if (ctx.state.collected || !trigger.entering) return
if (!trigger.other.hasTag(ctx.props.playerTag ?? "player")) return
ctx.state.collected = true
// The `goal` component on this coin has the same id, so the playtester and the HUD agree.
ctx.events.emit("collected", { goal: ctx.entity.id })
// Last: after `despawn` every member of this entity's handles throws.
ctx.entity.despawn()
},
} satisfies Behavior<State, Props>