Skip to content

Installation ​

Prerequisites ​

  • Node.js and npm (or your preferred package manager)
  • TypeScript knowledge -- SAGE is written in TypeScript and expects you to use it
  • A bundler like Vite (recommended), Webpack, or similar

Install Packages ​

SAGE requires BabylonJS, its loaders, and Havok as peer dependencies. @skewedaspect/sage-core is a dependency of @skewedaspect/sage, and game code that augments the event map or uses the provided behaviors imports from it directly:

bash
npm install @skewedaspect/sage @skewedaspect/sage-core @babylonjs/core @babylonjs/havok @babylonjs/loaders

If you are building a Vue application, also install the Vue integration package:

bash
npm install @skewedaspect/sage-vue

TypeScript Configuration ​

Make sure your tsconfig.json includes these settings:

json
{
    "compilerOptions": {
        "target": "ES2023",
        "module": "ESNext",
        "moduleResolution": "bundler",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "lib": ["ESNext", "DOM", "DOM.Iterable"]
    }
}

Quick Start: Spinning Cube ​

The fastest way to verify your setup is to get a cube on screen and spinning. This uses SAGE's createGameEngine() factory and BabylonJS directly for scene setup.

HTML ​

Create an index.html with a full-viewport canvas:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SAGE - Hello Cube</title>
    <style>
        html, body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
        #gameCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    <script type="module" src="./src/main.ts"></script>
</body>
</html>

Game Code ​

Create src/main.ts:

typescript
import { createGameEngine } from '@skewedaspect/sage';
import {
    ArcRotateCamera,
    Color3,
    Color4,
    HemisphericLight,
    MeshBuilder,
    StandardMaterial,
    Vector3,
} from '@babylonjs/core';

async function main() : Promise<void>
{
    const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;

    // Create the engine. The second argument is the entity registry; this demo spawns nothing.
    const engine = await createGameEngine(canvas, {});

    // Create a scene
    const scene = engine.engines.sceneEngine.createScene();
    scene.clearColor = new Color4(0.12, 0.12, 0.14, 1);

    // Camera
    const camera = new ArcRotateCamera(
        'camera',
        Math.PI / 4,
        Math.PI / 3,
        5,
        Vector3.Zero(),
        scene
    );
    camera.attachControl(canvas, true);

    // Light
    new HemisphericLight('light', new Vector3(0, 1, 0), scene).intensity = 0.8;

    // Cube
    const cube = MeshBuilder.CreateBox('cube', { size: 1.5 }, scene);
    const material = new StandardMaterial('cubeMat', scene);
    material.diffuseColor = new Color3(0.5, 0.17, 0.17);
    cube.material = material;

    // Spin the cube every frame
    engine.managers.gameManager.registerFrameCallback((dt : number) =>
    {
        cube.rotation.x += 0.3 * dt;
        cube.rotation.y += 0.6 * dt;
        scene.render();
    });

    // Start the engine
    await engine.start();
}

main();

Run it with Vite:

bash
npx vite

You should see a dark scene with a red cube rotating. Click and drag to orbit the camera.

Troubleshooting

"Cannot find module '@skewedaspect/sage'" -- make sure the package installed correctly and the import path matches the package name exactly.

Black screen, nothing renders -- check that the canvas element is actually sized (a 0x0 canvas renders nothing) and that engine.start() was called.

Behaviors not responding to events -- a behavior only hears the types listed in its static events, and every type must be declared in the game's EventMap augmentation. Verify the emitter and the subscriber agree on spelling.

A TypeError naming a method that exists in the types -- simulation.attach is not a function, or similar, after upgrading SAGE. Both packages are ESM only, so Vite pre-bundles them into node_modules/.vite, and that cache survives an install: TypeScript reads the new package while the browser loads the pre-bundle built from the old one. Clear it and restart the dev server:

bash
rm -rf node_modules/.vite

Vue Integration ​

If you are using Vue 3, the @skewedaspect/sage-vue package provides a SageCanvas component that handles engine creation, canvas management, and resize handling for you.

vue
<template>
    <SageCanvas @engine-ready="onEngineReady" :options="{ logLevel: 'info' }" />
</template>

<!--------------------------------------------------------------------------------------------------------------------->

<style scoped>
    /* SageCanvas fills its parent, so size the parent */
</style>

<!--------------------------------------------------------------------------------------------------------------------->

<script setup lang="ts">
    import type { GameEngine } from '@skewedaspect/sage';
    import { SageCanvas } from '@skewedaspect/sage-vue';

    function onEngineReady(engine : GameEngine) : void
    {
        // Set up your scene and start the game loop here.
        // The engine is fully initialized -- create scenes,
        // register frame callbacks, and call start().
    }
</script>

<!--------------------------------------------------------------------------------------------------------------------->

SageCanvas provides a scoped slot with loading, error, and engine properties, so you can overlay UI on top of the canvas:

vue
<SageCanvas @engine-ready="onEngineReady">
    <template #default="{ loading }">
        <div v-if="loading">Loading...</div>
    </template>
</SageCanvas>

The entity registry goes in through the :entity-classes prop, and engine options through :options.

TIP

Child components can access the engine instance using the useSageEngine() composable from @skewedaspect/sage-vue, without needing to pass it as a prop. useSageEntity, useSageEntities, useSageEvent, useSageAction, and useSageTimer read state, subscribe to events, and schedule timers the same way; see Vue 3 Integration.

Next Steps ​

This page got you from zero to spinning cube. For the full walkthrough with explanations of every piece, see the Hello Cube Guide. From there:

  • Architecture -- understand how the engine is put together
  • Entities -- learn the entity system and compiler-checked composition
  • Entity Behaviors -- build a game from behaviors and entity classes
  • Input System -- set up keyboard, mouse, and gamepad input
  • Levels -- load scenes from YAML and GLB files

Released under the MIT License.