Files

11 KiB

id, try, try_select, icon, group, group_title, group_icon, group_order, title, order
id try try_select icon group group_title group_icon group_order title order
camera try/CameraFollow2D.aescene camera.CameraFollow2D comp.vcam camera Cameras 2D cat.camera 55 Cameras 2D 1

Cameras 2D

A Camera 2D draws the view; virtual cameras decide where it looks. Every Virtual Camera 2D is a candidate pose. The enabled one with the highest priority drives the view, and switching between them blends. Follow, shake and zones are components on virtual cameras; scripts trigger them.

Quick start: Create → Follow Camera gives a node with Camera 2D, Virtual Camera 2D and Camera Follow 2D. Set Target to the player and press Play.

Component Purpose
Virtual Camera 2D a pose with a priority, a zoom, bounds and a blend
Camera Follow 2D moves its virtual camera after a target
Camera Shake 2D tunes the shake of its virtual camera
Camera Zone 2D lifts a camera while a target is inside an area
Camera Brain 2D makes a Camera 2D show another output

Cameras run during Play and in aego run. In edit mode the Scene View shows the camera frame, the deadzone, the bounds and the zones.


id: camera/virtual try: try/VirtualCamera2D.aescene try_select: camera.VirtualCamera2D icon: comp.vcam parent: camera order: 1 title: Virtual cameras

Virtual cameras

Each frame the brain picks one virtual camera per output:

  1. The enabled virtual camera with the highest Priority. Equal priorities pick the camera created first.
  2. A Camera Zone 2D with the target inside lifts its camera to the zone's priority. On a tie the zone beats an ordinary camera, and the zone entered later beats an earlier one.
  3. camera.BlendTo and camera.Cut override everything until camera.Release.

The pose comes from the virtual camera's node: its interpolated position and rotation, and Zoom, where 0 keeps the zoom of the Camera 2D. A Camera Follow 2D on the same node replaces the position. The node's transform is never changed; the camera only moves the view.

In Try it, select Close and set its Priority to 1: the view blends to it. Set it back to -1 and the view blends back.

Blends

When another camera becomes active, the view blends from the pose it shows now to the new camera over its Blend Time, shaped by Blend Curve. A switch during a blend starts from the current view, never from the old camera. Zoom blends evenly on screen. Blend Time 0 cuts.

Bounds

Use Bounds keeps the visible area inside Bounds, a world rectangle. When the area is smaller than the view on an axis, the view is centred on it there.


id: camera/outputs try: try/CameraBrain2D.aescene try_select: camera.CameraBrain2D icon: comp.camera2d parent: camera order: 2 title: Outputs

Outputs

Virtual cameras drive the main output unless Output names another one. A Camera 2D shows the main output; add Camera Brain 2D with the same Output to make it show another, such as a minimap. Each output has its own active camera, blends and shake. A Camera 2D whose output has no virtual cameras is left as it is.

In Try it, the minimap in the corner is a second Camera 2D with a small Viewport and Camera Brain 2D set to map; its own virtual camera follows the ball from far away.


id: camera/follow try: try/CameraFollow2D.aescene try_select: camera.CameraFollow2D icon: comp.follow parent: camera order: 3 title: Follow

Follow

Camera Follow 2D moves its virtual camera after Target.

Field Unit Meaning
Deadzone 0..1 of the view area where the target moves freely; (0.4, 0.4, 0.2, 0.2) is a centred fifth
Damping per second how fast the camera catches up on each axis; 0 follows rigidly
Lookahead seconds of velocity how far ahead of a moving target to look
MaxLookahead world units largest lookahead; 0 is unlimited
LookaheadSmoothing per second how fast the lookahead changes
SnapDistance world units a longer jump of the target moves the camera at once

The camera follows the interpolated pose of a physics body, so it is smooth at any frame rate. Damping is exponential: a camera at 30, 60 and 144 frames per second ends up in the same place. When the target teleports, call Snap() on the component, or let Snap Distance catch it.

In Try it, the ball rolls down a long ramp. Set Damping to 0 for a rigid camera, or widen the Deadzone to let the ball run ahead.


id: camera/zones try: try/CameraZone2D.aescene try_select: camera.CameraZone2D icon: comp.zone parent: camera order: 4 title: Zones

Zones

Camera Zone 2D changes the camera while a target is inside its Area, a rectangle in the zone node's space. Inside, Camera gets the zone's Priority, Zoom and Bounds; outside, everything is back as before. The fields of the camera are never changed.

  • Empty Camera uses the virtual camera on the zone node; give it a low priority so it shows only through the zone.
  • Empty Target uses the follow target of the zone's camera.
  • Zones use the exact position of the target, not the interpolated one, so the same input always enters a zone on the same frame.
  • Overlapping zones: the higher priority wins, then the zone entered later. Leaving one zone does not affect the others.

In Try it, the purple area in the middle of the ramp is the zone: the view blends to a closer, fixed shot while the ball rolls through it, and back to the follow camera after.


id: camera/shake icon: comp.shake parent: camera order: 5 title: Shake

Shake

Shake is driven by trauma, a value from 0 to 1 per output. camera.Shake(0.4) adds trauma, which fades by Decay per second. The view moves by up to Max Offset and turns by up to Max Angle, scaled by trauma to the power Exponent, so small hits stay subtle. Without a Camera Shake 2D on the active camera the defaults are used.

The motion comes from deterministic noise, so a replay shakes the same way, and it never touches the node's transform or the game's random numbers.

Trauma adds up and fades in real time. With the default Decay of 1.5 a hit of 0.6 is gone in 0.4 seconds:

package demo

import (
    "aego/camera"
    "aego/log"
)

script Hits {
    var frames int

    func Ready() {
        camera.Shake(0.6)
        log.Info("hit, trauma " + string(camera.Trauma()))
    }

    func Update(dt float) {
        frames++
        if frames % 6 == 0 && frames <= 30 {
            log.Info("trauma " + string(camera.Trauma()))
        }
        if frames == 30 {
            camera.Shake(0.5)
            camera.Shake(0.8)
            log.Info("two hits, capped at " + string(camera.Trauma()))
        }
    }
}

Accessibility

Settings → Accessibility → Screen Shake scales every shake; 0 turns it off. Reduce Motion turns shake off and removes lookahead. Trauma still fades, so gameplay that reads camera.Trauma() is not affected.


id: camera/time icon: play.speed parent: camera order: 6 title: Time and replays

Time and replays

Time Domain decides how blends and shake of the active camera count time. unscaled, the default, uses real time: a hit-stop with time scale 0 does not freeze the camera. game follows the time scale and the pause.

Follow always uses game time: when the game is paused or stepped, a still target keeps the camera still.

Rewind and replay keep the active camera, overrides, zones and everything in the game domain. After a seek an unscaled blend is finished at once; an unscaled shake keeps its trauma.


id: camera/scripting icon: file.script parent: camera order: 7 title: Scripting

Scripting

import "aego/camera"

func OnCollisionEnter(other NodeRef, c physics.Contact) {
  if c.Normal.Y < -0.5 && c.NormalImpulse > 8 {
    camera.Shake(0.35)
  }
}

func Boss() coroutine {
  yield camera.BlendTo(BossCam, 1.5)
  yield waitSeconds(3)
  camera.Release(0)
}
Function
Active() NodeRef, ActiveOn(output) NodeRef the virtual camera that drives an output
BlendTo(camera, seconds) Blend make a camera active over priorities and blend to it; 0 uses its Blend Time
Cut(camera) the same without a blend
Release(seconds) Blend hand the main output back to priorities
Shake(trauma), ShakeOn(output, trauma) add trauma
Trauma() float trauma of the main output
LogicalPose() Pose pose without shake and pixel snapping
RenderPose() Pose pose the main output was drawn with
ScreenToWorld(p), WorldToScreen(p) conversions with the last drawn view, in output pixels

Blend has Done(), Running(), Stop() and the signal Completed(finished bool); yield waits for it. Component methods: VirtualCamera2D.IsActive(), CameraFollow2D.Snap() and CameraShake2D.Add(trauma).

Each output keeps its own trauma:

package demo

import (
    "aego/camera"
    "aego/log"
)

script Outputs {
    func Ready() {
        camera.Shake(0.3)
        camera.ShakeOn("map", 0.9)
        log.Info("main " + string(camera.Trauma()))
        log.Info("active camera is nil: " + string(camera.Active() == nil))
    }
}

id: camera/editor icon: tool.move parent: camera order: 8 title: Editor tools

Editor tools

  • Handles. Select a virtual camera to drag its bounds, a zone to drag its area. A follow camera shows its frame, its deadzone and a line to its target. Every drag is one undo step.
  • Camera → Camera Frames turns the frames on and off in the Scene View and in the Game View during Play, where the deadzone of the active camera, its bounds and the zones are drawn over the game.
  • Inspector during Play. The virtual camera shows whether it is active, its priority, blend progress and trauma. Look Through moves the Scene View to its pose. Camera Follow 2D has Snap to Target.
  • Problems lists follow and shake components without a virtual camera (CAM001), zones without a camera (CAM002), empty rectangles (CAM003) and follows without a target (CAM004).

id: camera/go icon: code.brackets parent: camera order: 9 title: Go

Go

cams := sdk.CamerasOf(ctx)
cams.Shake("", 0.5)
if k, err := cams.BlendTo(bossCam, 1.5, false); err == nil {
  cams.OnBlendCompleted(k, func(finished bool) { ... })
}

Service has Active, BlendTo, Release, Shake, Trauma, Logical, Render, View and Snap, all keyed by output name; "" is the main output.

The module publishes the camera2d.adjust contract; render2d.cameras asks it to adjust the view of every Camera 2D. The system camera.update (PostUpdate, also while paused) picks cameras, advances blends and zones and fades trauma; follow and the final pose are computed when the view is built, once per output and frame. A module can scale the shake by providing camera2d.accessibility with the IDs of its settings.