Skip to content

Templates

Each scaffold tool of the MCP writes one of these files into a game, with Feature replaced by the name it was given and feature by that name in kebab case, and a page stub beside it under the game’s wiki/ folder. The registry serves each as an item under /r, so game add behaviour writes the same file unrenamed. The files live in packages/templates, where the typecheck and lint read them against the engine.

add_behaviour writes src/behaviours/<Name>.tsx: a trait, a Behaviour that says where it runs, and the component that adds the trait to the nearest Entity.

src/behaviours/Feature.tsx
import { trait } from "koota";
import {
RunContext,
useBehaviour,
type Behaviour,
} from "@daniel-zarinski/engine";
/** What Feature does to its entity, in one line. */
export const FeatureTrait = trait({ speed: 1 });
export const FeatureBehaviour: Behaviour<typeof FeatureTrait> = {
trait: FeatureTrait,
runsOn: RunContext.Client,
wiki: "/behaviours/feature/",
source: "src/behaviours/Feature.tsx",
};
export interface FeatureProps {
/** What speed means, and its unit. */
speed: number;
}
export function Feature(props: FeatureProps) {
useBehaviour(FeatureBehaviour, props);
return null;
}

add_entity writes src/components/<Name>.tsx: an Entity with a mesh and one behaviour to start from.

src/components/Feature.tsx
import { Entity, Spin, type EntityProps } from "@daniel-zarinski/engine";
/** What Feature is in the world, in one line. */
export function Feature({ position }: Pick<EntityProps, "position">) {
return (
<Entity position={position}>
<Spin speed={1} />
<mesh>
<boxGeometry args={[0.5, 0.5, 0.5]} />
<meshStandardMaterial color="gold" />
</mesh>
</Entity>
);
}

add_scene writes src/scenes/<Name>.tsx and returns the import and the <Scene> line to paste into src/app/app.tsx, as Scene describes.

src/scenes/Feature.tsx
import {
Camera,
CameraTarget,
Hud,
Panel,
Player,
Slot,
Text,
World,
} from "@daniel-zarinski/engine";
/** What happens in the feature scene, in one line. */
export function Feature() {
return (
<World map="meadow">
<Player />
<Camera follow={CameraTarget.Player} />
<Hud>
<Panel slot={Slot.Bottom}>
<Text>Feature</Text>
</Panel>
</Hud>
</World>
);
}

add_panel writes src/devtools/<Name>Panel.tsx and returns the import and the entry to paste into the panels list in src/app/devtools.tsx, as Devtools describes.

src/devtools/FeaturePanel.tsx
import { Stat } from "@daniel-zarinski/devtools";
/** What the Feature panel shows, in one line. */
export function FeaturePanel() {
return <Stat value="-" unit="feature" />;
}

add_shader writes src/shaders/<Name>.tsx: a fragment that includes one LYGIA function, and a <Name>Material component that draws it as the material of the mesh it sits in, as Shader describes. Put the material inside a mesh, then edit the fragment and its uniforms.

src/shaders/Feature.tsx
import { Shader } from "@daniel-zarinski/engine";
// Edit from void main() on. The engine declares uTime, uPointer and vUv
// ahead of it, and one uniform per uniforms key below.
const fragment = `#include "lygia/generative/snoise.glsl"
void main() {
float noise = 0.5 + 0.5 * snoise(vec3(vUv * uScale, uTime * uSpeed));
gl_FragColor = vec4(vec3(noise), 1.0);
}`;
export interface FeatureMaterialProps {
/** How fast the pattern changes, per second of world time. */
speed?: number;
/** How many cells of the pattern cross the mesh. */
scale?: number;
}
/** The material of the mesh it sits in: what Feature draws, in one line. */
export function FeatureMaterial({
speed = 0.5,
scale = 4,
}: FeatureMaterialProps) {
return (
<Shader
fragment={fragment}
uniforms={{ uSpeed: speed, uScale: scale }}
/>
);
}

add_map writes src/maps/<name>.json: a flat, empty map, 64 m across unless size says otherwise, or with from: "meadow" a copy of the engine’s meadow. It has no page stub. It returns the registerMaps line to paste into src/app/app.tsx only while the app file lacks one; every game template carries it. game add map <name> [--size <metres>] [--from meadow] does the same from the cli. Open the game with ?map=<name> in development to see the map alone, as Devtools describes.

src/maps/Feature.json
{
"seed": 1,
"size": 64,
"cell": 1,
"hills": { "amplitude": 0, "wavelength": 40, "detail": 0 },
"scatter": {
"treeRound": 0,
"treeTall": 0,
"bushRound": 0,
"rockBoulder": 0,
"rockFlat": 0,
"grassTuft": 0,
"seedOffset": 0
},
"regions": {},
"paths": {},
"views": {},
"props": {},
"rim": 0
}