@donut-games/create-game 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -0
- package/template/base/.donut/mcp-config.json +4 -0
- package/template/base/README.md.tpl +70 -0
- package/template/base/_eslintrc.json +33 -0
- package/template/base/assets/.gitkeep +0 -0
- package/template/base/src/components/example-component.ts +27 -0
- package/template/base/src/systems/example-system.ts +28 -0
- package/template/base/tsconfig.json +15 -0
- package/template/pixi-2d/donut.json.tpl +6 -0
- package/template/pixi-2d/package.json.tpl +24 -0
- package/template/pixi-2d/src/scenes/default-scene.ts +43 -0
- package/template/three-3d/donut.json.tpl +6 -0
- package/template/three-3d/package.json.tpl +24 -0
- package/template/three-3d/src/scenes/default-scene.ts +40 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { initializeProject } from "@donut-games/cli";
|
|
8
|
+
function printUsage() {
|
|
9
|
+
const usage = [
|
|
10
|
+
"Usage: npx @donut-games/create-game <targetDirectory> [--2d|--3d] [--skip-install]",
|
|
11
|
+
"",
|
|
12
|
+
"Options:",
|
|
13
|
+
" --2d Scaffold a Pixi 2D game (default).",
|
|
14
|
+
" --3d Scaffold a Three.js 3D game.",
|
|
15
|
+
" --skip-install Do not run the post-scaffold dependency install.",
|
|
16
|
+
" --help, -h Show this message."
|
|
17
|
+
].join("\n");
|
|
18
|
+
console.log(usage);
|
|
19
|
+
}
|
|
20
|
+
function resolveTemplateDirectory() {
|
|
21
|
+
const shippedCandidate = fileURLToPath(new URL("../template", import.meta.url));
|
|
22
|
+
if (existsSync(shippedCandidate)) {
|
|
23
|
+
return shippedCandidate;
|
|
24
|
+
}
|
|
25
|
+
const devCandidate = fileURLToPath(new URL("../../template", import.meta.url));
|
|
26
|
+
if (existsSync(devCandidate)) {
|
|
27
|
+
return devCandidate;
|
|
28
|
+
}
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Unable to locate the create-game template directory. Checked:
|
|
31
|
+
${shippedCandidate}
|
|
32
|
+
${devCandidate}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function parseCommandLineArguments(rawArguments) {
|
|
36
|
+
let rendererTarget = "pixi-2d";
|
|
37
|
+
let skipInstall = false;
|
|
38
|
+
const positionalArguments = [];
|
|
39
|
+
for (const argument of rawArguments) {
|
|
40
|
+
if (argument === "--2d") {
|
|
41
|
+
rendererTarget = "pixi-2d";
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (argument === "--3d") {
|
|
45
|
+
rendererTarget = "three-3d";
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (argument === "--skip-install") {
|
|
49
|
+
skipInstall = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (argument.startsWith("--")) {
|
|
53
|
+
throw new Error(`Unknown option: ${argument}`);
|
|
54
|
+
}
|
|
55
|
+
positionalArguments.push(argument);
|
|
56
|
+
}
|
|
57
|
+
if (positionalArguments.length === 0) {
|
|
58
|
+
throw new Error("Missing <targetDirectory> argument.");
|
|
59
|
+
}
|
|
60
|
+
if (positionalArguments.length > 1) {
|
|
61
|
+
throw new Error(`Unexpected extra arguments: ${positionalArguments.slice(1).join(" ")}`);
|
|
62
|
+
}
|
|
63
|
+
return { targetDirectory: positionalArguments[0], rendererTarget, skipInstall };
|
|
64
|
+
}
|
|
65
|
+
async function main() {
|
|
66
|
+
const rawArguments = process.argv.slice(2);
|
|
67
|
+
if (rawArguments.includes("--help") || rawArguments.includes("-h") || rawArguments.length === 0) {
|
|
68
|
+
printUsage();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const parsedArguments = parseCommandLineArguments(rawArguments);
|
|
72
|
+
resolveTemplateDirectory();
|
|
73
|
+
const absoluteTargetDirectory = path.resolve(parsedArguments.targetDirectory);
|
|
74
|
+
const projectName = path.basename(absoluteTargetDirectory);
|
|
75
|
+
await initializeProject({
|
|
76
|
+
projectName,
|
|
77
|
+
rendererTarget: parsedArguments.rendererTarget,
|
|
78
|
+
targetDirectory: absoluteTargetDirectory,
|
|
79
|
+
skipInstall: parsedArguments.skipInstall
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
main().catch((error) => {
|
|
83
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
+
console.error(`create-game: ${message}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\nimport { initializeProject, type RendererTarget } from '@donut-games/cli';\n\n/**\n * Print CLI usage to stdout.\n */\nfunction printUsage(): void {\n const usage = [\n 'Usage: npx @donut-games/create-game <targetDirectory> [--2d|--3d] [--skip-install]',\n '',\n 'Options:',\n ' --2d Scaffold a Pixi 2D game (default).',\n ' --3d Scaffold a Three.js 3D game.',\n ' --skip-install Do not run the post-scaffold dependency install.',\n ' --help, -h Show this message.',\n ].join('\\n');\n console.log(usage);\n}\n\n/**\n * Resolve the `template/` directory that ships alongside this package.\n *\n * Shipped layout places `template/` next to `dist/` in the tarball, so the\n * first candidate works in production. The fallback targets the source-tree\n * layout used when the file is invoked directly out of `src/` during local\n * development (rare, but keeps dev ergonomics predictable).\n */\nfunction resolveTemplateDirectory(): string {\n const shippedCandidate = fileURLToPath(new URL('../template', import.meta.url));\n if (existsSync(shippedCandidate)) {\n return shippedCandidate;\n }\n const devCandidate = fileURLToPath(new URL('../../template', import.meta.url));\n if (existsSync(devCandidate)) {\n return devCandidate;\n }\n throw new Error(\n `Unable to locate the create-game template directory. Checked:\\n ${shippedCandidate}\\n ${devCandidate}`,\n );\n}\n\n/**\n * Minimal argv parser. Keeps the package dependency-free.\n */\nfunction parseCommandLineArguments(rawArguments: readonly string[]): {\n readonly targetDirectory: string;\n readonly rendererTarget: RendererTarget;\n readonly skipInstall: boolean;\n} {\n let rendererTarget: RendererTarget = 'pixi-2d';\n let skipInstall = false;\n const positionalArguments: string[] = [];\n\n for (const argument of rawArguments) {\n if (argument === '--2d') {\n rendererTarget = 'pixi-2d';\n continue;\n }\n if (argument === '--3d') {\n rendererTarget = 'three-3d';\n continue;\n }\n if (argument === '--skip-install') {\n skipInstall = true;\n continue;\n }\n if (argument.startsWith('--')) {\n throw new Error(`Unknown option: ${argument}`);\n }\n positionalArguments.push(argument);\n }\n\n if (positionalArguments.length === 0) {\n throw new Error('Missing <targetDirectory> argument.');\n }\n if (positionalArguments.length > 1) {\n throw new Error(`Unexpected extra arguments: ${positionalArguments.slice(1).join(' ')}`);\n }\n\n return { targetDirectory: positionalArguments[0], rendererTarget, skipInstall };\n}\n\n/**\n * Entry point. Parses argv, resolves the template directory, and delegates to\n * `initializeProject` from `@donut-games/cli`.\n */\nasync function main(): Promise<void> {\n const rawArguments = process.argv.slice(2);\n if (rawArguments.includes('--help') || rawArguments.includes('-h') || rawArguments.length === 0) {\n printUsage();\n return;\n }\n\n const parsedArguments = parseCommandLineArguments(rawArguments);\n // Template directory is resolved so step 5 can wire it into the copier.\n // Step 4's initializeProject still renders files via its internal functions.\n resolveTemplateDirectory();\n\n const absoluteTargetDirectory = path.resolve(parsedArguments.targetDirectory);\n const projectName = path.basename(absoluteTargetDirectory);\n\n await initializeProject({\n projectName,\n rendererTarget: parsedArguments.rendererTarget,\n targetDirectory: absoluteTargetDirectory,\n skipInstall: parsedArguments.skipInstall,\n });\n}\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`create-game: ${message}`);\n process.exit(1);\n});\n"],"mappings":";;;AAAA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,yBAA8C;AAKvD,SAAS,aAAmB;AAC1B,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACX,UAAQ,IAAI,KAAK;AACnB;AAUA,SAAS,2BAAmC;AAC1C,QAAM,mBAAmB,cAAc,IAAI,IAAI,eAAe,YAAY,GAAG,CAAC;AAC9E,MAAI,WAAW,gBAAgB,GAAG;AAChC,WAAO;AAAA,EACT;AACA,QAAM,eAAe,cAAc,IAAI,IAAI,kBAAkB,YAAY,GAAG,CAAC;AAC7E,MAAI,WAAW,YAAY,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR;AAAA,IAAoE,gBAAgB;AAAA,IAAO,YAAY;AAAA,EACzG;AACF;AAKA,SAAS,0BAA0B,cAIjC;AACA,MAAI,iBAAiC;AACrC,MAAI,cAAc;AAClB,QAAM,sBAAgC,CAAC;AAEvC,aAAW,YAAY,cAAc;AACnC,QAAI,aAAa,QAAQ;AACvB,uBAAiB;AACjB;AAAA,IACF;AACA,QAAI,aAAa,QAAQ;AACvB,uBAAiB;AACjB;AAAA,IACF;AACA,QAAI,aAAa,kBAAkB;AACjC,oBAAc;AACd;AAAA,IACF;AACA,QAAI,SAAS,WAAW,IAAI,GAAG;AAC7B,YAAM,IAAI,MAAM,mBAAmB,QAAQ,EAAE;AAAA,IAC/C;AACA,wBAAoB,KAAK,QAAQ;AAAA,EACnC;AAEA,MAAI,oBAAoB,WAAW,GAAG;AACpC,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AACA,MAAI,oBAAoB,SAAS,GAAG;AAClC,UAAM,IAAI,MAAM,+BAA+B,oBAAoB,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE;AAAA,EACzF;AAEA,SAAO,EAAE,iBAAiB,oBAAoB,CAAC,GAAG,gBAAgB,YAAY;AAChF;AAMA,eAAe,OAAsB;AACnC,QAAM,eAAe,QAAQ,KAAK,MAAM,CAAC;AACzC,MAAI,aAAa,SAAS,QAAQ,KAAK,aAAa,SAAS,IAAI,KAAK,aAAa,WAAW,GAAG;AAC/F,eAAW;AACX;AAAA,EACF;AAEA,QAAM,kBAAkB,0BAA0B,YAAY;AAG9D,2BAAyB;AAEzB,QAAM,0BAA0B,KAAK,QAAQ,gBAAgB,eAAe;AAC5E,QAAM,cAAc,KAAK,SAAS,uBAAuB;AAEzD,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA,gBAAgB,gBAAgB;AAAA,IAChC,iBAAiB;AAAA,IACjB,aAAa,gBAAgB;AAAA,EAC/B,CAAC;AACH;AAEA,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAQ,MAAM,gBAAgB,OAAO,EAAE;AACvC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@donut-games/create-game",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-game": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"template",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@donut-games/cli": "0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"tsup": "^8.3.0",
|
|
23
|
+
"typescript": "^5.7.0",
|
|
24
|
+
"@types/node": "^20.0.0"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup && chmod +x dist/index.js",
|
|
31
|
+
"clean": "rm -rf dist"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A Donut game. Two ways to build it: on your own, or with Claude.
|
|
4
|
+
|
|
5
|
+
## Quick reference
|
|
6
|
+
|
|
7
|
+
| Command | What it does |
|
|
8
|
+
| ------------------ | ---------------------------------------------------- |
|
|
9
|
+
| `npm run dev` | Start the dev server with HMR on http://localhost:5173 |
|
|
10
|
+
| `npm run validate` | Type-check, manifest-check, import-graph check |
|
|
11
|
+
| `npm run build` | Produce `dist/{{projectName}}-<version>.donut` |
|
|
12
|
+
| `npm run review` | Run the Donut submission review pipeline |
|
|
13
|
+
|
|
14
|
+
## Track 1 — Code it yourself
|
|
15
|
+
|
|
16
|
+
Edit files under `src/`. Vite reloads on save.
|
|
17
|
+
|
|
18
|
+
- `src/components/` — pure data. Import only from
|
|
19
|
+
`@donut-games/engine/core` and `@donut-games/engine/math`.
|
|
20
|
+
- `src/systems/` — behavior. Same import restriction as components.
|
|
21
|
+
- `src/scenes/default-scene.ts` — wires entities together. This is
|
|
22
|
+
the only place renderer-specific imports
|
|
23
|
+
(`@donut-games/engine/pixi` or `@donut-games/engine/three`) are
|
|
24
|
+
allowed.
|
|
25
|
+
|
|
26
|
+
Run `npm run validate` before every commit. It fails if:
|
|
27
|
+
|
|
28
|
+
- `donut.json` is missing a field.
|
|
29
|
+
- A component or system imports from a renderer subpath.
|
|
30
|
+
- A file imports the wrong renderer for this project's
|
|
31
|
+
`rendererTarget`.
|
|
32
|
+
|
|
33
|
+
## Track 2 — Author with Claude
|
|
34
|
+
|
|
35
|
+
This project ships a Donut MCP server. Install the CLI globally once:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g @donut-games/cli
|
|
39
|
+
claude mcp add donut -- donut-mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then start Claude Code from this directory. Leave `npm run dev`
|
|
43
|
+
running in a second terminal (Claude can also start it itself as a
|
|
44
|
+
background task). Ask for what you want:
|
|
45
|
+
|
|
46
|
+
> Add an enemy that spawns every three seconds and chases the player.
|
|
47
|
+
|
|
48
|
+
Claude has four tool categories through the MCP:
|
|
49
|
+
|
|
50
|
+
- **component-system** — create, edit, delete components and systems.
|
|
51
|
+
- **entity-scene** — edit the scene graph and default scene.
|
|
52
|
+
- **quality** — type-check, lint, validate imports.
|
|
53
|
+
- **runtime** — inspect the running game, send synthetic input,
|
|
54
|
+
read logs.
|
|
55
|
+
|
|
56
|
+
Claude is subject to the same import rules as you. Game logic may
|
|
57
|
+
only import from `@donut-games/engine/core` and
|
|
58
|
+
`@donut-games/engine/math`. The MCP enforces this before writing.
|
|
59
|
+
|
|
60
|
+
## Renderer target
|
|
61
|
+
|
|
62
|
+
This project targets `{{rendererTarget}}`. Switching targets means
|
|
63
|
+
changing `donut.json`, swapping the installed renderer dependency
|
|
64
|
+
(`pixi.js` ↔ `three`), and updating every
|
|
65
|
+
`@donut-games/engine/pixi` or `@donut-games/engine/three` import
|
|
66
|
+
under `src/scenes/`.
|
|
67
|
+
|
|
68
|
+
## Docs
|
|
69
|
+
|
|
70
|
+
https://donut.games/docs
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"parser": "@typescript-eslint/parser",
|
|
4
|
+
"plugins": [
|
|
5
|
+
"@typescript-eslint",
|
|
6
|
+
"import"
|
|
7
|
+
],
|
|
8
|
+
"extends": [
|
|
9
|
+
"eslint:recommended",
|
|
10
|
+
"plugin:@typescript-eslint/recommended",
|
|
11
|
+
"plugin:import/recommended",
|
|
12
|
+
"plugin:import/typescript"
|
|
13
|
+
],
|
|
14
|
+
"rules": {
|
|
15
|
+
"no-console": "warn",
|
|
16
|
+
"@typescript-eslint/no-unused-vars": "error",
|
|
17
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
18
|
+
"import/order": [
|
|
19
|
+
"warn",
|
|
20
|
+
{
|
|
21
|
+
"groups": [
|
|
22
|
+
"builtin",
|
|
23
|
+
"external",
|
|
24
|
+
"internal",
|
|
25
|
+
"parent",
|
|
26
|
+
"sibling",
|
|
27
|
+
"index"
|
|
28
|
+
],
|
|
29
|
+
"newlines-between": "always"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Component } from '@donut-games/engine/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example component demonstrating the minimal shape required by Donut.
|
|
5
|
+
* Components hold state only; no renderer imports are allowed here.
|
|
6
|
+
*/
|
|
7
|
+
export class ExampleComponent extends Component {
|
|
8
|
+
/** Arbitrary counter used by the example system. */
|
|
9
|
+
public tickCount: number = 0;
|
|
10
|
+
|
|
11
|
+
/** Deep clone required by the Component contract. */
|
|
12
|
+
public clone(): ExampleComponent {
|
|
13
|
+
const copy = new ExampleComponent();
|
|
14
|
+
copy.tickCount = this.tickCount;
|
|
15
|
+
return copy;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Serialize this component to plain JSON. */
|
|
19
|
+
public serialize(): Record<string, unknown> {
|
|
20
|
+
return { tickCount: this.tickCount };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Restore state from plain JSON produced by {@link serialize}. */
|
|
24
|
+
public deserialize(data: Record<string, unknown>): void {
|
|
25
|
+
this.tickCount = typeof data.tickCount === 'number' ? data.tickCount : 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { System, SystemType, SystemPriority, type World } from '@donut-games/engine/core';
|
|
2
|
+
import { ExampleComponent } from '../components/example-component.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Example system that increments {@link ExampleComponent.tickCount}
|
|
6
|
+
* for every matching entity each frame.
|
|
7
|
+
*/
|
|
8
|
+
export class ExampleSystem extends System {
|
|
9
|
+
public readonly systemType = SystemType.Update;
|
|
10
|
+
public static priority: number = SystemPriority.Average;
|
|
11
|
+
|
|
12
|
+
private readonly exampleQuery;
|
|
13
|
+
|
|
14
|
+
public constructor(world: World) {
|
|
15
|
+
super();
|
|
16
|
+
this.exampleQuery = world.query([ExampleComponent]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Runs once per simulation tick. */
|
|
20
|
+
public update(_elapsedMilliseconds: number): void {
|
|
21
|
+
for (const entity of this.exampleQuery.entities) {
|
|
22
|
+
const exampleComponent = entity.get(ExampleComponent);
|
|
23
|
+
if (exampleComponent !== undefined) {
|
|
24
|
+
exampleComponent.tickCount += 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src"
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"src/**/*"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "donut dev",
|
|
8
|
+
"validate": "donut validate",
|
|
9
|
+
"build": "donut build",
|
|
10
|
+
"review": "donut review"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@donut-games/engine": "^{{donutEngineVersion}}",
|
|
14
|
+
"pixi.js": "^{{pixiVersion}}"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@donut-games/cli": "^{{donutEngineVersion}}",
|
|
18
|
+
"typescript": "^5.4.0",
|
|
19
|
+
"eslint": "^8.57.0",
|
|
20
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
21
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
22
|
+
"eslint-plugin-import": "^2.29.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Entity, TransformComponent, type World } from '@donut-games/engine/core';
|
|
2
|
+
import { Vector3 } from '@donut-games/engine/math';
|
|
3
|
+
import {
|
|
4
|
+
CtrllrInputComponent,
|
|
5
|
+
type CtrllrManagerBridge,
|
|
6
|
+
} from '@donut-games/engine/ctrllr';
|
|
7
|
+
import { SpriteComponent } from '@donut-games/engine/pixi';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Build the default starter scene: one player entity with a transform,
|
|
11
|
+
* a visual component, and CTRLLR input bindings for basic movement.
|
|
12
|
+
*
|
|
13
|
+
* Invoked by the player's `startGame` after all built-in systems are
|
|
14
|
+
* registered. Add entities and project-specific systems to `world` here.
|
|
15
|
+
*/
|
|
16
|
+
export function createDefaultScene(
|
|
17
|
+
world: World,
|
|
18
|
+
ctrllrManager: CtrllrManagerBridge,
|
|
19
|
+
): void {
|
|
20
|
+
const playerEntity = new Entity({ name: 'Player' });
|
|
21
|
+
|
|
22
|
+
const transformComponent = new TransformComponent();
|
|
23
|
+
transformComponent.position = new Vector3(0, 0, 0);
|
|
24
|
+
playerEntity.addComponent(transformComponent);
|
|
25
|
+
|
|
26
|
+
const spriteComponent = new SpriteComponent();
|
|
27
|
+
spriteComponent.texturePath = '';
|
|
28
|
+
spriteComponent.tintColor = 0xff5577;
|
|
29
|
+
playerEntity.addComponent(spriteComponent);
|
|
30
|
+
|
|
31
|
+
const inputComponent = new CtrllrInputComponent({
|
|
32
|
+
managerBridge: ctrllrManager,
|
|
33
|
+
actionMap: {
|
|
34
|
+
moveLeft: 'onMoveLeft',
|
|
35
|
+
moveRight: 'onMoveRight',
|
|
36
|
+
moveUp: 'onMoveUp',
|
|
37
|
+
moveDown: 'onMoveDown',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
playerEntity.addComponent(inputComponent);
|
|
41
|
+
|
|
42
|
+
world.add(playerEntity);
|
|
43
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "donut dev",
|
|
8
|
+
"validate": "donut validate",
|
|
9
|
+
"build": "donut build",
|
|
10
|
+
"review": "donut review"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@donut-games/engine": "^{{donutEngineVersion}}",
|
|
14
|
+
"three": "^{{threeVersion}}"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@donut-games/cli": "^{{donutEngineVersion}}",
|
|
18
|
+
"typescript": "^5.4.0",
|
|
19
|
+
"eslint": "^8.57.0",
|
|
20
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
21
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
22
|
+
"eslint-plugin-import": "^2.29.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Entity, TransformComponent, type World } from '@donut-games/engine/core';
|
|
2
|
+
import { Vector3 } from '@donut-games/engine/math';
|
|
3
|
+
import {
|
|
4
|
+
CtrllrInputComponent,
|
|
5
|
+
type CtrllrManagerBridge,
|
|
6
|
+
} from '@donut-games/engine/ctrllr';
|
|
7
|
+
// TODO: import sprite/mesh components from '@donut-games/engine/three' when available
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Build the default starter scene: one player entity with a transform,
|
|
11
|
+
* a visual component, and CTRLLR input bindings for basic movement.
|
|
12
|
+
*
|
|
13
|
+
* Invoked by the player's `startGame` after all built-in systems are
|
|
14
|
+
* registered. Add entities and project-specific systems to `world` here.
|
|
15
|
+
*/
|
|
16
|
+
export function createDefaultScene(
|
|
17
|
+
world: World,
|
|
18
|
+
ctrllrManager: CtrllrManagerBridge,
|
|
19
|
+
): void {
|
|
20
|
+
const playerEntity = new Entity({ name: 'Player' });
|
|
21
|
+
|
|
22
|
+
const transformComponent = new TransformComponent();
|
|
23
|
+
transformComponent.position = new Vector3(0, 0, 0);
|
|
24
|
+
playerEntity.addComponent(transformComponent);
|
|
25
|
+
|
|
26
|
+
// 3D visual component wiring goes here once @donut/three lands.
|
|
27
|
+
|
|
28
|
+
const inputComponent = new CtrllrInputComponent({
|
|
29
|
+
managerBridge: ctrllrManager,
|
|
30
|
+
actionMap: {
|
|
31
|
+
moveLeft: 'onMoveLeft',
|
|
32
|
+
moveRight: 'onMoveRight',
|
|
33
|
+
moveUp: 'onMoveUp',
|
|
34
|
+
moveDown: 'onMoveDown',
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
playerEntity.addComponent(inputComponent);
|
|
38
|
+
|
|
39
|
+
world.add(playerEntity);
|
|
40
|
+
}
|