Initial commit: Game Maker phase 1

Canvas-only 2D game editor/runtime built on PixiJS v8 + Vite + TypeScript.

- core: asset model (Scene/Camera/Sprite), scene objects with sparse
  overrides, property schema + resolve, command stack (undo/redo),
  serialization and project store interface (16 unit tests)
- ui: custom in-canvas widget toolkit (panels, fields, color picker,
  list/scroll, popup) with a hidden <input> keyboard bridge
- editor: asset/hierarchy panels, schema-driven inspector, toolbar
- viewport: box rendering, camera frame, selection gizmo, drag/zoom/pan
- runtime: world/entity instantiation for play mode, input, camera follow
- api: PHP JSON project store with revision counter (409 on conflict)
- tools: CDP-based e2e smoke test

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-07 10:07:06 +02:00
commit 5a46124454
65 files changed
+7193

No files matched your search

+47
View File
@@ -0,0 +1,47 @@
import type { Asset, SceneAsset } from './Asset';
import { isSceneAsset } from './Asset';
import type { SceneObject } from './SceneObject';
/** Bumped whenever the persisted shape changes; the loader migrates older documents. */
export const PROJECT_FORMAT_VERSION = 1;
export interface Project {
formatVersion: number;
id: string;
name: string;
assets: Asset[];
/** Scene shown in the viewport and instantiated on Play. Empty string = none. */
activeSceneId: string;
}
export function createEmptyProject(id: string, name: string): Project {
return { formatVersion: PROJECT_FORMAT_VERSION, id, name, assets: [], activeSceneId: '' };
}
export function findAsset(project: Project, assetId: string): Asset | undefined {
return project.assets.find((a) => a.id === assetId);
}
export function findScene(project: Project, sceneId: string): SceneAsset | undefined {
const asset = findAsset(project, sceneId);
return asset && isSceneAsset(asset) ? asset : undefined;
}
export function listScenes(project: Project): SceneAsset[] {
return project.assets.filter(isSceneAsset);
}
export function findSceneObject(scene: SceneAsset, objectId: string): SceneObject | undefined {
return scene.objects.find((o) => o.id === objectId);
}
/** Every scene object (across all scenes) that references the given asset. */
export function findUsages(project: Project, assetId: string): { scene: SceneAsset; object: SceneObject }[] {
const usages: { scene: SceneAsset; object: SceneObject }[] = [];
for (const scene of listScenes(project)) {
for (const object of scene.objects) {
if (object.assetId === assetId) usages.push({ scene, object });
}
}
return usages;
}