@downdraft/cli 0.0.0 → 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.
Files changed (82) hide show
  1. package/README.md +11 -0
  2. package/package.json +36 -3
  3. package/scripts/package-mobile.mjs +779 -0
  4. package/scripts/package-native.mjs +616 -0
  5. package/src/args.ts +333 -0
  6. package/src/assets.ts +523 -0
  7. package/src/blob-store-s3.ts +145 -0
  8. package/src/build-games.ts +45 -0
  9. package/src/build.ts +40 -0
  10. package/src/bun-env.d.ts +6 -0
  11. package/src/debug.ts +56 -0
  12. package/src/dev.ts +231 -0
  13. package/src/dist.ts +36 -0
  14. package/src/export.ts +38 -0
  15. package/src/index.ts +131 -0
  16. package/src/list-games.ts +37 -0
  17. package/src/mcp.ts +320 -0
  18. package/src/new.ts +63 -0
  19. package/src/paths.ts +101 -0
  20. package/src/plugin-command.ts +214 -0
  21. package/src/process-utils.ts +179 -0
  22. package/src/release.ts +374 -0
  23. package/src/scaffold-plugin.ts +280 -0
  24. package/src/scaffold.ts +207 -0
  25. package/src/test.ts +187 -0
  26. package/src/usage.ts +255 -0
  27. package/templates/_shared/ai-companion/devin-config.eta +11 -0
  28. package/templates/_shared/ai-companion/engine-prompt.eta +155 -0
  29. package/templates/_shared/ai-companion/logging-rule.eta +3 -0
  30. package/templates/_shared/ai-companion/loops-rule.eta +3 -0
  31. package/templates/_shared/partials/config-json-fields.eta +3 -0
  32. package/templates/_shared/partials/dev-deps.eta +8 -0
  33. package/templates/_shared/partials/package-json-fields.eta +12 -0
  34. package/templates/_shared/partials/tsconfig.eta +16 -0
  35. package/templates/_shared/partials/vscode-extensions.eta +11 -0
  36. package/templates/_shared/partials/vscode-settings.eta +29 -0
  37. package/templates/_shared/partials/vscode-tasks.eta +93 -0
  38. package/templates/full/.gitignore.eta +5 -0
  39. package/templates/full/.vscode/extensions.json.eta +1 -0
  40. package/templates/full/.vscode/settings.json.eta +1 -0
  41. package/templates/full/.vscode/tasks.json.eta +1 -0
  42. package/templates/full/README.md.eta +45 -0
  43. package/templates/full/assets/shaders/.eta +0 -0
  44. package/templates/full/downdraft.config.json.eta +10 -0
  45. package/templates/full/package.json.eta +9 -0
  46. package/templates/full/src/entities/.eta +0 -0
  47. package/templates/full/src/game-module.ts.eta +30 -0
  48. package/templates/full/src/native-entry.ts.eta +14 -0
  49. package/templates/full/src/sim-host.ts.eta +19 -0
  50. package/templates/full/src/sim-worker.ts.eta +99 -0
  51. package/templates/full/src/systems/.eta +0 -0
  52. package/templates/full/tsconfig.json.eta +1 -0
  53. package/templates/gamemodule/README.md.eta +32 -0
  54. package/templates/gamemodule/downdraft.config.json.eta +3 -0
  55. package/templates/gamemodule/package.json.eta +9 -0
  56. package/templates/gamemodule/src/game-module.ts.eta +63 -0
  57. package/templates/gamemodule/src/native-entry.ts.eta +14 -0
  58. package/templates/gamemodule/src/sim-host.ts.eta +25 -0
  59. package/templates/gamemodule/src/sim-worker.ts.eta +24 -0
  60. package/templates/gamemodule/tsconfig.json.eta +1 -0
  61. package/templates/minimal/.gitignore.eta +5 -0
  62. package/templates/minimal/.vscode/extensions.json.eta +1 -0
  63. package/templates/minimal/.vscode/settings.json.eta +1 -0
  64. package/templates/minimal/.vscode/tasks.json.eta +1 -0
  65. package/templates/minimal/README.md.eta +32 -0
  66. package/templates/minimal/downdraft.config.json.eta +3 -0
  67. package/templates/minimal/package.json.eta +9 -0
  68. package/templates/minimal/src/game-module.ts.eta +44 -0
  69. package/templates/minimal/src/native-entry.ts.eta +14 -0
  70. package/templates/minimal/tsconfig.json.eta +1 -0
  71. package/templates/physics/.gitignore.eta +5 -0
  72. package/templates/physics/.vscode/extensions.json.eta +1 -0
  73. package/templates/physics/.vscode/settings.json.eta +1 -0
  74. package/templates/physics/.vscode/tasks.json.eta +1 -0
  75. package/templates/physics/README.md.eta +36 -0
  76. package/templates/physics/downdraft.config.json.eta +6 -0
  77. package/templates/physics/package.json.eta +9 -0
  78. package/templates/physics/src/game-module.ts.eta +29 -0
  79. package/templates/physics/src/native-entry.ts.eta +14 -0
  80. package/templates/physics/src/sim-host.ts.eta +19 -0
  81. package/templates/physics/src/sim-worker.ts.eta +155 -0
  82. package/templates/physics/tsconfig.json.eta +1 -0
@@ -0,0 +1,63 @@
1
+ import { GameRenderer } from "@downdraft/engine";
2
+ import type { GameModule, SimWorkerSeed } from "@downdraft/engine/app/renderer";
3
+ import { WaterLib } from "@downdraft/engine/libraries/water";
4
+ import { SimHost } from "./sim-host";
5
+
6
+ // ============================================================================
7
+ // GameModule template — declarative game with libraries + plugins
8
+ //
9
+ // The SAME module drives both entry points:
10
+ // src/native-entry.ts — Bun + SDL + wgpu-native (draft dev, the default)
11
+ //
12
+ // This template shows the full declarative pattern:
13
+ // - Engine libraries (WaterLib) for SAB allocation + DI tokens
14
+ // - Declarative event routing (events map)
15
+ // - Save config
16
+ // - Game-specific hooks (onReady, onDispose)
17
+ //
18
+ // Feature modules such as @downdraft/engine/modules/terrain can be registered on the
19
+ // renderer's module host inside onReady.
20
+ // ============================================================================
21
+
22
+ export const gameModule: GameModule = {
23
+ // ── Engine libraries (declarative SAB allocation + DI tokens) ──
24
+ libraries: [WaterLib],
25
+
26
+ // ── Renderer + Sim ──
27
+ renderer: (canvas) => new GameRenderer(canvas),
28
+ sim: (seed?: SimWorkerSeed) => new SimHost(seed?.libraryBuffers),
29
+ simConfig: { seed: 12345, gamemode: 0, rules: {} },
30
+
31
+ // ── Sim→Renderer event routing (declarative) ──
32
+ events: {
33
+ ready: (_data, _ctx) => {
34
+ console.log("[game] Sim ready");
35
+ },
36
+ error: (data) => {
37
+ console.error("[game] Sim error:", data);
38
+ },
39
+ },
40
+
41
+ // ── Save ──
42
+ save: {
43
+ mode: "auto",
44
+ engineVersion: "0.1.0",
45
+ maxGenerations: 3,
46
+ },
47
+
48
+ // ── Post-init wiring ──
49
+ onReady: (ctx) => {
50
+ // Library-allocated SABs are available on ctx.extraBuffers, keyed by the
51
+ // library descriptor's channel name.
52
+ console.log("[game] ready", {
53
+ simSAB: ctx.simSAB,
54
+ inputSAB: ctx.inputSAB,
55
+ water: ctx.extraBuffers.water,
56
+ });
57
+ },
58
+
59
+ // ── Cleanup ──
60
+ onDispose: () => {
61
+ console.log("[game] disposed");
62
+ },
63
+ };
@@ -0,0 +1,14 @@
1
+ import { runNativeGameModule } from "@downdraft/platform-native";
2
+ import { gameModule } from "./game-module";
3
+
4
+ // Native entry — `draft dev`/`draft release` runtime (winit + wgpu under Bun, Node, or Deno).
5
+ // Saves, MCP, display info, and screenshots work through the in-process
6
+ // HostAPI bridge.
7
+
8
+ await runNativeGameModule(gameModule, {
9
+ title: "<%= it.name %> — Native",
10
+ appId: "downdraft-<%= it.name %>",
11
+ }).catch((e) => {
12
+ console.error("[native-entry] Fatal:", e);
13
+ process.exit(1);
14
+ });
@@ -0,0 +1,25 @@
1
+ import { SimWorkerHost } from "@downdraft/engine";
2
+
3
+ // Sim worker host — spawns sim-worker.ts on the main (renderer) thread and
4
+ // exposes the sim SAB + event stream to startGame().
5
+ //
6
+ // `libraryBuffers` are the SABs allocated by EngineLibrary descriptors in
7
+ // GameModule.libraries[] — forwarded to the worker via the init RPC.
8
+ export class SimHost extends SimWorkerHost {
9
+ private libraryBuffers?: Record<string, SharedArrayBuffer>;
10
+
11
+ constructor(libraryBuffers?: Record<string, SharedArrayBuffer>) {
12
+ // Replace with your game's sim-buffer allocation (see the SAB APIs in
13
+ // @downdraft/engine, e.g. defineRecordBuffer/allocateSimBuffer).
14
+ super(new SharedArrayBuffer(4096));
15
+ this.libraryBuffers = libraryBuffers;
16
+ }
17
+
18
+ protected createWorker(): Worker {
19
+ return new Worker(new URL("./sim-worker.ts", import.meta.url), { type: "module" });
20
+ }
21
+
22
+ protected async onInit(): Promise<void> {
23
+ await this.getProxy()!.proxy.init(this.getSimBuffer(), this.libraryBuffers);
24
+ }
25
+ }
@@ -0,0 +1,24 @@
1
+ import { createSimWorker } from "@downdraft/engine";
2
+
3
+ // Sim worker — runs the fixed-timestep simulation loop in a Web Worker.
4
+ // createSimWorker handles the tick loop, pause/resume/step control, stats,
5
+ // and the "ready" event; you provide onInit + onTick.
6
+ //
7
+ // The init RPC args sent by SimHost arrive after `control`:
8
+ // proxy.init(simBuffer, libraryBuffers) → onInit(sab, control, libraryBuffers)
9
+ createSimWorker({
10
+ fixedDt: 1 / 60,
11
+
12
+ onInit(sab, _control, libraryBuffers?: Record<string, SharedArrayBuffer>) {
13
+ // `sab` is the sim state buffer shared with the renderer.
14
+ // `libraryBuffers` are the SABs allocated by your EngineLibrary
15
+ // descriptors (e.g. WaterLib's water channel) — wire them into your sim.
16
+ void sab;
17
+ void libraryBuffers;
18
+ },
19
+
20
+ onTick(dt, _ctx) {
21
+ // Step your simulation here. `dt` equals fixedDt.
22
+ void dt;
23
+ },
24
+ });
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/tsconfig", it) %>
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ export/
4
+ *.log
5
+ .DS_Store
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/vscode-extensions", it) %>
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/vscode-settings", it) %>
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/vscode-tasks", it) %>
@@ -0,0 +1,32 @@
1
+ # <%= it.name %>
2
+
3
+ <% if (it.description) { %><%= it.description %><% } else { %>A game built with the DownDraft Engine.<% } %>
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ bun install
10
+
11
+ # Start dev server
12
+ npm run dev
13
+
14
+ # Build for production
15
+ npm run build
16
+
17
+ # Export for distribution
18
+ npm run export
19
+ ```
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ src/ Game source code
25
+ native-entry.ts Native runtime entry (draft dev)
26
+ assets/ Game assets (models, textures, shaders)
27
+ downdraft.config.json Engine configuration
28
+ ```
29
+
30
+ ## Engine
31
+
32
+ Built with [DownDraft Engine](https://github.com/downdraft-engine) — a TypeScript-first game engine on WebGPU — native runtime (Bun + SDL + wgpu).
@@ -0,0 +1,3 @@
1
+ {
2
+ <%~ include("_shared/partials/config-json-fields", it) %>
3
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "<%= it.name %>",
3
+ <%~ include("_shared/partials/package-json-fields", it) %>
4
+ "dependencies": {
5
+ "@downdraft/engine": "^0.1.0",
6
+ "@downdraft/platform-native": "^0.1.0"
7
+ },
8
+ <%~ include("_shared/partials/dev-deps", it) %>
9
+ }
@@ -0,0 +1,44 @@
1
+ import type { GameModule } from "@downdraft/engine/app/renderer";
2
+
3
+ // ============================================================================
4
+ // Minimal game template — declarative GameModule
5
+ //
6
+ // The SAME module drives both entry points:
7
+ // src/native-entry.ts — Bun + SDL + wgpu-native (draft dev, the default)
8
+ //
9
+ // For a more complete example with WebGPU rendering, sim worker, and
10
+ // libraries, see the "full" template (`draft init --template full`).
11
+ // ============================================================================
12
+
13
+ export const gameModule: GameModule = {
14
+ // Renderer factory — creates the renderer from a canvas element
15
+ renderer: (canvas) => ({
16
+ async init() { return true; },
17
+ setBuffers() {},
18
+ setupInputListeners() {},
19
+ render() {},
20
+ stop() {},
21
+ getFPS() { return 0; },
22
+ }),
23
+
24
+ // Sim worker factory — creates the sim worker
25
+ sim: () => ({
26
+ async start(_config: unknown) {},
27
+ onEvent(_cb: (msg: any) => void) {},
28
+ getSimBuffer() { return new SharedArrayBuffer(1024); },
29
+ getInputBuffer() { return new SharedArrayBuffer(1024); },
30
+ }),
31
+
32
+ // Sim config (passed to sim.start())
33
+ simConfig: { seed: 12345 },
34
+
35
+ // Post-init wiring — called after renderer init + sim start
36
+ onReady: (ctx) => {
37
+ console.log("[game] ready", { sim: ctx.sim, renderer: ctx.renderer });
38
+ },
39
+
40
+ // Cleanup — called on hot-reload dispose
41
+ onDispose: () => {
42
+ console.log("[game] disposed");
43
+ },
44
+ };
@@ -0,0 +1,14 @@
1
+ import { runNativeGameModule } from "@downdraft/platform-native";
2
+ import { gameModule } from "./game-module";
3
+
4
+ // Native entry — `draft dev`/`draft release` runtime (winit + wgpu under Bun, Node, or Deno).
5
+ // Saves, MCP, display info, and screenshots work through the in-process
6
+ // HostAPI bridge.
7
+
8
+ await runNativeGameModule(gameModule, {
9
+ title: "<%= it.name %> — Native",
10
+ appId: "downdraft-<%= it.name %>",
11
+ }).catch((e) => {
12
+ console.error("[native-entry] Fatal:", e);
13
+ process.exit(1);
14
+ });
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/tsconfig", it) %>
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ export/
4
+ *.log
5
+ .DS_Store
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/vscode-extensions", it) %>
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/vscode-settings", it) %>
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/vscode-tasks", it) %>
@@ -0,0 +1,36 @@
1
+ # <%= it.name %>
2
+
3
+ <% if (it.description) { %><%= it.description %><% } else { %>A physics-based game built with the DownDraft Engine.<% } %>
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ bun install
10
+
11
+ # Start dev server
12
+ npm run dev
13
+
14
+ # Build for production
15
+ npm run build
16
+
17
+ # Export for distribution
18
+ npm run export
19
+ ```
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ src/ Game source code
25
+ native-entry.ts Native runtime entry (draft dev)
26
+ assets/ Game assets (models, textures, shaders)
27
+ downdraft.config.json Engine configuration (physics-rapier plugin enabled)
28
+ ```
29
+
30
+ ## Plugins
31
+
32
+ - **@downdraft/engine/libraries/physics-rapier** — Rapier physics backend for rigid body simulation, colliders, and raycasting.
33
+
34
+ ## Engine
35
+
36
+ Built with [DownDraft Engine](https://github.com/downdraft-engine) — a TypeScript-first game engine on WebGPU — native runtime (Bun + SDL + wgpu).
@@ -0,0 +1,6 @@
1
+ {
2
+ <%~ include("_shared/partials/config-json-fields", it) %>,
3
+ "plugins": [
4
+ "physics-rapier"
5
+ ]
6
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "<%= it.name %>",
3
+ <%~ include("_shared/partials/package-json-fields", it) %>
4
+ "dependencies": {
5
+ "@downdraft/engine": "^0.1.0",
6
+ "@downdraft/platform-native": "^0.1.0"
7
+ },
8
+ <%~ include("_shared/partials/dev-deps", it) %>
9
+ }
@@ -0,0 +1,29 @@
1
+ import { GameRenderer } from "@downdraft/engine";
2
+ import type { GameModule } from "@downdraft/engine/app/renderer";
3
+ import { SimHost } from "./sim-host";
4
+
5
+ // ============================================================================
6
+ // Physics template — ECS sim worker + GameRenderer
7
+ //
8
+ // The sim runs in sim-worker.ts (a Web Worker): an ECS World with gravity and
9
+ // integration systems that spawns falling shapes. The SAME module drives
10
+ // both entry points:
11
+ // src/native-entry.ts — Bun + SDL + wgpu-native (draft dev, the default)
12
+ // ============================================================================
13
+
14
+ export const gameModule: GameModule = {
15
+ // ── Renderer + Sim ──
16
+ renderer: (canvas) => new GameRenderer(canvas),
17
+ sim: () => new SimHost(),
18
+ simConfig: { seed: 12345 },
19
+
20
+ // ── Post-init wiring ──
21
+ onReady: (ctx) => {
22
+ console.log("[game] ready", { simSAB: ctx.simSAB, inputSAB: ctx.inputSAB });
23
+ },
24
+
25
+ // ── Cleanup ──
26
+ onDispose: () => {
27
+ console.log("[game] disposed");
28
+ },
29
+ };
@@ -0,0 +1,14 @@
1
+ import { runNativeGameModule } from "@downdraft/platform-native";
2
+ import { gameModule } from "./game-module";
3
+
4
+ // Native entry — `draft dev`/`draft release` runtime (winit + wgpu under Bun, Node, or Deno).
5
+ // Saves, MCP, display info, and screenshots work through the in-process
6
+ // HostAPI bridge.
7
+
8
+ await runNativeGameModule(gameModule, {
9
+ title: "<%= it.name %> — Native",
10
+ appId: "downdraft-<%= it.name %>",
11
+ }).catch((e) => {
12
+ console.error("[native-entry] Fatal:", e);
13
+ process.exit(1);
14
+ });
@@ -0,0 +1,19 @@
1
+ import { SimWorkerHost } from "@downdraft/engine";
2
+
3
+ // Sim worker host — spawns sim-worker.ts on the main (renderer) thread and
4
+ // exposes the sim SAB + event stream to startGame().
5
+ export class SimHost extends SimWorkerHost {
6
+ constructor() {
7
+ // Replace with your game's sim-buffer allocation (see the SAB APIs in
8
+ // @downdraft/engine, e.g. defineRecordBuffer/allocateSimBuffer).
9
+ super(new SharedArrayBuffer(4096));
10
+ }
11
+
12
+ protected createWorker(): Worker {
13
+ return new Worker(new URL("./sim-worker.ts", import.meta.url), { type: "module" });
14
+ }
15
+
16
+ protected async onInit(): Promise<void> {
17
+ await this.getProxy()!.proxy.init(this.getSimBuffer());
18
+ }
19
+ }
@@ -0,0 +1,155 @@
1
+ import {
2
+ c,
3
+ Collider,
4
+ Component,
5
+ createBoxCollider,
6
+ createLogger,
7
+ createSimWorker,
8
+ createSphereCollider,
9
+ PhysicsTransform,
10
+ Query,
11
+ RigidBody,
12
+ spawn,
13
+ Stage,
14
+ system,
15
+ Velocity,
16
+ World,
17
+ } from "@downdraft/engine";
18
+
19
+ const log = createLogger();
20
+
21
+ // Physics demo sim worker — an ECS World with gravity + integration systems
22
+ // that spawns falling shapes. createSimWorker owns the tick loop, worker
23
+ // protocol, and stats; this file provides onInit + onTick.
24
+
25
+ type PhysicsBodyData = {
26
+ bodyType: "dynamic" | "static" | "kinematic";
27
+ mass: number;
28
+ restitution: number;
29
+ friction: number;
30
+ };
31
+
32
+ const PhysicsBody = Component.register<PhysicsBodyData>("PhysicsBody", {
33
+ bodyType: "dynamic",
34
+ mass: 1,
35
+ restitution: 0.4,
36
+ friction: 0.5,
37
+ });
38
+
39
+ type FallingShapeData = {
40
+ shape: "box" | "sphere";
41
+ size: number;
42
+ color: [number, number, number];
43
+ };
44
+
45
+ const FallingShape = Component.register<FallingShapeData>("FallingShape", {
46
+ shape: "box",
47
+ size: 1,
48
+ color: [1, 1, 1],
49
+ });
50
+
51
+ const gravityQuery = new Query([RigidBody.id, Velocity.id, PhysicsTransform.id]);
52
+
53
+ let world: World;
54
+ let spawnTimer = 0;
55
+
56
+ createSimWorker({
57
+ fixedDt: 1 / 60,
58
+
59
+ onInit() {
60
+ world = new World();
61
+
62
+ // Ground entity — static body that shapes bounce on.
63
+ spawn(world,
64
+ c(PhysicsBody, { bodyType: "static", mass: 0, restitution: 0.3, friction: 0.8 }),
65
+ c(Collider, createBoxCollider([10, 0.5, 10])),
66
+ c(PhysicsTransform, { position: [0, -1, 0] }),
67
+ c(FallingShape, { shape: "box", size: 20, color: [0.3, 0.5, 0.3] }),
68
+ );
69
+
70
+ world.schedule.addSystem(system(
71
+ "physics-gravity", Stage.Update,
72
+ (ctx) => {
73
+ gravityQuery.iterate(ctx.tick, (_ent, comps) => {
74
+ const body = comps[0] as any;
75
+ const vel = comps[1] as any;
76
+ if (body && vel && body.bodyType === "dynamic") {
77
+ vel.linear[1] -= 9.81 * ctx.dt * (body.gravityScale as number);
78
+ }
79
+ });
80
+ },
81
+ { queries: [gravityQuery] },
82
+ ));
83
+
84
+ world.schedule.addSystem(system(
85
+ "physics-integrate", Stage.Update,
86
+ (ctx) => {
87
+ gravityQuery.iterate(ctx.tick, (ent, comps) => {
88
+ const body = comps[0] as any;
89
+ const vel = comps[1] as any;
90
+ const transform = comps[2] as any;
91
+ if (body && vel && transform && body.bodyType === "dynamic") {
92
+ const [vx, vy, vz] = vel.linear as [number, number, number];
93
+ const [px, py, pz] = transform.position as [number, number, number];
94
+ const newPy = py + vy * ctx.dt;
95
+ transform.position = [px + vx * ctx.dt, newPy, pz + vz * ctx.dt];
96
+
97
+ if (newPy < -10) {
98
+ world.despawn(ent);
99
+ }
100
+ }
101
+ });
102
+ },
103
+ { queries: [gravityQuery] },
104
+ ));
105
+
106
+ log.info("sim", "physics demo initialized — shapes will fall and bounce on the ground");
107
+ },
108
+
109
+ onTick(dt) {
110
+ spawnTimer += dt;
111
+ if (spawnTimer > 0.5) {
112
+ spawnTimer = 0;
113
+ spawnFallingShape();
114
+ }
115
+
116
+ world.step(dt);
117
+ },
118
+
119
+ onShutdown() {
120
+ log.info("sim", "sim worker shutdown");
121
+ },
122
+ });
123
+
124
+ function spawnFallingShape(): void {
125
+ const isBox = Math.random() > 0.5;
126
+ const size = 0.5 + Math.random() * 1.5;
127
+ const x = (Math.random() - 0.5) * 10;
128
+ const z = (Math.random() - 0.5) * 10;
129
+
130
+ spawn(world,
131
+ c(PhysicsBody, {
132
+ bodyType: "dynamic",
133
+ mass: size * size * size,
134
+ restitution: 0.3 + Math.random() * 0.4,
135
+ friction: 0.3 + Math.random() * 0.4,
136
+ }),
137
+ c(RigidBody, {
138
+ bodyType: "dynamic",
139
+ mass: size * size * size,
140
+ gravityScale: 1,
141
+ }),
142
+ c(Velocity, {
143
+ linear: [(Math.random() - 0.5) * 2, 0, (Math.random() - 0.5) * 2],
144
+ }),
145
+ c(PhysicsTransform, { position: [x, 15, z] }),
146
+ c(Collider, isBox
147
+ ? createBoxCollider([size / 2, size / 2, size / 2])
148
+ : createSphereCollider(size * 0.5)),
149
+ c(FallingShape, {
150
+ shape: isBox ? "box" : "sphere",
151
+ size: isBox ? size : size * 0.5,
152
+ color: [Math.random(), Math.random(), Math.random()],
153
+ }),
154
+ );
155
+ }
@@ -0,0 +1 @@
1
+ <%~ include("_shared/partials/tsconfig", it) %>