@aura3d/engine 1.3.3 → 1.4.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 (229) hide show
  1. package/README.md +122 -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 +206 -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 +2 -0
  102. package/dist/create-aura3d/index.js.map +1 -1
  103. package/dist/create-aura3d/showcase-spec-artifacts.d.ts +104 -0
  104. package/dist/create-aura3d/showcase-spec-artifacts.d.ts.map +1 -0
  105. package/dist/create-aura3d/showcase-spec-artifacts.js +142 -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 +815 -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 +662 -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 +276 -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 +304 -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 +181 -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/RuntimeNodeHandle.d.ts +4 -0
  200. package/dist/engine/agent-api/RuntimeNodeHandle.d.ts.map +1 -1
  201. package/dist/engine/agent-api/RuntimeNodeHandle.js +2 -0
  202. package/dist/engine/agent-api/RuntimeNodeHandle.js.map +1 -1
  203. package/dist/engine/agent-api/SceneGroundingUtils.d.ts +86 -0
  204. package/dist/engine/agent-api/SceneGroundingUtils.d.ts.map +1 -1
  205. package/dist/engine/agent-api/SceneGroundingUtils.js +116 -0
  206. package/dist/engine/agent-api/SceneGroundingUtils.js.map +1 -1
  207. package/dist/engine/agent-api/index.d.ts +149 -5
  208. package/dist/engine/agent-api/index.d.ts.map +1 -1
  209. package/dist/engine/agent-api/index.js +1893 -113
  210. package/dist/engine/agent-api/index.js.map +1 -1
  211. package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
  212. package/dist/engine/production-runtime/TypedGLBActor.d.ts +14 -2
  213. package/dist/engine/production-runtime/TypedGLBActor.d.ts.map +1 -1
  214. package/dist/engine/production-runtime/TypedGLBActor.js +95 -4
  215. package/dist/engine/production-runtime/TypedGLBActor.js.map +1 -1
  216. package/dist/rendering/ForwardPass.js +7 -1
  217. package/dist/rendering/ForwardPass.js.map +1 -1
  218. package/dist/rendering/MorphTarget.d.ts +1 -0
  219. package/dist/rendering/MorphTarget.d.ts.map +1 -1
  220. package/dist/rendering/MorphTarget.js.map +1 -1
  221. package/dist/rendering/Renderer.js +7 -1
  222. package/dist/rendering/Renderer.js.map +1 -1
  223. package/package.json +22 -22
  224. package/templates/mini-game/README.md +13 -0
  225. package/templates/mini-game/index.html +1 -1
  226. package/templates/mini-game/package.json +1 -1
  227. package/templates/mini-game/src/main.ts +251 -21
  228. package/templates/mini-game/tests/playable.spec.ts +49 -0
  229. package/templates/mini-game/tests/screenshot.spec.ts +37 -32
@@ -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
  });