Talos docs

ViewsHandle

Since 0.7

ctx.views: split screen for local multiplayer.

Plain text

Views are numbered from 1, like players: view 1 is ctx.camera itself, and views 2 to 4 each have a camera rig of their own with exactly ctx.camera's set and follow. A view number outside 1..4 throws a ViewsError (views-out-of-range). The UI is drawn once over every view.

Signature

export interface ViewsHandle {
  /** Splits the window. Throws `ViewsError` (`views-layout-unknown`) for a name that is not a {@link ViewLayout}. */
  setLayout(layout: ViewLayout): void
  /** The layout in force: `"single"` until one is set. */
  readonly layout: ViewLayout
  /** How many views the layout has. */
  readonly count: number
  /** Where view `view` is in the window; `null` when the layout has no such view. */
  rect(view: number): ViewRect | null
  /** `ctx.camera.set` for view `view`. */
  set(view: number, kind: CameraKind, params?: CameraParams): void
  /** `ctx.camera.follow` for view `view`. */
  follow(view: number, target: EntityHandle | string | null): void
  /**
   * A camera that keeps every one of `targets` in view: at their centre, from `yaw`/`pitch`, pulled
   * back (and with `maxFov`, widened) to fit them all, eased on game time. For several players on one
   * shared screen: `ctx.views.frame(1, players)`. An empty list, `set` or `follow` ends it.
   */
  frame(view: number, targets: (EntityHandle | string)[], params?: FrameParams): void
  /**
   * One camera for players sharing a screen that splits when they spread out. View 1 frames every
   * target (as {@link ViewsHandle.frame}); once their spread is beyond `split.beyond` for `split.hold`
   * seconds the window splits into one view per live target, in the order given (two to four), each
   * framing its own target from the same direction, and it merges back once the spread is under
   * `split.back` for as long. Each switch raises `views-split` ({@link ViewsSplitEvent})
   * or `views-merged` ({@link ViewsMergedEvent}). The same call again changes nothing; a different one
   * starts shared. `setLayout`, `frame(1, …)` or `ctx.camera.set`/`follow` end it where it stands;
   * `shared([])` ends it and goes back to one view. Throws `ViewsError` (`views-layout-unknown`) for a
   * `split.layout` that is not `"auto"`, `"left-right"` or `"top-bottom"`.
   */
  shared(targets: (EntityHandle | string)[], params?: SharedParams): void
  /** View `view`'s rig yaw now, radians (`ctx.camera.yaw` per view). */
  yaw(view: number): number
  /** View `view`'s rig pitch now, radians. */
  pitch(view: number): number
  /**
   * The heading view `view` is drawn from, radians about +Y with 0 facing -Z, as of the top of the tick:
   * the frame that view's player reads a camera-relative stick in (`ctx.camera.viewYaw` per view). 0 for
   * a view the layout does not have.
   */
  viewYaw(view: number): number
  /** The vertical field of view view `view` is drawn at, degrees (`ctx.camera.fov` per view). */
  fov(view: number): number
}

Import: import type { ViewsHandle } from "@argus/api"

Members

MemberSignatureDescription
setLayoutsetLayout(layout: ViewLayout): voidSplits the window. Throws ViewsError (views-layout-unknown) for a name that is not a {@link ViewLayout}.
layoutreadonly layout: ViewLayoutThe layout in force: "single" until one is set.
countreadonly count: numberHow many views the layout has.
rectrect(view: number): ViewRect | nullWhere view view is in the window; null when the layout has no such view.
setset(view: number, kind: CameraKind, params?: CameraParams): voidctx.camera.set for view view.
followfollow(view: number, target: EntityHandle | string | null): voidctx.camera.follow for view view.
frameframe(view: number, targets: (EntityHandle | string)[], params?: FrameParams): voidA camera that keeps every one of targets in view: at their centre, from yaw/pitch, pulled back (and with maxFov, widened) to fit them all, eased on game time. For several players on one shared screen: ctx.views.frame(1, players). An empty list, set or follow ends it.
sharedshared(targets: (EntityHandle | string)[], params?: SharedParams): voidOne camera for players sharing a screen that splits when they spread out. View 1 frames every target (as {@link ViewsHandle.frame}); once their spread is beyond split.beyond for split.hold seconds the window splits into one view per live target, in the order given (two to four), each framing its own target from the same direction, and it merges back once the spread is under split.back for as long. Each switch raises views-split ({@link ViewsSplitEvent}) or views-merged ({@link ViewsMergedEvent}). The same call again changes nothing; a different one starts shared. setLayout, frame(1, …) or ctx.camera.set/follow end it where it stands; shared([]) ends it and goes back to one view. Throws ViewsError (views-layout-unknown) for a split.layout that is not "auto", "left-right" or "top-bottom".
yawyaw(view: number): numberView view's rig yaw now, radians (ctx.camera.yaw per view).
pitchpitch(view: number): numberView view's rig pitch now, radians.
viewYawviewYaw(view: number): numberThe heading view view is drawn from, radians about +Y with 0 facing -Z, as of the top of the tick: the frame that view's player reads a camera-relative stick in (ctx.camera.viewYaw per view). 0 for a view the layout does not have.
fovfov(view: number): numberThe vertical field of view view view is drawn at, degrees (ctx.camera.fov per view).

Example

The tested fixture fixtures/app/agent.ts.

import type { Behavior } from "@argus/api"

// Sends a nav agent to `props.target` once, then reports arrival through ctx.state so the
// test can read it without polling the crowd directly.
export default {
  init(ctx) {
    ctx.state.arrived = false
    ctx.nav?.goTo(ctx.props.target as [number, number, number])
  },
  update(ctx) {
    if (ctx.nav?.arrived) ctx.state.arrived = true
  },
} satisfies Behavior