Skip to content

GameComponents in action

tutorial lesson Beginner ⏱ 12 min vgai 0.1.0

Prerequisites: Get Started (install + first look)

What you'll learn

  • Understand that the Three.js Object3D IS the entity
  • Write a GameComponent with init / update / dispose
  • Know which phase a component's update() runs in
  • Register a component and see it tick in play mode

What you'll build: A mental model of the GameComponent lifecycle, seen running in a component-rich scene.

Walkthrough — GameComponents in action · generated by site/media/lessons/foundations-components.lesson.ts (5 steps)

The whole engine rests on one idea: the Three.js Object3D is the entity, and behavior is attached to it with a GameComponent. This lesson makes that concrete — first the shape of a component, then seeing components tick live.

A GameComponent (packages/engine/src/ecs/game-component.ts) is a small class with a lifecycle:

// A minimal component: spin an object every frame.
import { GameComponent } from '@engine/ecs/game-component';
import { z } from 'zod';
export class Spin extends GameComponent {
static phase = 'gameLogic' as const; // when update() runs
static schema = z.object({ speed: z.number().default(1) }); // authored field
speed = 1;
init(ctx) { /* runs once, after the entity + physics exist */ }
update(dt, ctx) { this.object3D.rotation.y += this.speed * dt; }
dispose(ctx) { /* clean up anything you created */ }
}
  • this.object3D is the entity — there is no separate handle.
  • static phase picks which phase update() runs in (default gameLogic).
  • static schema (Zod) declares fields an author can set in the scene; they’re validated and assigned on load.
  • init / update / dispose are the lifecycle; sensor overlaps also call onTriggerEnter / onTriggerExit.

Register the class by name in the component registry, then reference it from a scene’s components — the loader validates the data through schema.parse() and attaches an instance (the ComponentManager ticks it each frame in its phase).

  1. Open a scene whose entities have GameComponents. Run VGAI_PROJECT=examples/editor-tutorial npm run dev — the project’s scene loads by default.

    A component-rich scene loaded
    A scene whose entities carry components.
  2. Enter play mode — each component’s update() runs every tick. Press Play; the ComponentManager begins ticking every attached component in its phase.

    The scene running in play mode
    Components tick once play starts.
  3. Watch component-driven animation and physics. The motion you see is components mutating their object3D each frame.

    Component-driven motion in play mode
    Per-frame update() in action.
  4. Components keep ticking in their phase. Each runs in the fixed phase order — input → … → gameLogic → … → render.

  5. Stop play mode. Press Stop (or Escape) — dispose() runs and the runtime tears down.

No install needed. Open the Code Playground — it starts with a Spinner GameComponent and runs your code live in the real engine (your work autosaves to your browser). Even if you’ve never written code, you can change one number and watch it happen. The starter’s update(dt) already spins, bobs, and shifts color:

update(dt) {
this.t += dt;
this.object3D.rotation.y += dt * 1.5; // spin speed
this.object3D.rotation.x += dt * 0.6;
this.object3D.position.y = Math.sin(this.t * 2) * 0.4; // bob height
}

Change one thing, press Run, watch it:

  1. Spin faster — make 1.5 bigger, e.g. dt * 6.
  2. Bob higher — make the last number bigger: Math.sin(this.t * 2) * 1.5.
  3. Bob faster — make the 2 bigger: Math.sin(this.t * 6) * 0.4.

Then reload the page — your edited code is still there. That’s the whole loop: edit → Run → see it → it persists. You just wrote and ran real engine code with zero setup.

Recap

New functionality

  • Saw a component-rich scene run
  • Read the GameComponent lifecycle

New concepts & skills

  • The Object3D IS the entity (no separate ECS)
  • update() runs each fixed tick in the component's phase
  • Static schema authors fields; the registry + ComponentManager wire and tick instances
  • State on the instance survives hot reload

Next lesson → Manual: Entities & GameComponents