Camera2D

The orthographic camera of a Scene2D: which part of the 2D world is on screen, how far it is zoomed in, and the conversion between screen and world points. Every scene owns one — scene.camera; you never construct a Camera2D. The 3D camera is Camera.

  • make the camera follow the player
  • zoom in for pixel art, pan or rotate the view
  • turn a screen point (a tap, a drag) into a point in the 2D world
  • find where a world point is on the screen

Platforms: iOS, Android, desktop, the web player, the le.codes viewer app; not on the web (Web Lite, lecodes web). In headless tests screenToWorld / worldToScreen work against a 1×1 view and return wrong points.

At a glance

TypeScript
async function main() {
  const scene = new Scene2D()
  scene.camera.zoom = 2                            // 2× magnification — pixel-art friendly
  scene.camera.position = [0, 0]                   // the world point at the center of the screen

  const tex = await Texture2D.load(asset('./hero.png'))
  const hero = new Sprite({ texture: tex })
  scene.add(hero)

  // a drag reports screen points; the camera turns them into world points
  scene.addEventListener('touchstart', ev => {
    ev.track({ onMove: p => { hero.position = scene.camera.screenToWorld(p.clientX, p.clientY) } })
  })
  scene.open()
}
main()

Pan, zoom and rotate the view: position, zoom, rotation

TypeScript
camera.position = [x, y]     // world units — the point at the center of the screen
camera.zoom = 2              // magnification; at 1, 1 world unit = 1 logical px. Default 1
camera.rotation = 15         // degrees, CCW. Default 0

Getters return fresh values; camera.position.x = 3 written directly compiles to a camera write, a stored copy stays a copy — see conventions.

rotation turns the camera, so the world turns the other way on screen: at 90, world +X points down the screen.

Make the camera follow the player

There is no follow(): an aspect on the player whose update(dt) — the late phase, after physics — eases the camera toward the node. It goes away with the node; there is nothing to dispose.

TypeScript
class Follow extends Aspect<'follow', Node2D> {
  camera!: Camera2D
  update(dt: number) {                             // late phase: after physics, before the draw
    this.camera.position = this.camera.position.lerp(this.node.worldPosition, 10 * dt)
  }
}

async function main() {
  const scene = new Scene2D()
  scene.camera.zoom = 2

  const tex = await Texture2D.load(asset('./hero.png'))
  const hero = new Sprite({ texture: tex }).aspect(Follow, { camera: scene.camera })
  scene.add(hero)
  scene.open()
}
main()

The built-in CameraFollow does the same and adds pixel snapping: use(CameraFollow, …) in a .scene2d.ts file, or hero.aspect(CameraFollow, { scene }) in code (which runs, but a project's type-check currently rejects it — see its note).

Turn a tap into a world point: screenToWorld / worldToScreen

Convert a tap or drag position on the screen to a point in the 2D world, and back.

TypeScript
camera.screenToWorld(screenX, screenY): Vec2   // logical px → world units
camera.worldToScreen(worldX, worldY): Vec2     // world units → logical px

screenToWorld turns a tap or drag point (clientX / clientY) into the world: feed it to scene.pick() or Physics2D.overlapPoint(). A scene's own click / touchstart already carry ev.worldX / ev.worldY. Screen Y points down, world Y points up — the conversion handles the flip.

Note

both use the size of the view the scene is drawn in, so they are meaningful once the scene is on screen — before its first frame they work against a 1×1 view.

See also