@aura3d/engine 1.3.3 → 1.4.1

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 (233) hide show
  1. package/README.md +138 -15
  2. package/dist/assets/GLTFLoader.js +1 -0
  3. package/dist/assets/GLTFLoader.js.map +1 -1
  4. package/dist/assets/GLTFRenderResources.d.ts.map +1 -1
  5. package/dist/assets/GLTFRenderResources.js +1 -0
  6. package/dist/assets/GLTFRenderResources.js.map +1 -1
  7. package/dist/aura3d-cli/asset-constants.d.ts +5 -0
  8. package/dist/aura3d-cli/asset-constants.d.ts.map +1 -0
  9. package/dist/aura3d-cli/asset-constants.js +5 -0
  10. package/dist/aura3d-cli/asset-constants.js.map +1 -0
  11. package/dist/aura3d-cli/asset-core-types.d.ts +222 -0
  12. package/dist/aura3d-cli/asset-core-types.d.ts.map +1 -0
  13. package/dist/aura3d-cli/asset-core-types.js +2 -0
  14. package/dist/aura3d-cli/asset-core-types.js.map +1 -0
  15. package/dist/aura3d-cli/asset-inspection-types.d.ts +131 -0
  16. package/dist/aura3d-cli/asset-inspection-types.d.ts.map +1 -0
  17. package/dist/aura3d-cli/asset-inspection-types.js +2 -0
  18. package/dist/aura3d-cli/asset-inspection-types.js.map +1 -0
  19. package/dist/aura3d-cli/asset-manifest.d.ts +8 -0
  20. package/dist/aura3d-cli/asset-manifest.d.ts.map +1 -0
  21. package/dist/aura3d-cli/asset-manifest.js +116 -0
  22. package/dist/aura3d-cli/asset-manifest.js.map +1 -0
  23. package/dist/aura3d-cli/asset-readiness-types.d.ts +164 -0
  24. package/dist/aura3d-cli/asset-readiness-types.d.ts.map +1 -0
  25. package/dist/aura3d-cli/asset-readiness-types.js +2 -0
  26. package/dist/aura3d-cli/asset-readiness-types.js.map +1 -0
  27. package/dist/aura3d-cli/asset-source-ast.d.ts +8 -0
  28. package/dist/aura3d-cli/asset-source-ast.d.ts.map +1 -0
  29. package/dist/aura3d-cli/asset-source-ast.js +241 -0
  30. package/dist/aura3d-cli/asset-source-ast.js.map +1 -0
  31. package/dist/aura3d-cli/asset-source-roles.d.ts +4 -0
  32. package/dist/aura3d-cli/asset-source-roles.d.ts.map +1 -0
  33. package/dist/aura3d-cli/asset-source-roles.js +79 -0
  34. package/dist/aura3d-cli/asset-source-roles.js.map +1 -0
  35. package/dist/aura3d-cli/asset-source-validation.d.ts +4 -0
  36. package/dist/aura3d-cli/asset-source-validation.d.ts.map +1 -0
  37. package/dist/aura3d-cli/asset-source-validation.js +200 -0
  38. package/dist/aura3d-cli/asset-source-validation.js.map +1 -0
  39. package/dist/aura3d-cli/character-assembly-types.d.ts +39 -0
  40. package/dist/aura3d-cli/character-assembly-types.d.ts.map +1 -0
  41. package/dist/aura3d-cli/character-assembly-types.js +2 -0
  42. package/dist/aura3d-cli/character-assembly-types.js.map +1 -0
  43. package/dist/aura3d-cli/cli-help.d.ts +4 -0
  44. package/dist/aura3d-cli/cli-help.d.ts.map +1 -0
  45. package/dist/aura3d-cli/cli-help.js +48 -0
  46. package/dist/aura3d-cli/cli-help.js.map +1 -0
  47. package/dist/aura3d-cli/cli-options.d.ts +66 -0
  48. package/dist/aura3d-cli/cli-options.d.ts.map +1 -0
  49. package/dist/aura3d-cli/cli-options.js +234 -0
  50. package/dist/aura3d-cli/cli-options.js.map +1 -0
  51. package/dist/aura3d-cli/cli.js +20 -177
  52. package/dist/aura3d-cli/cli.js.map +1 -1
  53. package/dist/aura3d-cli/index.d.ts +12 -402
  54. package/dist/aura3d-cli/index.d.ts.map +1 -1
  55. package/dist/aura3d-cli/index.js +948 -90
  56. package/dist/aura3d-cli/index.js.map +1 -1
  57. package/dist/aura3d-cli/pull-bridge/adapters.d.ts +4 -0
  58. package/dist/aura3d-cli/pull-bridge/adapters.d.ts.map +1 -0
  59. package/dist/aura3d-cli/pull-bridge/adapters.js +38 -0
  60. package/dist/aura3d-cli/pull-bridge/adapters.js.map +1 -0
  61. package/dist/aura3d-cli/pull-bridge/download.d.ts +6 -0
  62. package/dist/aura3d-cli/pull-bridge/download.d.ts.map +1 -0
  63. package/dist/aura3d-cli/pull-bridge/download.js +208 -0
  64. package/dist/aura3d-cli/pull-bridge/download.js.map +1 -0
  65. package/dist/aura3d-cli/pull-bridge/profiles.d.ts +14 -0
  66. package/dist/aura3d-cli/pull-bridge/profiles.d.ts.map +1 -0
  67. package/dist/aura3d-cli/pull-bridge/profiles.js +63 -0
  68. package/dist/aura3d-cli/pull-bridge/profiles.js.map +1 -0
  69. package/dist/aura3d-cli/pull-bridge/provenance.d.ts +9 -0
  70. package/dist/aura3d-cli/pull-bridge/provenance.d.ts.map +1 -0
  71. package/dist/aura3d-cli/pull-bridge/provenance.js +99 -0
  72. package/dist/aura3d-cli/pull-bridge/provenance.js.map +1 -0
  73. package/dist/aura3d-cli/pull-bridge/pullable.d.ts +14 -0
  74. package/dist/aura3d-cli/pull-bridge/pullable.d.ts.map +1 -0
  75. package/dist/aura3d-cli/pull-bridge/pullable.js +58 -0
  76. package/dist/aura3d-cli/pull-bridge/pullable.js.map +1 -0
  77. package/dist/aura3d-cli/pull-bridge/scoring.d.ts +21 -0
  78. package/dist/aura3d-cli/pull-bridge/scoring.d.ts.map +1 -0
  79. package/dist/aura3d-cli/pull-bridge/scoring.js +177 -0
  80. package/dist/aura3d-cli/pull-bridge/scoring.js.map +1 -0
  81. package/dist/aura3d-cli/pull-bridge/search.d.ts +39 -0
  82. package/dist/aura3d-cli/pull-bridge/search.d.ts.map +1 -0
  83. package/dist/aura3d-cli/pull-bridge/search.js +114 -0
  84. package/dist/aura3d-cli/pull-bridge/search.js.map +1 -0
  85. package/dist/aura3d-cli/pull-bridge/types.d.ts +9 -0
  86. package/dist/aura3d-cli/pull-bridge/types.d.ts.map +1 -0
  87. package/dist/aura3d-cli/pull-bridge/types.js +2 -0
  88. package/dist/aura3d-cli/pull-bridge/types.js.map +1 -0
  89. package/dist/aura3d-cli/pull-bridge/vector3.d.ts +2 -0
  90. package/dist/aura3d-cli/pull-bridge/vector3.d.ts.map +1 -0
  91. package/dist/aura3d-cli/pull-bridge/vector3.js +4 -0
  92. package/dist/aura3d-cli/pull-bridge/vector3.js.map +1 -0
  93. package/dist/aura3d-cli/pull-bridge.d.ts +18 -100
  94. package/dist/aura3d-cli/pull-bridge.d.ts.map +1 -1
  95. package/dist/aura3d-cli/pull-bridge.js +59 -526
  96. package/dist/aura3d-cli/pull-bridge.js.map +1 -1
  97. package/dist/create-aura3d/cli.js +10 -0
  98. package/dist/create-aura3d/cli.js.map +1 -1
  99. package/dist/create-aura3d/index.d.ts +1 -1
  100. package/dist/create-aura3d/index.d.ts.map +1 -1
  101. package/dist/create-aura3d/index.js +3 -1
  102. package/dist/create-aura3d/index.js.map +1 -1
  103. package/dist/create-aura3d/showcase-spec-artifacts.d.ts +105 -0
  104. package/dist/create-aura3d/showcase-spec-artifacts.d.ts.map +1 -0
  105. package/dist/create-aura3d/showcase-spec-artifacts.js +143 -0
  106. package/dist/create-aura3d/showcase-spec-artifacts.js.map +1 -0
  107. package/dist/create-aura3d/showcase-spec-category-plan.d.ts +15 -0
  108. package/dist/create-aura3d/showcase-spec-category-plan.d.ts.map +1 -0
  109. package/dist/create-aura3d/showcase-spec-category-plan.js +45 -0
  110. package/dist/create-aura3d/showcase-spec-category-plan.js.map +1 -0
  111. package/dist/create-aura3d/showcase-spec-compiler.d.ts +5 -0
  112. package/dist/create-aura3d/showcase-spec-compiler.d.ts.map +1 -0
  113. package/dist/create-aura3d/showcase-spec-compiler.js +240 -0
  114. package/dist/create-aura3d/showcase-spec-compiler.js.map +1 -0
  115. package/dist/create-aura3d/showcase-spec-evidence.d.ts +7 -0
  116. package/dist/create-aura3d/showcase-spec-evidence.d.ts.map +1 -0
  117. package/dist/create-aura3d/showcase-spec-evidence.js +881 -0
  118. package/dist/create-aura3d/showcase-spec-evidence.js.map +1 -0
  119. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.d.ts +21 -0
  120. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.d.ts.map +1 -0
  121. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js +725 -0
  122. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js.map +1 -0
  123. package/dist/create-aura3d/showcase-spec-game-replacement-evidence.d.ts +14 -0
  124. package/dist/create-aura3d/showcase-spec-game-replacement-evidence.d.ts.map +1 -0
  125. package/dist/create-aura3d/showcase-spec-game-replacement-evidence.js +219 -0
  126. package/dist/create-aura3d/showcase-spec-game-replacement-evidence.js.map +1 -0
  127. package/dist/create-aura3d/showcase-spec-game-template-evidence.d.ts +72 -0
  128. package/dist/create-aura3d/showcase-spec-game-template-evidence.d.ts.map +1 -0
  129. package/dist/create-aura3d/showcase-spec-game-template-evidence.js +435 -0
  130. package/dist/create-aura3d/showcase-spec-game-template-evidence.js.map +1 -0
  131. package/dist/create-aura3d/showcase-spec-gameplay-evidence.d.ts +3 -0
  132. package/dist/create-aura3d/showcase-spec-gameplay-evidence.d.ts.map +1 -0
  133. package/dist/create-aura3d/showcase-spec-gameplay-evidence.js +108 -0
  134. package/dist/create-aura3d/showcase-spec-gameplay-evidence.js.map +1 -0
  135. package/dist/create-aura3d/showcase-spec-parser.d.ts +3 -0
  136. package/dist/create-aura3d/showcase-spec-parser.d.ts.map +1 -0
  137. package/dist/create-aura3d/showcase-spec-parser.js +690 -0
  138. package/dist/create-aura3d/showcase-spec-parser.js.map +1 -0
  139. package/dist/create-aura3d/showcase-spec-platformer-artifacts.d.ts +3 -0
  140. package/dist/create-aura3d/showcase-spec-platformer-artifacts.d.ts.map +1 -0
  141. package/dist/create-aura3d/showcase-spec-platformer-artifacts.js +274 -0
  142. package/dist/create-aura3d/showcase-spec-platformer-artifacts.js.map +1 -0
  143. package/dist/create-aura3d/showcase-spec-racing-artifacts.d.ts +3 -0
  144. package/dist/create-aura3d/showcase-spec-racing-artifacts.d.ts.map +1 -0
  145. package/dist/create-aura3d/showcase-spec-racing-artifacts.js +273 -0
  146. package/dist/create-aura3d/showcase-spec-racing-artifacts.js.map +1 -0
  147. package/dist/create-aura3d/showcase-spec-replacement-apply.d.ts +10 -0
  148. package/dist/create-aura3d/showcase-spec-replacement-apply.d.ts.map +1 -0
  149. package/dist/create-aura3d/showcase-spec-replacement-apply.js +166 -0
  150. package/dist/create-aura3d/showcase-spec-replacement-apply.js.map +1 -0
  151. package/dist/create-aura3d/showcase-spec-replacement-candidates.d.ts +16 -0
  152. package/dist/create-aura3d/showcase-spec-replacement-candidates.d.ts.map +1 -0
  153. package/dist/create-aura3d/showcase-spec-replacement-candidates.js +244 -0
  154. package/dist/create-aura3d/showcase-spec-replacement-candidates.js.map +1 -0
  155. package/dist/create-aura3d/showcase-spec-replacement-game-geometry.d.ts +4 -0
  156. package/dist/create-aura3d/showcase-spec-replacement-game-geometry.d.ts.map +1 -0
  157. package/dist/create-aura3d/showcase-spec-replacement-game-geometry.js +175 -0
  158. package/dist/create-aura3d/showcase-spec-replacement-game-geometry.js.map +1 -0
  159. package/dist/create-aura3d/showcase-spec-replacement-manifest.d.ts +41 -0
  160. package/dist/create-aura3d/showcase-spec-replacement-manifest.d.ts.map +1 -0
  161. package/dist/create-aura3d/showcase-spec-replacement-manifest.js +62 -0
  162. package/dist/create-aura3d/showcase-spec-replacement-manifest.js.map +1 -0
  163. package/dist/create-aura3d/showcase-spec-replacement-values.d.ts +9 -0
  164. package/dist/create-aura3d/showcase-spec-replacement-values.d.ts.map +1 -0
  165. package/dist/create-aura3d/showcase-spec-replacement-values.js +41 -0
  166. package/dist/create-aura3d/showcase-spec-replacement-values.js.map +1 -0
  167. package/dist/create-aura3d/showcase-spec-replacement.d.ts +13 -0
  168. package/dist/create-aura3d/showcase-spec-replacement.d.ts.map +1 -0
  169. package/dist/create-aura3d/showcase-spec-replacement.js +144 -0
  170. package/dist/create-aura3d/showcase-spec-replacement.js.map +1 -0
  171. package/dist/create-aura3d/showcase-spec-types.d.ts +319 -0
  172. package/dist/create-aura3d/showcase-spec-types.d.ts.map +1 -0
  173. package/dist/create-aura3d/showcase-spec-types.js +2 -0
  174. package/dist/create-aura3d/showcase-spec-types.js.map +1 -0
  175. package/dist/engine/agent-api/GameAssetValidation.d.ts +5 -0
  176. package/dist/engine/agent-api/GameAssetValidation.d.ts.map +1 -1
  177. package/dist/engine/agent-api/GameAssetValidation.js +103 -0
  178. package/dist/engine/agent-api/GameAssetValidation.js.map +1 -1
  179. package/dist/engine/agent-api/GameEvidence.d.ts +14 -1
  180. package/dist/engine/agent-api/GameEvidence.d.ts.map +1 -1
  181. package/dist/engine/agent-api/GameEvidence.js +77 -6
  182. package/dist/engine/agent-api/GameEvidence.js.map +1 -1
  183. package/dist/engine/agent-api/GameGenreKits.d.ts +473 -0
  184. package/dist/engine/agent-api/GameGenreKits.d.ts.map +1 -0
  185. package/dist/engine/agent-api/GameGenreKits.js +1269 -0
  186. package/dist/engine/agent-api/GameGenreKits.js.map +1 -0
  187. package/dist/engine/agent-api/GameRuntime.d.ts +190 -3
  188. package/dist/engine/agent-api/GameRuntime.d.ts.map +1 -1
  189. package/dist/engine/agent-api/GameRuntime.js +431 -2
  190. package/dist/engine/agent-api/GameRuntime.js.map +1 -1
  191. package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts +182 -0
  192. package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts.map +1 -0
  193. package/dist/engine/agent-api/GameSceneGeometryBindings.js +488 -0
  194. package/dist/engine/agent-api/GameSceneGeometryBindings.js.map +1 -0
  195. package/dist/engine/agent-api/GameSceneGeometryMath.d.ts +39 -0
  196. package/dist/engine/agent-api/GameSceneGeometryMath.d.ts.map +1 -0
  197. package/dist/engine/agent-api/GameSceneGeometryMath.js +89 -0
  198. package/dist/engine/agent-api/GameSceneGeometryMath.js.map +1 -0
  199. package/dist/engine/agent-api/PublicGameGeometry.d.ts +117 -0
  200. package/dist/engine/agent-api/PublicGameGeometry.d.ts.map +1 -0
  201. package/dist/engine/agent-api/PublicGameGeometry.js +118 -0
  202. package/dist/engine/agent-api/PublicGameGeometry.js.map +1 -0
  203. package/dist/engine/agent-api/RuntimeNodeHandle.d.ts +4 -0
  204. package/dist/engine/agent-api/RuntimeNodeHandle.d.ts.map +1 -1
  205. package/dist/engine/agent-api/RuntimeNodeHandle.js +2 -0
  206. package/dist/engine/agent-api/RuntimeNodeHandle.js.map +1 -1
  207. package/dist/engine/agent-api/SceneGroundingUtils.d.ts +86 -0
  208. package/dist/engine/agent-api/SceneGroundingUtils.d.ts.map +1 -1
  209. package/dist/engine/agent-api/SceneGroundingUtils.js +116 -0
  210. package/dist/engine/agent-api/SceneGroundingUtils.js.map +1 -1
  211. package/dist/engine/agent-api/index.d.ts +266 -5
  212. package/dist/engine/agent-api/index.d.ts.map +1 -1
  213. package/dist/engine/agent-api/index.js +2445 -113
  214. package/dist/engine/agent-api/index.js.map +1 -1
  215. package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
  216. package/dist/engine/production-runtime/TypedGLBActor.d.ts +14 -2
  217. package/dist/engine/production-runtime/TypedGLBActor.d.ts.map +1 -1
  218. package/dist/engine/production-runtime/TypedGLBActor.js +95 -4
  219. package/dist/engine/production-runtime/TypedGLBActor.js.map +1 -1
  220. package/dist/rendering/ForwardPass.js +7 -1
  221. package/dist/rendering/ForwardPass.js.map +1 -1
  222. package/dist/rendering/MorphTarget.d.ts +1 -0
  223. package/dist/rendering/MorphTarget.d.ts.map +1 -1
  224. package/dist/rendering/MorphTarget.js.map +1 -1
  225. package/dist/rendering/Renderer.js +7 -1
  226. package/dist/rendering/Renderer.js.map +1 -1
  227. package/package.json +30 -32
  228. package/templates/mini-game/README.md +13 -0
  229. package/templates/mini-game/index.html +1 -1
  230. package/templates/mini-game/package.json +1 -1
  231. package/templates/mini-game/src/main.ts +251 -21
  232. package/templates/mini-game/tests/playable.spec.ts +49 -0
  233. package/templates/mini-game/tests/screenshot.spec.ts +37 -32
@@ -3,6 +3,19 @@
3
3
  Small interactive scene starter for agent-authored controls, movement, and route
4
4
  health checks.
5
5
 
6
+ Status: playable starter, not a production game claim. This template uses a
7
+ typed player asset, the public `game.platformer(...)` kit, runtime nodes, HUD
8
+ evidence, keyboard input, reset, scoring, and screenshot tests. It proves the
9
+ starter boots and responds to keyboard input through the root `@aura3d/engine`
10
+ safe API; it does not prove production art direction, skinned animation,
11
+ advanced PBR, WebGPU, postprocess, or commercial game polish.
12
+
13
+ Before presenting a route from this template as production-quality, replace any
14
+ placeholder art with typed primary assets from the Aura3D CLI, keep
15
+ `model(assets.x)` for real assets, prove each claimed mechanic with keyboard
16
+ tests, and capture desktop/mobile screenshots where the main subject is
17
+ readable.
18
+
6
19
  ```bash
7
20
  npm install
8
21
  npm run dev
@@ -8,6 +8,6 @@
8
8
  </head>
9
9
  <body>
10
10
  <div id="app"></div>
11
- <script type="module" src="/src/main.ts"></script>
11
+ <script type="module" src="./src/main.ts"></script>
12
12
  </body>
13
13
  </html>
@@ -8,7 +8,7 @@
8
8
  "typecheck": "tsc --noEmit",
9
9
  "build": "npm run typecheck && vite build",
10
10
  "preview": "vite preview --host 127.0.0.1",
11
- "test": "playwright test tests/route-health.spec.ts tests/screenshot.spec.ts --workers=1"
11
+ "test": "playwright test tests/route-health.spec.ts tests/playable.spec.ts tests/screenshot.spec.ts --workers=1"
12
12
  },
13
13
  "dependencies": {
14
14
  "@aura3d/engine": "1.0.0"
@@ -1,27 +1,257 @@
1
- import { createAuraApp, definePromptPlan, promptPlanToScene } from "@aura3d/engine";
1
+ import {
2
+ camera,
3
+ createAuraApp,
4
+ game,
5
+ lights,
6
+ material,
7
+ model,
8
+ primitives,
9
+ scene,
10
+ type AuraNodeInput
11
+ } from "@aura3d/engine";
2
12
  import { assets } from "./aura-assets";
3
13
 
4
- const plan = definePromptPlan({
5
- sceneType: "mini-game",
6
- subject: { asset: assets.playerModel, label: "player" },
7
- style: "readable neon collect-and-dodge arena",
8
- environment: "bounded board with rails, lane markings, coins, hazards, and portal goal",
9
- camera: { preset: "game-board" },
10
- lighting: { preset: "game-readable" },
11
- effects: ["motion-trail", "hud", "bloom"],
12
- interaction: "keyboard",
13
- acceptanceCriteria: [
14
- "player, coins, hazards, and portal are all visible",
15
- "arena boundaries and lane direction are readable",
16
- "motion trail and shield communicate active game state"
14
+ declare global {
15
+ interface Window {
16
+ __AURA3D_MINI_GAME__?: MiniGameEvidence;
17
+ }
18
+ }
19
+
20
+ interface MiniGameEvidence {
21
+ readonly status: string;
22
+ readonly frame: number;
23
+ readonly score: number;
24
+ readonly deaths: number;
25
+ readonly checkpointId: string;
26
+ readonly collected: readonly string[];
27
+ readonly player: {
28
+ readonly x: number;
29
+ readonly y: number;
30
+ readonly grounded: boolean;
31
+ };
32
+ readonly events: readonly string[];
33
+ readonly evidence: unknown;
34
+ }
35
+
36
+ const level = {
37
+ start: { x: 0, y: 0.35 },
38
+ finish: { x: 12.4, y: 0.35 },
39
+ platforms: [
40
+ { id: "launch", x: -0.6, y: 0, width: 4.2, height: 0.35 },
41
+ { id: "middle", x: 3.8, y: 0.62, width: 3.2, height: 0.3 },
42
+ { id: "finish", x: 8.2, y: 0.18, width: 4.8, height: 0.35 }
43
+ ],
44
+ movingPlatforms: [
45
+ { id: "lift", x: 6.9, y: 1.05, width: 1.35, height: 0.22, axis: "y" as const, amplitude: 0.42, period: 2.4 }
46
+ ],
47
+ collectibles: [
48
+ { id: "coin-01", x: 1.4, y: 1.15, value: 50, radius: 0.55 },
49
+ { id: "coin-02", x: 4.8, y: 1.45, value: 75, radius: 0.55 },
50
+ { id: "coin-03", x: 9.8, y: 1.05, value: 100, radius: 0.55 }
51
+ ],
52
+ hazards: [
53
+ { id: "spikes", x: 7.7, y: 0.42, width: 0.85, height: 0.24 }
54
+ ],
55
+ checkpoints: [
56
+ { id: "mid", x: 5.2, y: 0.92, radius: 0.8 }
17
57
  ],
18
- negativeCriteria: [
19
- "do not accept random primitives without readable game state",
20
- "do not accept a character on an empty grid as a mini-game"
21
- ]
22
- } as const);
58
+ lowerBound: -2.4,
59
+ moveSpeed: 5.2,
60
+ jumpVelocity: 8,
61
+ dashSpeed: 9.5
62
+ };
23
63
 
24
- createAuraApp("#app", {
64
+ const input = game.input({
65
+ actions: {
66
+ left: ["KeyA", "ArrowLeft"],
67
+ right: ["KeyD", "ArrowRight"],
68
+ jump: ["Space", "KeyW", "ArrowUp"],
69
+ dash: ["ShiftLeft", "ShiftRight"],
70
+ down: ["KeyS", "ArrowDown"],
71
+ reset: ["KeyR"]
72
+ },
73
+ axes: {
74
+ moveX: { negative: "left", positive: "right" }
75
+ },
76
+ bufferMs: 140
77
+ });
78
+
79
+ const platformer = game.platformer(level);
80
+ const routeEvents = game.eventLog({ label: "mini game route events", maxEvents: 16 });
81
+ const hud = game.hud.bindings([
82
+ game.hud.score({ valuePath: "appState.score" }),
83
+ game.hud.lives({ valuePath: "appState.lives" }),
84
+ game.hud.objective({ valuePath: "appState.objective" }),
85
+ game.hud.checkpoint({ valuePath: "appState.checkpoint" }),
86
+ game.hud.eventLog({ valuePath: "appState.events" })
87
+ ]);
88
+
89
+ const app = createAuraApp("#app", {
25
90
  diagnostics: { overlay: true, performancePanel: true },
26
- scene: promptPlanToScene(plan)
91
+ scene: buildScene()
92
+ });
93
+
94
+ const player = app.nodes.require("mini-player");
95
+ const lift = app.nodes.require("platform-lift");
96
+ const coins = level.collectibles.map((coin) => [coin.id, app.nodes.require(`coin-${coin.id}`)] as const);
97
+ const checkpoint = app.nodes.require("checkpoint-mid");
98
+ const goal = app.nodes.require("goal");
99
+ const hudRoot = createHud();
100
+
101
+ let objective = "Collect coins, avoid spikes, reach the goal";
102
+
103
+ app.onFrame(({ dt }: { readonly dt: number }) => {
104
+ input.update(dt);
105
+ if (input.pressed("reset")) {
106
+ platformer.reset();
107
+ routeEvents.push({ type: "reset", label: "Route reset" });
108
+ objective = "Collect coins, avoid spikes, reach the goal";
109
+ }
110
+
111
+ const state = platformer.step(dt, {
112
+ moveX: input.axis("moveX"),
113
+ jumpPressed: input.buffered("jump"),
114
+ jumpHeld: input.held("jump"),
115
+ dashPressed: input.pressed("dash"),
116
+ fastFall: input.held("down")
117
+ });
118
+
119
+ for (const event of state.events) {
120
+ routeEvents.push({
121
+ type: event.type,
122
+ label: event.id ? `${event.type}:${event.id}` : event.type,
123
+ targetId: event.id,
124
+ severity: event.type === "complete" || event.type === "collect" ? "success" : event.type === "hazard" || event.type === "fall" ? "warning" : "info",
125
+ frame: event.frame,
126
+ time: event.time
127
+ });
128
+ if (event.type === "checkpoint") objective = "Checkpoint reached. Finish the route.";
129
+ if (event.type === "complete") objective = "Finished. Press R to replay.";
130
+ if (event.type === "respawn") objective = "Respawned. Take the safer route.";
131
+ }
132
+
133
+ player.setPosition(state.player.x, state.player.y + 0.54, 0);
134
+ lift.setPosition(level.movingPlatforms[0].x + level.movingPlatforms[0].width / 2, movingLiftY(state.time), 0);
135
+ for (const [id, node] of coins) node.setVisible(!state.collected.includes(id));
136
+ checkpoint.setVisible(!state.activatedCheckpoints.includes("mid"));
137
+ goal.setScale(state.status === "completed" ? [0.28, 1.08, 0.18] : [0.22, 0.92, 0.16]);
138
+
139
+ renderHud(state);
140
+ publishEvidence(state);
27
141
  });
142
+
143
+ publishEvidence(platformer.snapshot());
144
+ renderHud(platformer.snapshot());
145
+
146
+ function buildScene() {
147
+ const nodes: AuraNodeInput[] = [
148
+ model(assets.playerModel, { name: "typed mini-game player", castShadow: true })
149
+ .position(level.start.x, level.start.y + 0.54, 0)
150
+ .scale(0.42)
151
+ .runtime(game.runtimeNode("mini-player", { tags: ["player", "typed-asset", "runtime"] })),
152
+ ...level.platforms.map((platform) =>
153
+ primitives.box({ name: `${platform.id} platform`, material: material.neon({ color: "#62d8ef", emissive: "#62d8ef", emissiveIntensity: 0.18 }) })
154
+ .position(platform.x + platform.width / 2, platform.y + platform.height / 2, -0.05)
155
+ .scale([platform.width, platform.height, 0.22])
156
+ ),
157
+ primitives.box({ name: "moving lift platform", material: material.neon({ color: "#b5f77d", emissive: "#b5f77d", emissiveIntensity: 0.35 }) })
158
+ .position(level.movingPlatforms[0].x + level.movingPlatforms[0].width / 2, movingLiftY(0), 0)
159
+ .scale([level.movingPlatforms[0].width, level.movingPlatforms[0].height, 0.22])
160
+ .runtime(game.runtimeNode("platform-lift", { tags: ["moving-platform", "runtime"] })),
161
+ ...level.collectibles.map((coin) =>
162
+ primitives.sphere({ name: `${coin.id} collectible`, material: material.neon({ color: "#f7d76b", emissive: "#f7d76b", emissiveIntensity: 0.7 }) })
163
+ .position(coin.x, coin.y, 0.02)
164
+ .scale(0.18)
165
+ .runtime(game.runtimeNode(`coin-${coin.id}`, { tags: ["coin", "runtime"] }))
166
+ ),
167
+ primitives.box({ name: "hazard spikes", material: material.neon({ color: "#f06b7a", emissive: "#f06b7a", emissiveIntensity: 0.7 }) })
168
+ .position(level.hazards[0].x + level.hazards[0].width / 2, level.hazards[0].y + level.hazards[0].height / 2, 0.05)
169
+ .scale([level.hazards[0].width, level.hazards[0].height, 0.2]),
170
+ primitives.box({ name: "checkpoint marker", material: material.neon({ color: "#9df59e", emissive: "#9df59e", emissiveIntensity: 0.65 }) })
171
+ .position(level.checkpoints[0].x, level.checkpoints[0].y + 0.42, 0.08)
172
+ .scale([0.14, 0.85, 0.14])
173
+ .runtime(game.runtimeNode("checkpoint-mid", { tags: ["checkpoint", "runtime"] })),
174
+ primitives.box({ name: "finish portal", material: material.neon({ color: "#ffad57", emissive: "#ffad57", emissiveIntensity: 0.82 }) })
175
+ .position(level.finish.x, level.finish.y + 0.7, 0.08)
176
+ .scale([0.22, 0.92, 0.16])
177
+ .runtime(game.runtimeNode("goal", { tags: ["finish", "runtime"] }))
178
+ ];
179
+
180
+ return scene()
181
+ .background("#071015")
182
+ .addMany(nodes)
183
+ .add(lights.ambient({ name: "game ambient", intensity: 0.38, color: "#d9f8ff" }))
184
+ .add(lights.directional({ name: "game key", position: [4, 6, 5], intensity: 1.2, color: "#ffffff" }))
185
+ .camera(camera.perspective({ position: [5.8, 4.3, 8.5], target: [5.8, 0.9, 0], fov: 44 }));
186
+ }
187
+
188
+ function movingLiftY(time: number): number {
189
+ const platform = level.movingPlatforms[0];
190
+ return platform.y + platform.height / 2 + Math.sin((time / platform.period) * Math.PI * 2) * platform.amplitude;
191
+ }
192
+
193
+ function createHud(): HTMLElement {
194
+ const root = document.createElement("aside");
195
+ root.id = "mini-game-hud";
196
+ root.style.cssText = [
197
+ "position:absolute",
198
+ "left:16px",
199
+ "top:16px",
200
+ "z-index:5",
201
+ "min-width:260px",
202
+ "font:600 13px/1.35 Inter, system-ui, sans-serif",
203
+ "color:#f5fbff",
204
+ "background:rgba(3,9,14,0.78)",
205
+ "border:1px solid rgba(125,220,235,0.34)",
206
+ "border-radius:8px",
207
+ "padding:12px",
208
+ "pointer-events:none"
209
+ ].join(";");
210
+ document.body.append(root);
211
+ return root;
212
+ }
213
+
214
+ function renderHud(state: ReturnType<typeof platformer.snapshot>): void {
215
+ hudRoot.innerHTML = [
216
+ `<strong>Aura3D Mini Game</strong>`,
217
+ `<div>Score ${state.score} | Lives ${state.lives} | Deaths ${state.deaths}</div>`,
218
+ `<div>Checkpoint ${state.checkpointId}</div>`,
219
+ `<div>${objective}</div>`,
220
+ `<div>Move A/D or arrows. Jump Space. Dash Shift. Reset R.</div>`
221
+ ].join("");
222
+ }
223
+
224
+ function publishEvidence(state: ReturnType<typeof platformer.snapshot>): void {
225
+ const evidence = app.evidence({
226
+ input,
227
+ events: routeEvents,
228
+ hud,
229
+ appState: {
230
+ score: state.score,
231
+ lives: state.lives,
232
+ objective,
233
+ checkpoint: state.checkpointId,
234
+ events: routeEvents.events().map((event: { readonly label: string }) => event.label)
235
+ },
236
+ assets: {
237
+ typedAssets: Object.keys(assets).length,
238
+ missingAssets: []
239
+ },
240
+ source: { expectsGame: true }
241
+ });
242
+ window.__AURA3D_MINI_GAME__ = {
243
+ status: state.status,
244
+ frame: state.frame,
245
+ score: state.score,
246
+ deaths: state.deaths,
247
+ checkpointId: state.checkpointId,
248
+ collected: state.collected,
249
+ player: {
250
+ x: state.player.x,
251
+ y: state.player.y,
252
+ grounded: state.player.grounded
253
+ },
254
+ events: routeEvents.events().map((event: { readonly label: string }) => event.label),
255
+ evidence
256
+ };
257
+ }
@@ -0,0 +1,49 @@
1
+ import { expect, test } from "@playwright/test";
2
+
3
+ test.setTimeout(60_000);
4
+
5
+ type MiniGameState = {
6
+ readonly score: number;
7
+ readonly events: readonly string[];
8
+ readonly player: {
9
+ readonly x: number;
10
+ readonly y: number;
11
+ };
12
+ };
13
+
14
+ function readMiniGameState(): MiniGameState | undefined {
15
+ return (window as unknown as { readonly __AURA3D_MINI_GAME__?: MiniGameState }).__AURA3D_MINI_GAME__;
16
+ }
17
+
18
+ test("Aura3D mini game responds to keyboard input, scoring, and reset", async ({ page }) => {
19
+ await page.goto("/");
20
+ await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 45_000 }).toBe("true");
21
+
22
+ const initial = await page.evaluate(readMiniGameState);
23
+ expect(initial?.player.x).toBeLessThan(0.2);
24
+
25
+ await page.keyboard.down("ArrowRight");
26
+ await page.waitForTimeout(650);
27
+ await page.keyboard.up("ArrowRight");
28
+
29
+ const moved = await page.evaluate(readMiniGameState);
30
+ expect(moved?.player.x ?? 0).toBeGreaterThan((initial?.player.x ?? 0) + 1.2);
31
+
32
+ await page.keyboard.press("Space");
33
+ await page.waitForTimeout(180);
34
+ const jumped = await page.evaluate(readMiniGameState);
35
+ expect(jumped?.player.y ?? 0).toBeGreaterThan(moved?.player.y ?? 0);
36
+
37
+ await page.keyboard.down("ArrowRight");
38
+ await page.waitForTimeout(1600);
39
+ await page.keyboard.up("ArrowRight");
40
+ const progressed = await page.evaluate(readMiniGameState);
41
+ expect(progressed?.score ?? 0).toBeGreaterThanOrEqual(50);
42
+ expect(progressed?.events.length ?? 0).toBeGreaterThan(0);
43
+
44
+ await page.keyboard.press("KeyR");
45
+ await page.waitForTimeout(120);
46
+ const reset = await page.evaluate(readMiniGameState);
47
+ expect(reset?.player.x ?? 99).toBeLessThan(0.3);
48
+ expect(reset?.events.some((event) => event.includes("reset"))).toBe(true);
49
+ });
@@ -4,60 +4,65 @@ import { expect, test } from "@playwright/test";
4
4
 
5
5
  test.setTimeout(60_000);
6
6
 
7
- test("Aura3D mini game screenshot shows a playable collect-and-dodge scene", async ({ page }) => {
7
+ test("Aura3D mini game screenshot shows a readable playable scene", async ({ page }) => {
8
8
  await page.goto("/");
9
9
  await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 45_000 }).toBe("true");
10
+
11
+ await page.keyboard.down("ArrowRight");
12
+ await page.waitForTimeout(550);
13
+ await page.keyboard.up("ArrowRight");
14
+
15
+ const state = await page.evaluate(() =>
16
+ (window as unknown as {
17
+ readonly __AURA3D_MINI_GAME__?: {
18
+ readonly score: number;
19
+ readonly player: { readonly x: number };
20
+ };
21
+ }).__AURA3D_MINI_GAME__
22
+ );
23
+ expect(state?.player.x ?? 0).toBeGreaterThan(0.8);
24
+
10
25
  const canvas = page.locator("canvas");
11
26
  const profile = await canvas.evaluate((element) => {
12
27
  const target = element as HTMLCanvasElement;
13
28
  const gl = target.getContext("webgl2", { preserveDrawingBuffer: true });
14
- if (!gl) return { error: "missing-webgl2", robotArmorPixels: 0, robotJointPixels: 0, boostPixels: 0, coinPixels: 0, hazardPixels: 0, portalPixels: 0, cyanTrailPixels: 0, arenaPixels: 0, uniqueBuckets: 0 };
29
+ if (!gl) return { error: "missing-webgl2", brightPixels: 0, warmPixels: 0, cyanPixels: 0, redPixels: 0, uniqueBuckets: 0 };
15
30
  const pixels = new Uint8Array(target.width * target.height * 4);
16
31
  gl.readPixels(0, 0, target.width, target.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
17
32
  const buckets = new Set<string>();
18
- let robotArmorPixels = 0;
19
- let robotJointPixels = 0;
20
- let boostPixels = 0;
21
- let coinPixels = 0;
22
- let hazardPixels = 0;
23
- let portalPixels = 0;
24
- let cyanTrailPixels = 0;
25
- let arenaPixels = 0;
33
+ let brightPixels = 0;
34
+ let warmPixels = 0;
35
+ let cyanPixels = 0;
36
+ let redPixels = 0;
26
37
  for (let y = 0; y < target.height; y += 4) {
27
38
  for (let x = 0; x < target.width; x += 4) {
28
- if (x > target.width * 0.76 && y > target.height * 0.74) continue;
29
39
  const offset = (y * target.width + x) * 4;
30
40
  const r = pixels[offset] ?? 0;
31
41
  const g = pixels[offset + 1] ?? 0;
32
42
  const b = pixels[offset + 2] ?? 0;
33
43
  const luminance = r * 0.2126 + g * 0.7152 + b * 0.0722;
34
- if (luminance > 32) buckets.add(`${r >> 5}-${g >> 5}-${b >> 5}`);
35
- const inPlayerWindow = x > target.width * 0.2 && x < target.width * 0.42 && y > target.height * 0.34 && y < target.height * 0.7;
36
- if (inPlayerWindow && r > 120 && g > 118 && b > 86 && Math.abs(r - g) < 70) robotArmorPixels += 1;
37
- if (inPlayerWindow && r < 95 && g < 115 && b < 125 && luminance > 18) robotJointPixels += 1;
38
- if (inPlayerWindow && r > 150 && g > 80 && b < 82) boostPixels += 1;
39
- if (r > 170 && g > 130 && b < 110) coinPixels += 1;
40
- if (r > 165 && g < 125 && b < 145) hazardPixels += 1;
41
- if (r > 165 && g > 95 && b < 125) portalPixels += 1;
42
- if (g > 140 && b > 150 && r < 150) cyanTrailPixels += 1;
43
- if (g > 42 && b > 50 && b > r * 1.08 && luminance > 24 && luminance < 100) arenaPixels += 1;
44
+ if (luminance > 30) {
45
+ brightPixels += 1;
46
+ buckets.add(`${r >> 5}-${g >> 5}-${b >> 5}`);
47
+ }
48
+ if (r > 150 && g > 110 && b < 110) warmPixels += 1;
49
+ if (g > 120 && b > 130 && r < 150) cyanPixels += 1;
50
+ if (r > 145 && g < 115 && b < 140) redPixels += 1;
44
51
  }
45
52
  }
46
- return { robotArmorPixels, robotJointPixels, boostPixels, coinPixels, hazardPixels, portalPixels, cyanTrailPixels, arenaPixels, uniqueBuckets: buckets.size };
53
+ return { brightPixels, warmPixels, cyanPixels, redPixels, uniqueBuckets: buckets.size };
47
54
  });
55
+
48
56
  const screenshot = await page.screenshot({ fullPage: false });
49
57
  mkdirSync(resolve("tests/reports"), { recursive: true });
50
58
  writeFileSync(resolve("tests/reports/screenshot.png"), screenshot);
51
- writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength, profile }, null, 2)}\n`);
59
+ writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength, profile, state }, null, 2)}\n`);
60
+
52
61
  expect(profile.error).toBeUndefined();
53
- expect(profile.robotArmorPixels).toBeGreaterThan(90);
54
- expect(profile.robotJointPixels).toBeGreaterThan(18);
55
- expect(profile.boostPixels).toBeGreaterThan(8);
56
- expect(profile.coinPixels).toBeGreaterThan(35);
57
- expect(profile.hazardPixels).toBeGreaterThan(45);
58
- expect(profile.portalPixels).toBeGreaterThan(45);
59
- expect(profile.cyanTrailPixels).toBeGreaterThan(90);
60
- expect(profile.arenaPixels).toBeGreaterThan(600);
61
- expect(profile.uniqueBuckets).toBeGreaterThan(22);
62
+ expect(profile.brightPixels).toBeGreaterThan(900);
63
+ expect(profile.cyanPixels).toBeGreaterThan(20);
64
+ expect(profile.warmPixels).toBeGreaterThan(10);
65
+ expect(profile.redPixels).toBeGreaterThan(5);
66
+ expect(profile.uniqueBuckets).toBeGreaterThan(10);
62
67
  expect(screenshot.byteLength).toBeGreaterThan(1000);
63
68
  });