Skip to content

Latest commit

 

History

History
428 lines (346 loc) · 10.4 KB

File metadata and controls

428 lines (346 loc) · 10.4 KB

03 – Engine API Reference

All types are TypeScript. The library is built with tsc and ships .d.ts files. Consumers work exclusively with the types and classes described here.


Game

class Game {
  constructor(
    baseURLorDirHandle: string | FileSystemDirectoryHandle,
    options?: GameOptions
  );

  // Async initialisation – MUST be called before loadScene()
  // Note: named with underscore prefix to match the upstream reference repo.
  // Alias `init()` may be used in future versions.
  _init(): Promise<void>;

  // Load a named scene (must match a key in game.json "scenes")
  loadScene(sceneName: string): Promise<void>;

  // Register custom GameObject subclasses keyed by type name
  registerGameObjectClasses(map: Record<string, typeof GameObject>): void;

  // Asset loading (delegates to AssetStore)
  loadAsset(path: string): Promise<Asset>;

  // Read-only references
  readonly scene: Scene | null;
  readonly renderer: Renderer;
  readonly inputManager: InputManager;
  readonly assetStore: AssetStore;
  readonly networkManager: NetworkManager | null;
}

GameOptions

interface GameOptions {
  rendererOptions?: RendererOptions;
  assetOptions?:    AssetOptions;
  inputOptions?:    InputOptions;
  disablePhysics?:  boolean;
  networkOptions?:  NetworkOptions;
}

interface RendererOptions {
  width?:               number;
  height?:              number;
  pixelRatio?:          number;
  setupFullScreenCanvas?: boolean;
  canvas?:              HTMLCanvasElement;
  cameraOptions?:       CameraOptions;
  beforeRender?:        (args: { deltaTimeInSec: number; time: number }) => void;
}

interface CameraOptions {
  fov?:    number;  // default 50
  aspect?: number;  // default width/height
  near?:   number;  // default 0.01
  far?:    number;  // default 1000
}

interface AssetOptions {
  dracoLoaderOptions?: DracoLoaderOptions;
}

interface InputOptions {
  wsadMovement?: boolean;  // default true
  mouseOptions?: { usePointerLock?: boolean };
}

interface NetworkOptions {
  serverURL?: string;   // e.g. "http://localhost:3333"
  autoConnect?: boolean;
}

Scene

class Scene {
  constructor(game: Game, jsonAssetPath?: string);

  load(): Promise<void>;
  unload(): void;

  // Scene-graph mutation (available after load())
  addGameObject(options: GameObjectJSON): GameObject;
  removeGameObject(id: string): void;
  findGameObjectById(id: string): GameObject | undefined;
  findGameObjectsByTag(tag: string): GameObject[];
  findGameObjectByName(name: string): GameObject | undefined;

  // Environment
  setFog(fog: FogJSON | THREE.Fog | null): void;
  setBackground(color: string | THREE.Texture): void;
  setGravity(gravity: Vector3Data): void;

  // Audio
  playSound(name: string): void;
  stopSound(name: string): void;

  // Per-frame (called by Renderer loop)
  beforeRender(deltaTimeInSec: number): void;

  // Apply the base server world snapshot (iterates snapshot.entities and updates positions).
  // Games with custom schemas should process their extra fields in their own network listener.
  applyWorldSnapshot(snapshot: WorldSnapshot): void;

  // Serialise current in-memory state back to SceneJSON
  toJSON(): SceneJSON;

  // Public properties
  readonly name: string;
  readonly threeJSScene: THREE.Scene;
  readonly rapierWorld: RAPIER.World;
  readonly gameObjects: GameObject[];
  readonly game: Game;
  active: boolean;
}

SceneJSON

interface SceneJSON {
  background?: string | null;
  fog?:        FogJSON | null;
  gravity?:    Vector3Data;
  lights?:     SceneLightJSON[];
  sounds?:     SceneSoundJSON[];
  gameObjects?: GameObjectJSON[];
}

interface FogJSON {
  color: string;
  near:  number;
  far:   number;
}

interface SceneLightJSON {
  type:       string;   // 'AmbientLight' | 'DirectionalLight' | 'PointLight' | ...
  color?:     string;
  intensity?: number;
  position?:  Vector3Data;
}

interface SceneSoundJSON {
  name:         string;
  assetPath:    string;
  loop?:        boolean;
  autoplay?:    boolean;
  volume?:      number;
  playbackRate?: number;
}

GameObject

class GameObject {
  constructor(parent: Scene | GameObject, options?: GameObjectJSON);

  // Lifecycle
  load(): Promise<void>;
  unload(): void;
  reset(json?: GameObjectJSON): void;

  // Per-frame (override in subclasses)
  beforeRender(deltaTimeInSec: number): void;

  // Hierarchy
  getScene(): Scene;
  addChild(options: GameObjectJSON): GameObject;
  removeChild(id: string): void;

  // Component access
  getComponent<T extends Component>(type: string): T | undefined;
  getComponents<T extends Component>(type: string): T[];
  addComponent(json: ComponentJSON): Component;

  // Serialise
  toJSON(): GameObjectJSON;

  // Static registry
  static registerClassForComponentType(type: string, klass: typeof Component): void;

  // Properties
  readonly id: string;
  readonly type: string | null;
  name: string;
  tags: string[];
  readonly threeJSGroup: THREE.Group;
  readonly parent: Scene | GameObject;
  readonly components: Component[];
  readonly gameObjects: GameObject[];
  loaded: boolean;
}

GameObjectJSON

interface GameObjectJSON {
  type?:       string;
  name?:       string;
  tags?:       string[];
  userData?:   Record<string, unknown>;
  position?:   Vector3Data;
  rotation?:   EulerValues;
  scale?:      Vector3Data;
  components?: ComponentJSON[];
  children?:   GameObjectJSON[];
}

Component (abstract)

abstract class Component {
  constructor(gameObject: GameObject, jsonData: ComponentJSON);

  load(): Promise<void>;
  beforeRender(args: { deltaTimeInSec: number }): void;

  getName(): string | undefined;
  getType(): string;

  readonly gameObject: GameObject;
  readonly jsonData: ComponentJSON;
}

interface ComponentJSON {
  type: string;
  name?: string;
  [key: string]: unknown;
}

Built-in Component Types

ModelComponent

interface ModelComponentJSON extends ComponentJSON {
  type: 'model';
  assetPath: string;      // path to .gltf or .glb
  position?: Vector3Data;
  scale?:    Vector3Data;
  rotation?: EulerValues;
}

Loads the GLTF, clones the scene graph, and appends it to gameObject.threeJSGroup.

RigidBodyComponent

interface RigidBodyComponentJSON extends ComponentJSON {
  type: 'rigidBody';
  rigidBodyType: 'dynamic' | 'fixed' | 'kinematicPositionBased' | 'kinematicVelocityBased';
  enabledTranslations?: { x: boolean; y: boolean; z: boolean };
  enabledRotations?:    { x: boolean; y: boolean; z: boolean };
  colliders: ColliderData[];
}

LightComponent

interface LightComponentJSON extends ComponentJSON {
  type:      'light';
  lightType: 'AmbientLight' | 'DirectionalLight' | 'HemisphereLight' |
             'PointLight' | 'RectAreaLight' | 'SpotLight';
  color?:     string;
  intensity?: number;
  position?:  Vector3Data;
}

SoundComponent

interface SoundComponentJSON extends ComponentJSON {
  type:          'sound';
  assetPath:     string;
  name:          string;
  loop?:         boolean;
  autoplay?:     boolean;
  volume?:       number;
  playbackRate?: number;
  refDistance?:  number;
  rolloffFactor?: number;
  distanceModel?: 'linear' | 'inverse' | 'exponential';
  maxDistance?:  number;
}
// Extra method exposed at runtime:
// soundComponent.playSound(delayInSec?, detune?)

Renderer

class Renderer {
  constructor(game: Game, options?: RendererOptions);

  getCanvas(): HTMLCanvasElement;
  getCameraAudioListener(): THREE.AudioListener;

  // Start/stop the animation loop
  start(): void;
  stop(): void;

  // Direct access (read-only in normal usage)
  readonly threeJSCamera: THREE.Camera;
  readonly threeJSRenderer: THREE.WebGLRenderer;
}

AssetStore

class AssetStore {
  constructor(baseURLorDirHandle: string | FileSystemDirectoryHandle, options?: AssetOptions);

  load(path: string): Promise<Asset>;
  get(path: string): Asset | undefined;
  unload(path: string): void;
  unloadAll(): void;
}

InputManager

class InputManager {
  readVerticalAxis(): number;    // -1.0 (forward) to +1.0 (back)
  readHorizontalAxis(): number;  // -1.0 (left) to +1.0 (right)
  isKeyDown(key: string): boolean;
  getMouseDeltaX(): number;
  getMouseDeltaY(): number;
  resetMouseDeltas(): void;
  isGamepadButtonDown(button: number | string): boolean;
  getGamepadAxis(index: number): number;
}

Settings

class Settings {
  static load(): void;
  static get(key: string): unknown;
  static set(key: string, value: unknown): void;
  static reset(): void;
  static on(event: 'CHANGE', listener: () => void): void;
  static off(event: 'CHANGE', listener: () => void): void;
}

Shared Type Helpers

interface Vector3Data  { x?: number; y?: number; z?: number; }
interface EulerValues  { x?: number; y?: number; z?: number; order?: string; }

interface ColliderData {
  type: ColliderType;
  density?:      number;
  friction?:     number;
  restitution?:  number;
  sensor?:       boolean;
  // shape-specific fields:
  hx?: number; hy?: number; hz?: number;      // cuboid
  radius?: number;                             // ball, capsule
  halfHeight?: number;                         // capsule, cylinder
  vertices?: Float32Array;                     // convexHull, trimesh
  indices?: Uint32Array;                       // trimesh
  borderRadius?: number;                       // round* variants
}

type ColliderType =
  | 'ball'
  | 'capsule'
  | 'cuboid'
  | 'cylinder'
  | 'cone'
  | 'trimesh'
  | 'heightfield'
  | 'convexHull'
  | 'roundCuboid'
  | 'roundCylinder'
  | 'roundCone'
  | 'roundConvexHull';

Asset note: CubeTextureAsset (listed in the folder tree, packages/engine/src/assets/CubeTextureAsset.ts) handles six-face cube-map textures for skyboxes and environment mapping. It loads six images via THREE.CubeTextureLoader and returns a THREE.CubeTexture. It is not yet included in the AssetStore auto-detection by extension — consumers must call assetStore.load('path/to/cubemap.json') where the JSON contains the six face paths.


Lifecycle Example

import { Game } from '@tge/engine';

const game = new Game('/assets', {
  rendererOptions: { setupFullScreenCanvas: true }
});

await game._init();
await game.loadScene('level1');

// The engine loop is now running via requestAnimationFrame inside Renderer.