docs/style.md, docs/glossary.md, style checks DOC010–DOC023 and docs report; the site in a light theme; the Editor, Aego Script, Gameplay, Physics, Scenes, Rendering, Tools and Reference sections rewritten.
289 lines
12 KiB
Markdown
289 lines
12 KiB
Markdown
---
|
|
id: camera
|
|
try: try/CameraFollow2D.aescene
|
|
try_select: camera.CameraFollow2D
|
|
icon: comp.vcam
|
|
group: camera
|
|
group_title: Cameras 2D
|
|
group_icon: cat.camera
|
|
group_order: 55
|
|
title: Cameras 2D
|
|
order: 1
|
|
---
|
|
# Cameras 2D
|
|
|
|
A [Camera 2D](ref:engine.Camera2D) draws the view; virtual cameras decide where it looks. Every [Virtual Camera 2D](ref:camera.VirtualCamera2D) 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](ref:camera.VirtualCamera2D.Priority). Equal priorities pick the camera created first.
|
|
2. A [Camera Zone 2D](ref:camera.CameraZone2D) 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](ref:camera.VirtualCamera2D.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, the Director script raises **Close**, then **Tilt**, for two seconds each, and the view blends to them and back. Change Zoom, Blend Time or Blend Curve of Close in the Inspector and watch the next blend; the script is under the scene.
|
|
|
|
## Blends
|
|
|
|
When another camera becomes active, the view blends from the pose it shows now to the new camera over its [Blend Time](ref:camera.VirtualCamera2D.BlendTime), shaped by [Blend Curve](ref:camera.VirtualCamera2D.BlendCurve). 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](ref:camera.VirtualCamera2D.UseBounds) keeps the visible area inside [Bounds](ref:camera.VirtualCamera2D.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](ref:camera.VirtualCamera2D.Output) names another one. A Camera 2D shows the main output; add [Camera Brain 2D](ref:camera.CameraBrain2D) 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](ref:camera.CameraFollow2D) moves its virtual camera after [Target](ref:camera.CameraFollow2D.Target).
|
|
|
|
The target moves freely inside the **Deadzone**, a part of the view; (0.4, 0.4, 0.2, 0.2) is a centred fifth. Outside it the camera catches up at the speed set by **Damping**, and 0 follows rigidly.
|
|
|
|
**Lookahead** moves the camera ahead of a moving target, by that many seconds of its velocity. **Max Lookahead** limits how far, and **Lookahead Smoothing** how fast it changes. When the target jumps further than **Snap Distance**, for example after a teleport, the camera moves at once.
|
|
|
|
Every field, with its unit, is listed on the [Camera Follow 2D](ref:camera.CameraFollow2D) page.
|
|
|
|

|
|
|
|
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](ref:camera.CameraZone2D) changes the camera while a target is inside its [Area](ref:camera.CameraZone2D.Area), a rectangle in the zone node's space. Inside, [Camera](ref:camera.CameraZone2D.Camera) gets the zone's [Priority](ref:camera.CameraZone2D.Priority), [Zoom](ref:camera.CameraZone2D.Zoom) and [Bounds](ref:camera.CameraZone2D.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
|
|
try: try/CameraShake2D.aescene
|
|
try_select: camera.CameraShake2D
|
|
icon: comp.shake
|
|
parent: camera
|
|
order: 5
|
|
title: Shake
|
|
---
|
|
# Shake
|
|
|
|
In Try it, the crate drops every two seconds and its script calls `camera.Shake(0.9)` when it lands. Raise Max Angle or lower Exponent and Decay on the camera to feel the difference.
|
|
|
|
Shake is driven by trauma, a value from 0 to 1 per output. `camera.Shake(0.4)` adds trauma, which fades by [Decay](ref:camera.CameraShake2D.Decay) per second. The view moves by up to [Max Offset](ref:camera.CameraShake2D.MaxOffset) and turns by up to [Max Angle](ref:camera.CameraShake2D.MaxAngle), scaled by trauma to the power [Exponent](ref:camera.CameraShake2D.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:
|
|
|
|
```aes run
|
|
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](ref:camera.VirtualCamera2D.TimeDomain) 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:
|
|
|
|
```aes run
|
|
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
|
|
|
|
The editor draws camera frames, deadzones and zones, and shows the state of the active camera during Play.
|
|
|
|
## Handles and frames
|
|
|
|
- Select a virtual camera to drag its bounds, or 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. During Play the Game View also draws the deadzone of the active camera, its bounds and the zones.
|
|
|
|
## 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**.
|
|
|
|
## Profiling and problems
|
|
|
|
- **Profiler → Camera** lists each output with its active virtual camera, zoom and blend progress.
|
|
- Problems lists follow and shake components without a virtual camera ([CAM001](ref:CAM001)), zones without a camera ([CAM002](ref:CAM002)), empty rectangles ([CAM003](ref:CAM003)) and follows without a target ([CAM004](ref: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.
|