docs / core-concepts / sprites-and-transforms

Sprites & Transforms

Sprite2D and Transform2D in @g2w/coreare a minimal, renderer-independent sprite model: a typed texture handle, a transform, a normalized anchor, and the visual properties games need. Core never inspects or destroys the texture — it is a genericTTexture the caller supplies, typically from a renderer's asset API.

The model is plain mutable data

Transform2D holds position (logical pixels), rotation (radians — clockwise in PixiJS screen space, because y points down), and scale multipliers. Properties are plain mutable numbers: gameplay code mutates them, and nothing touches the renderer until you say so:

src/game.ts
import { Sprite2D, Transform2D } from "@g2w/core";

const logo = new Sprite2D({
  texture,                    // typed by the caller
  transform: new Transform2D({ x: 16, y: 32 }),
  anchor: { x: 0.5, y: 0.5 },  // rotate around center
  zIndex: 2,
});

// plain mutable data — no observers, no proxies
logo.transform.rotation += deltaTime;
logo.alpha = 0.8;
AnchorThe anchor is a property of the sprite, not the transform: transforms are shared between sprites and game objects, and a game object has no visual. Anchor2D.set() is the only mutation path and rejects out-of-range values instead of clamping.

Project it with a view

createSpriteView from @g2w/renderer-pixi attaches a PixiJS Sprite to the stage as a pure projection of your model. There is no ticker, subscription, or second loop — you callsync() once per frame, after the model changes:

inside a scene
import { createSpriteView } from "@g2w/renderer-pixi";

const view = createSpriteView({ sprite: logo, renderer });

// once per frame, after the model changes:
update(deltaTime) {
  // ...mutate logo.transform here
  view.sync();
},

// teardown — the texture survives, only the display object dies
view.dispose();

Ownership splits cleanly: the model is yours, the view's display object belongs to whoever created it. dispose() removes and destroys the display object — never the texture — and never touches the stage or other views. Disposal is idempotent. Each call tocreateSpriteView makes one new display object, so dispose a view before creating another for the same model.

Share one transform between sprites

Supplying an existing Transform2D makes the sprite reference your instance instead of creating its own — several sprites can be driven by one transform:

src/boss.ts
const bossTransform = new Transform2D({ x: 320, y: 180 });
const bossBody = new Sprite2D({
  texture: bodyTex, transform: bossTransform,
});
const bossShadow = new Sprite2D({
  texture: shadowTex, transform: bossTransform, alpha: 0.4,
});

// one mutation moves both sprites
bossTransform.x += 40 * deltaTime;

Scope

Intentionally unsupported: parent/child hierarchies, tint, blend modes, animation timelines, and entity components — roadmap items, not hidden APIs.

Next steps

See Assets to load the textures these sprites render, orarchitecture notes for the renderer contract in full.