Skip to Content
Modlock

Camera

Pull back, aim and shake the camera, and play curves on it.

playCamera moves a player's camera along a curve for each camera param, all on one timeline. The host sends each curve to the game as the game's own lerps, and the game plays them smoothly with no timers in your mod. Camera curves exist only in TypeScript, so this page's samples are TypeScript only. The calls they build on, moveCamera and clearCamera, are in every language.

// Pull back while the view widens, ease back in after two seconds.
playCamera(player, {
  relative: true,
  tracks: {
    distance: curve.chain(
      curve.line(0, 300, 0.4, curve.ease.out),
      curve.hold(300, 2),
      curve.line(300, 0, 0.6, curve.ease.inOut),
    ),
    fieldOfView: curve.keyframes([
      { at: 0, value: 0 },
      { at: 0.4, value: 15, ease: curve.ease.out },
      { at: 3, value: 0, ease: curve.ease.sine },
    ]),
  },
})

// Circle a point of interest until the round starts.
const tour = playCamera(player, {
  loop: true,
  tracks: { target: curve.orbit({ center: spot, radius: 400, duration: 12 }) },
})
after(30, () => tour?.cancel())

Options

OptionDoes
tracksA curve for each param: distance, fieldOfView, target, verticalOffset, horizontalOffset.
relativeAdds each curve to the param's usual value, so end each curve at zero.
loopPlays the curves again until cancel().
rateHow many lerps a second the host sends for a sampled span. 30 by default.
context, priorityA move takes a context of its own unless you pass one. When moves in two contexts overlap, the one with the higher priority plays, and the camera returns to the other when it ends.

playCamera returns a move with duration and cancel(), or nothing when the player has no hero. Each param snaps back to its usual value when its curve ends, so end a curve at rest.

Curves

The curve namespace builds curves. A curve is a list of spans, and every builder returns a curve, so you can pass one builder's result to another:

CallCurve
curve.line(from, to, seconds, ease?), curve.hold(value, seconds)A move from one value to another, or a value held.
curve.keyframes(keys, { smooth? })Through { at, value, ease? } keys, straight or on a spline.
curve.through(points, { speed, ease?, smooth? })Through points at a speed, each move as long as its distance.
curve.orbit({ center, radius, duration, turns?, start?, rise? })Around a point, for the target param; a spiral when rising.
curve.wave({ amplitude, frequency, duration, decay? })Swings either side of zero, shrinking with decay.
curve.chain(...), repeat(c, n), reverse(c), pingPong(c)Curves one after another, repeated, backward, there and back.
curve.durationOf(c), curve.valueAt(c, seconds)How long a curve plays, and its value at a time.
cameraPulseMoves a param out, holds it and brings it back, as an ability pulls the camera back while it charges.
cameraShakeShakes one param.
EasePlays as
linear, ease.bias(b), ease.gain(g) (Valve's, linear at 0.5)One lerp the game draws itself.
in, out, inOut, sine, back, elastic, bounce, shaped(fn)The host samples the ease into rate lerps a second. The client plays at most one queued lerp a frame, so on a slower client the span plays slower than its duration.

curve.shaped(fn) makes any function from 0 to 1 an ease.

The view follows the aim

The view always looks along the player's own aim, which keeps Pawn.cameraPosition and cameraAngles accurate for finding what the player aims at. The game's camera operations (CCitadelUserMsg_CameraController) move only the five params above. Frame a higher view by raising verticalOffset and pulling distance back, and shift it with horizontalOffset; the player tilts it with the mouse. Turning the view itself means setting the player's view angles, which takes over their look.

On this page