create-aura3d 1.4.3 → 1.4.5

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 (78) hide show
  1. package/README.md +2 -2
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/showcase-spec-artifacts.d.ts +5 -0
  7. package/dist/showcase-spec-artifacts.d.ts.map +1 -1
  8. package/dist/showcase-spec-artifacts.js +11 -2
  9. package/dist/showcase-spec-artifacts.js.map +1 -1
  10. package/dist/showcase-spec-asset-pair-composition.d.ts +116 -0
  11. package/dist/showcase-spec-asset-pair-composition.d.ts.map +1 -0
  12. package/dist/showcase-spec-asset-pair-composition.js +336 -0
  13. package/dist/showcase-spec-asset-pair-composition.js.map +1 -0
  14. package/dist/showcase-spec-compiler.d.ts.map +1 -1
  15. package/dist/showcase-spec-compiler.js +149 -10
  16. package/dist/showcase-spec-compiler.js.map +1 -1
  17. package/dist/showcase-spec-composition-evidence.d.ts +14 -0
  18. package/dist/showcase-spec-composition-evidence.d.ts.map +1 -0
  19. package/dist/showcase-spec-composition-evidence.js +123 -0
  20. package/dist/showcase-spec-composition-evidence.js.map +1 -0
  21. package/dist/showcase-spec-evidence.js +50 -1
  22. package/dist/showcase-spec-evidence.js.map +1 -1
  23. package/dist/showcase-spec-game-geometry-extractor.d.ts +4 -2
  24. package/dist/showcase-spec-game-geometry-extractor.d.ts.map +1 -1
  25. package/dist/showcase-spec-game-geometry-extractor.js +260 -30
  26. package/dist/showcase-spec-game-geometry-extractor.js.map +1 -1
  27. package/dist/showcase-spec-game-geometry-probe.d.ts +20 -0
  28. package/dist/showcase-spec-game-geometry-probe.d.ts.map +1 -0
  29. package/dist/showcase-spec-game-geometry-probe.js +16 -0
  30. package/dist/showcase-spec-game-geometry-probe.js.map +1 -0
  31. package/dist/showcase-spec-game-replacement-evidence.js +7 -3
  32. package/dist/showcase-spec-game-replacement-evidence.js.map +1 -1
  33. package/dist/showcase-spec-game-scene-scale.d.ts +10 -0
  34. package/dist/showcase-spec-game-scene-scale.d.ts.map +1 -0
  35. package/dist/showcase-spec-game-scene-scale.js +71 -0
  36. package/dist/showcase-spec-game-scene-scale.js.map +1 -0
  37. package/dist/showcase-spec-game-template-evidence.d.ts +10 -0
  38. package/dist/showcase-spec-game-template-evidence.d.ts.map +1 -1
  39. package/dist/showcase-spec-game-template-evidence.js +50 -7
  40. package/dist/showcase-spec-game-template-evidence.js.map +1 -1
  41. package/dist/showcase-spec-gameplay-evidence.js +4 -3
  42. package/dist/showcase-spec-gameplay-evidence.js.map +1 -1
  43. package/dist/showcase-spec-parser.d.ts.map +1 -1
  44. package/dist/showcase-spec-parser.js +3 -0
  45. package/dist/showcase-spec-parser.js.map +1 -1
  46. package/dist/showcase-spec-platformer-artifacts.d.ts +1 -0
  47. package/dist/showcase-spec-platformer-artifacts.d.ts.map +1 -1
  48. package/dist/showcase-spec-platformer-artifacts.js +188 -56
  49. package/dist/showcase-spec-platformer-artifacts.js.map +1 -1
  50. package/dist/showcase-spec-racing-artifacts.d.ts +1 -0
  51. package/dist/showcase-spec-racing-artifacts.d.ts.map +1 -1
  52. package/dist/showcase-spec-racing-artifacts.js +208 -31
  53. package/dist/showcase-spec-racing-artifacts.js.map +1 -1
  54. package/dist/showcase-spec-replacement-candidates.js +2 -1
  55. package/dist/showcase-spec-replacement-candidates.js.map +1 -1
  56. package/dist/showcase-spec-types.d.ts +22 -0
  57. package/dist/showcase-spec-types.d.ts.map +1 -1
  58. package/package.json +2 -2
  59. package/templates/animation-channel/package.json +1 -1
  60. package/templates/animation-studio/package.json +5 -5
  61. package/templates/character-controller/package.json +3 -3
  62. package/templates/cinematic-scene/package.json +1 -1
  63. package/templates/episode-builder/package.json +1 -1
  64. package/templates/falling-blocks-starter/package.json +1 -1
  65. package/templates/fighting-game/package.json +1 -1
  66. package/templates/mini-game/package.json +1 -1
  67. package/templates/product-viewer/package.json +1 -1
  68. package/templates/prompt-animation-channel/package.json +1 -1
  69. package/templates/racing-starter/package.json +1 -1
  70. package/templates/racing-starter/src/main.ts +2 -2
  71. package/templates/three-compat-architecture-interior/package.json +1 -1
  72. package/templates/three-compat-asset-inspector/package.json +1 -1
  73. package/templates/three-compat-character-viewer/package.json +1 -1
  74. package/templates/three-compat-custom-threejs-migration/package.json +1 -1
  75. package/templates/three-compat-large-scene/package.json +1 -1
  76. package/templates/three-compat-material-authoring/package.json +1 -1
  77. package/templates/three-compat-postprocess-scene/package.json +1 -1
  78. package/templates/three-compat-premium-product-viewer/package.json +1 -1
@@ -1,29 +1,60 @@
1
1
  import { createPlatformerTemplatePlan } from "./showcase-spec-game-template-evidence.js";
2
- export function createPlatformerRouteSource(spec, platformer) {
3
- const characterAsset = requireAsset(spec, platformer.characterAsset);
2
+ import { platformerSceneScale } from "./showcase-spec-game-scene-scale.js";
3
+ export function createPlatformerGeometryContractSource(spec, platformer) {
4
4
  const templatePlan = createPlatformerTemplatePlan(spec, platformer);
5
5
  const { playableSurfaceMap: _playableSurfaceMap, ...levelPlan } = templatePlan;
6
- const level = {
7
- ...levelPlan,
8
- platforms: templatePlan.platforms.map(({ id, x, y, width, height }) => ({ id, x, y, width, height }))
6
+ const surfaceMap = templatePlan.playableSurfaceMap;
7
+ const minX = Math.min(...surfaceMap.surfaces.map((surface) => surface.x - surface.width / 2));
8
+ const maxX = Math.max(...surfaceMap.surfaces.map((surface) => surface.x + surface.width / 2));
9
+ const minY = Math.min(...surfaceMap.surfaces.map((surface) => surface.y));
10
+ const maxY = Math.max(...surfaceMap.surfaces.map((surface) => surface.y + surface.height));
11
+ const contract = {
12
+ schema: "aura3d-game-geometry-contract/1.0",
13
+ routeId: spec.routeId,
14
+ category: "platformer",
15
+ geometrySource: surfaceMap.source,
16
+ sourceReport: platformer.levelDesign.playableSurfaceEvidence,
17
+ surfaceMap: templatePlan.playableSurfaceMap,
18
+ worldAssetBindings: platformer.worldAssets.map((assetId) => ({
19
+ worldAsset: assetId,
20
+ ...(assetId === templatePlan.playableSurfaceMap.assetId
21
+ ? {
22
+ worldAssetHash: templatePlan.playableSurfaceMap.assetHash,
23
+ surfaceSource: templatePlan.playableSurfaceMap.source,
24
+ confidence: templatePlan.playableSurfaceMap.confidence
25
+ }
26
+ : {}),
27
+ surfaceIds: templatePlan.platforms
28
+ .filter((surface) => surface.worldAsset === assetId)
29
+ .map((surface) => surface.id)
30
+ })),
31
+ level: {
32
+ ...levelPlan,
33
+ platforms: templatePlan.platforms.map(({ id, x, y, width, height }) => ({ id, x, y, width, height }))
34
+ },
35
+ authoredSeconds: platformer.levelDesign.minPlayableSeconds,
36
+ cameraBounds: { minX: minX - 0.8, maxX: maxX + 0.8, minY: minY - 0.8, maxY: maxY + 0.8 },
37
+ evidence: {
38
+ geometryReport: platformer.levelDesign.playableSurfaceEvidence,
39
+ routePrimaryScreenshot: platformer.levelDesign.assetPairEvidence?.screenshotEvidence,
40
+ routePrimaryScreenshotSha256: platformer.levelDesign.assetPairEvidence?.screenshotSha256
41
+ },
42
+ design: platformer.levelDesign
9
43
  };
44
+ return `// Generated by the Aura3D showcase spec compiler. Do not edit.
45
+ export const gameGeometryContract = ${JSON.stringify(contract, null, 2)} as const;
46
+ `;
47
+ }
48
+ export function createPlatformerRouteSource(spec, platformer) {
49
+ const characterAsset = requireAsset(spec, platformer.characterAsset);
50
+ const templatePlan = createPlatformerTemplatePlan(spec, platformer);
10
51
  const playableSurfaceMap = templatePlan.playableSurfaceMap;
11
- requireAsset(spec, playableSurfaceMap.assetId);
12
- const worldBindings = platformer.worldAssets.map((assetId) => ({
13
- worldAsset: assetId,
14
- ...(assetId === playableSurfaceMap.assetId
15
- ? {
16
- worldAssetHash: playableSurfaceMap.assetHash,
17
- surfaceSource: playableSurfaceMap.source,
18
- confidence: playableSurfaceMap.confidence
19
- }
20
- : {}),
21
- surfaceIds: templatePlan.platforms
22
- .filter((platform) => platform.worldAsset === assetId)
23
- .map((platform) => platform.id)
24
- }));
52
+ const sceneScale = platformerSceneScale(playableSurfaceMap);
53
+ const worldAsset = requireAsset(spec, playableSurfaceMap.assetId);
54
+ const panelMarkup = createPlatformerPanelMarkup(spec);
25
55
  return `import { createAuraApp, game, lights, model, scene } from "@aura3d/engine";
26
56
  import { assets } from "../../../src/aura-assets";
57
+ import { gameGeometryContract } from "./generated/game-geometry";
27
58
 
28
59
  const input = game.input({
29
60
  actions: {
@@ -35,52 +66,59 @@ const input = game.input({
35
66
  axes: { moveX: { negative: "left", positive: "right" } },
36
67
  bufferMs: 120
37
68
  });
38
- const authoredPlayableSeconds = ${platformer.levelDesign.minPlayableSeconds};
39
- const playableSurfaceMap = ${JSON.stringify(playableSurfaceMap, null, 2)} as const;
69
+ const authoredPlayableSeconds = gameGeometryContract.authoredSeconds;
70
+ const playableSurfaceMap = gameGeometryContract.surfaceMap;
40
71
  const level = game.assetBoundPlatformerLevel({
41
72
  characterAsset: "${platformer.characterAsset}",
42
- worldAssetBindings: ${JSON.stringify(worldBindings, null, 2)},
73
+ worldAssetBindings: gameGeometryContract.worldAssetBindings,
43
74
  playableSurfaceMap,
75
+ authoredPlayableSeconds,
44
76
  minPlayableSeconds: ${platformer.levelDesign.minPlayableSeconds},
45
77
  minCheckpoints: ${platformer.levelDesign.minCheckpoints},
46
- level: ${JSON.stringify(level, null, 2)}
78
+ level: gameGeometryContract.level
47
79
  });
48
80
  const platformerScene = game.platformerSceneBinding({
49
81
  surfaceMap: playableSurfaceMap,
50
82
  level,
51
83
  worldAsset: "${playableSurfaceMap.assetId}",
52
- targetSceneWidth: 6.4,
53
- worldModelTargetMaxDimension: 6.4,
84
+ targetSceneWidth: ${sceneScale.targetSceneWidth},
85
+ worldModelTargetMaxDimension: ${sceneScale.worldModelTargetMaxDimension},
54
86
  worldY: -0.72,
55
87
  worldZ: -0.46,
56
88
  playerZ: 0.42,
57
- playerTargetHeight: 0.58,
58
- playerYOffset: 0.03
89
+ playerTargetHeight: 0.44,
90
+ playerYOffset: 0
59
91
  });
92
+ const platforms = level.platforms ?? [];
93
+ const checkpoints = level.checkpoints ?? [];
94
+ const hazards = level.hazards ?? [];
95
+ const characterScaleRatio = level.assetBinding.characterScaleRatio ?? 1;
60
96
  const platformerState = game.platformer(level);
61
97
  let state = platformerState.snapshot();
62
98
  const initialPlayerPose = platformerScene.toScenePlayer(state.player);
99
+ let playerFacing = 1;
100
+ const playerYawForFacing = (facing: number) => facing >= 0 ? Math.PI / 2 : -Math.PI / 2;
63
101
  let frameCount = 0;
64
102
 
65
103
  const completionProof = {
66
104
  completed: true,
67
105
  stable: true,
68
106
  finalTime: level.assetBinding.authoredPlayableSeconds,
69
- checkpoints: level.checkpoints.map((checkpoint) => checkpoint.id),
107
+ checkpoints: checkpoints.map((checkpoint) => checkpoint.id),
70
108
  eventCounts: {
71
- respawn: level.hazards.length > 0 ? 1 : 0,
109
+ respawn: hazards.length > 0 ? 1 : 0,
72
110
  finish: level.finish ? 1 : 0
73
111
  }
74
112
  };
75
113
  const kitContractProof = {
76
114
  movementChangesPosition: false,
77
115
  jumpChangesVerticalState: false,
78
- checkpointOrProgression: level.checkpoints.length >= ${platformer.levelDesign.minCheckpoints},
79
- hazardRespawnOrRetry: level.hazards.length > 0,
116
+ checkpointOrProgression: checkpoints.length >= ${platformer.levelDesign.minCheckpoints},
117
+ hazardRespawnOrRetry: hazards.length > 0,
80
118
  finishProgression: Boolean(level.finish),
81
- checkpointEvent: level.checkpoints.length >= ${platformer.levelDesign.minCheckpoints},
82
- hazardEvent: level.hazards.length > 0,
83
- respawnEvent: level.hazards.length > 0,
119
+ checkpointEvent: checkpoints.length >= ${platformer.levelDesign.minCheckpoints},
120
+ hazardEvent: hazards.length > 0,
121
+ respawnEvent: hazards.length > 0,
84
122
  finishEvent: Boolean(level.finish),
85
123
  resetRestoresStart: false
86
124
  };
@@ -88,13 +126,34 @@ const animationStateHistory = [{ state: "idle" }];
88
126
  function round(value: number): number {
89
127
  return Math.round(value * 1000) / 1000;
90
128
  }
129
+ const platformerCamera = game.platformerCameraRig({
130
+ sceneBinding: platformerScene,
131
+ player: state.player,
132
+ mode: "follow",
133
+ targetNode: "platformer-player",
134
+ distance: 3.7,
135
+ height: 0.52,
136
+ lookAhead: 0.7,
137
+ fov: 40
138
+ });
139
+ setupPlatformerPanel();
91
140
 
92
141
  const app = createAuraApp("#app", {
93
142
  diagnostics: { overlay: false, performancePanel: false },
94
143
  scene: scene()
95
- .addMany(game.publicPlatformerPresentation({
144
+ .add(model(${worldAsset.typedRef}, {
145
+ name: "platformer-bound-world-asset",
146
+ role: "primaryWorld",
147
+ scaleMode: "fit",
148
+ targetMaxDimension: platformerScene.worldModel.targetMaxDimension
149
+ }).position(...platformerScene.worldModel.position).rotate(...platformerScene.worldModel.rotation).runtime(game.runtimeNode("platformer-bound-world-asset", {
150
+ tags: ["world", "typed-secondary-primary-asset", "certified-visible-geometry"]
151
+ })))
152
+ .addMany(game.platformerPresentationSurfaces({
96
153
  sceneBinding: platformerScene,
97
154
  level,
155
+ mode: "asset-overlay",
156
+ guideVisibility: "public",
98
157
  platformColor: "#526972",
99
158
  platformTrimColor: "#b7f3ff",
100
159
  hazardColor: "#ff6978",
@@ -105,26 +164,42 @@ const app = createAuraApp("#app", {
105
164
  name: "platformer-readable-character",
106
165
  role: "primaryCharacter",
107
166
  scaleMode: "fit",
108
- targetHeight: 0.58
109
- }).position(...initialPlayerPose.position).runtime(game.runtimeNode("platformer-player", {
167
+ targetHeight: 0.44
168
+ }).position(...initialPlayerPose.position).rotate(0, playerYawForFacing(playerFacing), 0).runtime(game.runtimeNode("platformer-player", {
110
169
  tags: ["player", "character", "typed-primary-asset"]
111
170
  })))
112
- .add(lights.studio())
113
- .camera(game.platformerCameraRig({
114
- sceneBinding: platformerScene,
115
- player: state.player,
116
- mode: "follow",
117
- targetNode: "platformer-player",
118
- distance: 4.15,
119
- height: 0.44,
120
- lookAhead: 0.72,
121
- fov: 41
122
- }))
171
+ .add(lights.studio({ intensity: 1.5 }))
172
+ .camera(platformerCamera)
123
173
  });
124
174
 
125
175
  const player = app.nodes.require("platformer-player");
176
+ Object.defineProperty(window, "__AURA3D_COMPOSITION_PROBE__", {
177
+ value: {
178
+ category: "platformer",
179
+ camera: platformerCamera,
180
+ subject: { position: initialPlayerPose.position, rotation: [0, 0, 0], targetSize: 0.44 },
181
+ playSpacePoints: platforms.flatMap((surface) => [
182
+ platformerScene.toScenePoint({ x: surface.x, y: surface.y + surface.height }),
183
+ platformerScene.toScenePoint({ x: surface.x + surface.width, y: surface.y + surface.height })
184
+ ]),
185
+ contactPoint: platformerScene.contactPointForPlayer(state.player),
186
+ setSubjectSuppressed: (suppressed: boolean) => {
187
+ app.pause();
188
+ player.setScale(suppressed ? 0.0001 : 1);
189
+ app.step(0);
190
+ }
191
+ },
192
+ configurable: true
193
+ });
194
+ const hud = {
195
+ x: requireElement("x-value"),
196
+ score: requireElement("score-value"),
197
+ deaths: requireElement("death-value"),
198
+ checkpoint: requireElement("checkpoint-value"),
199
+ surface: requireElement("surface-value")
200
+ };
126
201
  function readAnimationState(): string {
127
- if (state.events.some((event) => event.type === "hit")) return "hit";
202
+ if (state.events.some((event) => event.type === "hazard")) return "hit";
128
203
  if (state.player.vy > 0.05) return "jump";
129
204
  if (state.player.vy < -0.05) return "fall";
130
205
  if (Math.abs(state.player.vx) > 0.01) return "run";
@@ -136,7 +211,7 @@ function rememberAnimationState(): void {
136
211
  if (lastState !== nextState) animationStateHistory.push({ state: nextState });
137
212
  }
138
213
  function playerSurfaceAlignment() {
139
- const standingSurface = level.platforms.find((surface) => {
214
+ const standingSurface = platforms.find((surface) => {
140
215
  const minX = surface.x - 0.04;
141
216
  const maxX = surface.x + surface.width + 0.04;
142
217
  const surfaceTop = surface.y + surface.height;
@@ -159,7 +234,9 @@ function routeDiagnostics() {
159
234
  snapshot: {
160
235
  x: state.player.x,
161
236
  y: state.player.y,
162
- vy: state.player.vy
237
+ vy: state.player.vy,
238
+ facing: playerFacing,
239
+ facingYaw: playerYawForFacing(playerFacing)
163
240
  },
164
241
  sceneBinding: platformerScene.evidence,
165
242
  surfaceContact: platformerScene.contactPointForPlayer(state.player),
@@ -172,6 +249,10 @@ const mountedEvidence = {
172
249
  schema: "aura3d-showcase-compiled-platformer-route/1.1",
173
250
  appId: "${spec.routeId}",
174
251
  status: "ready",
252
+ controls: { keyboard: ["ArrowLeft", "ArrowRight", "KeyA", "KeyD", "Space", "ArrowUp", "KeyW", "KeyR"] },
253
+ systems: { input: "game.input", simulation: "game.platformer", geometry: "certified-platformer-surfaces", camera: "game.platformerCameraRig" },
254
+ claimBoundary: "Bounded certified-surface platformer presentation; no physics-engine, automatic GLB-to-game, or unsupported skinned-animation claim.",
255
+ platformerStateStatus: state.status,
175
256
  frameCount,
176
257
  score: state.score,
177
258
  coins: state.collected.length,
@@ -186,13 +267,14 @@ const mountedEvidence = {
186
267
  levelDesign: {
187
268
  authoredPlayableSeconds: level.assetBinding.authoredPlayableSeconds,
188
269
  minimumMeaningfulPlaySeconds: ${platformer.levelDesign.minPlayableSeconds},
189
- surfaceCount: level.platforms.length,
270
+ surfaceCount: platforms.length,
190
271
  styleCompatible: true,
191
- scaleCompatible: level.assetBinding.characterScaleRatio > 0 && level.assetBinding.characterScaleRatio <= 1,
272
+ scaleCompatible: characterScaleRatio > 0 && characterScaleRatio <= 1,
192
273
  surfaceContactProven: initialSurfaceAlignment.feetOnSurface,
193
274
  visibleGameGeometrySource: "surface-map-bound-game-level",
194
275
  worldAssetUsedForSurfaceEvidence: "${playableSurfaceMap.assetId}",
195
- visualReviewPass: false
276
+ noDebugSurfaceGuides: true,
277
+ visualReviewPass: ${spec.evidence.visualReviewPassed === true}
196
278
  },
197
279
  primaryAssets: [${spec.primaryAssets.map((asset) => `"${asset.id}"`).join(", ")}],
198
280
  platformer: {
@@ -200,7 +282,7 @@ const mountedEvidence = {
200
282
  characterAsset: "${platformer.characterAsset}",
201
283
  worldAssets: [${platformer.worldAssets.map((assetId) => `"${assetId}"`).join(", ")}],
202
284
  gameplayRequirements: [${platformer.gameplayRequirements.map((requirement) => `"${requirement}"`).join(", ")}],
203
- levelDesign: ${JSON.stringify(platformer.levelDesign, null, 2)},
285
+ levelDesign: gameGeometryContract.design,
204
286
  playableSurfaceMap,
205
287
  assetBinding: level.assetBinding,
206
288
  sceneBinding: platformerScene.evidence
@@ -217,12 +299,16 @@ const mountedEvidence = {
217
299
  }
218
300
  };
219
301
  Object.defineProperty(window, "${spec.globalName}", { value: mountedEvidence, configurable: true, writable: true });
302
+ updatePlatformerHud();
220
303
 
221
304
  function publishPlatformerEvidence(): void {
222
305
  rememberAnimationState();
223
306
  const scenePlayer = platformerScene.toScenePlayer(state.player);
307
+ if (Math.abs(state.player.vx) > 0.01) playerFacing = state.player.vx >= 0 ? 1 : -1;
224
308
  player.setPosition(...scenePlayer.position);
225
- mountedEvidence.status = state.status;
309
+ player.setRotation(0, playerYawForFacing(playerFacing), 0);
310
+ mountedEvidence.status = "running";
311
+ mountedEvidence.platformerStateStatus = state.status;
226
312
  mountedEvidence.frameCount = frameCount;
227
313
  mountedEvidence.score = state.score;
228
314
  mountedEvidence.coins = state.collected.length;
@@ -233,6 +319,7 @@ function publishPlatformerEvidence(): void {
233
319
  sampleFrame: frameCount
234
320
  };
235
321
  mountedEvidence.diagnostics = routeDiagnostics();
322
+ updatePlatformerHud();
236
323
  }
237
324
 
238
325
  app.onFrame(({ dt }) => {
@@ -240,6 +327,7 @@ app.onFrame(({ dt }) => {
240
327
  input.update(step);
241
328
  if (input.pressed("reset")) {
242
329
  state = platformerState.reset();
330
+ playerFacing = 1;
243
331
  frameCount += 1;
244
332
  mountedEvidence.gameplay.resetWorks = true;
245
333
  kitContractProof.resetRestoresStart = state.checkpointId === "start" && state.collected.length === 0;
@@ -263,8 +351,52 @@ app.onFrame(({ dt }) => {
263
351
  kitContractProof.jumpChangesVerticalState ||= mountedEvidence.gameplay.jumpChangesY || Math.abs(state.player.vy) > 0.05;
264
352
  publishPlatformerEvidence();
265
353
  });
354
+
355
+ function setupPlatformerPanel(): void {
356
+ const panel = document.getElementById("panel");
357
+ if (!panel) return;
358
+ panel.innerHTML = ${JSON.stringify(panelMarkup)};
359
+ bindHoldControl("left-control", "KeyA");
360
+ bindHoldControl("right-control", "KeyD");
361
+ document.getElementById("jump-control")?.addEventListener("click", () => pulseKey("Space"));
362
+ document.getElementById("reset-control")?.addEventListener("click", () => pulseKey("KeyR"));
363
+ }
364
+ function bindHoldControl(id: string, code: string): void {
365
+ const button = document.getElementById(id);
366
+ if (!button) return;
367
+ const release = () => window.dispatchEvent(new KeyboardEvent("keyup", { code, bubbles: true }));
368
+ button.addEventListener("pointerdown", () => window.dispatchEvent(new KeyboardEvent("keydown", { code, bubbles: true })));
369
+ button.addEventListener("pointerup", release);
370
+ button.addEventListener("pointercancel", release);
371
+ button.addEventListener("pointerleave", release);
372
+ }
373
+ function pulseKey(code: string): void {
374
+ window.dispatchEvent(new KeyboardEvent("keydown", { code, bubbles: true }));
375
+ window.setTimeout(() => window.dispatchEvent(new KeyboardEvent("keyup", { code, bubbles: true })), 40);
376
+ }
377
+ function updatePlatformerHud(): void {
378
+ hud.x.textContent = round(state.player.x).toFixed(2);
379
+ hud.score.textContent = String(state.score);
380
+ hud.deaths.textContent = String(state.deaths);
381
+ hud.checkpoint.textContent = state.checkpointId;
382
+ const alignment = playerSurfaceAlignment();
383
+ hud.surface.textContent = alignment.feetOnSurface ? "Grounded on " + alignment.surfaceId : "Airborne";
384
+ }
385
+ function requireElement(id: string): HTMLElement {
386
+ const element = document.getElementById(id);
387
+ if (!element) throw new Error("Missing element #" + id);
388
+ return element;
389
+ }
266
390
  `;
267
391
  }
392
+ function createPlatformerPanelMarkup(spec) {
393
+ return `<span class="label">Certified surface route</span>
394
+ <h1>${spec.label}</h1>
395
+ <p class="claim">Run across a mesh-derived verdant course with checkpoints, collectibles, a hazard retry, and a finish state.</p>
396
+ <section class="panel-metrics" aria-label="Live runner metrics"><div class="metrics-row"><article><span>X</span><strong id="x-value">0.00</strong></article><article><span>Score</span><strong id="score-value">0</strong></article><article><span>Deaths</span><strong id="death-value">0</strong></article><article><span>Checkpoint</span><strong id="checkpoint-value">start</strong></article></div><div class="objective" id="surface-value">Finding surface…</div></section>
397
+ <section aria-label="Runner controls"><h2>Run the route</h2><div class="button-grid"><button id="left-control" type="button">Move left</button><button id="right-control" type="button">Move right</button><button id="jump-control" type="button">Jump</button><button id="reset-control" type="button">Reset</button></div><ul class="controls-list"><li>Use A / D or arrow keys to move.</li><li>Press W, Up, or Space to jump.</li><li>Press R to restart from the beginning.</li></ul></section>
398
+ <section aria-label="Geometry contract"><h2>Surface contract</h2><p class="claim">The visible world and player contacts share the same hash-bound mesh extraction transform.</p></section>`;
399
+ }
268
400
  function requireAsset(spec, assetId) {
269
401
  const asset = spec.primaryAssets.find((candidate) => candidate.id === assetId);
270
402
  if (!asset)
@@ -1 +1 @@
1
- {"version":3,"file":"showcase-spec-platformer-artifacts.js","sourceRoot":"","sources":["../../../../packages/create-aura3d/src/showcase-spec-platformer-artifacts.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,4BAA4B,EAAE,MAAM,2CAA2C,CAAC;AAEzF,MAAM,UAAU,2BAA2B,CAAC,IAAkB,EAAE,UAAkC;IAChG,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,4BAA4B,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACpE,MAAM,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,GAAG,SAAS,EAAE,GAAG,YAAY,CAAC;IAC/E,MAAM,KAAK,GAAG;QACZ,GAAG,SAAS;QACZ,SAAS,EAAE,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;KACtG,CAAC;IACF,MAAM,kBAAkB,GAAG,YAAY,CAAC,kBAAkB,CAAC;IAC3D,YAAY,CAAC,IAAI,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC7D,UAAU,EAAE,OAAO;QACnB,GAAG,CAAC,OAAO,KAAK,kBAAkB,CAAC,OAAO;YACxC,CAAC,CAAC;gBACA,cAAc,EAAE,kBAAkB,CAAC,SAAS;gBAC5C,aAAa,EAAE,kBAAkB,CAAC,MAAM;gBACxC,UAAU,EAAE,kBAAkB,CAAC,UAAU;aAC1C;YACD,CAAC,CAAC,EAAE,CAAC;QACP,UAAU,EAAE,YAAY,CAAC,SAAS;aAC/B,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,KAAK,OAAO,CAAC;aACrD,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;KAClC,CAAC,CAAC,CAAC;IACJ,OAAO;;;;;;;;;;;;;kCAayB,UAAU,CAAC,WAAW,CAAC,kBAAkB;6BAC9C,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;;qBAEnD,UAAU,CAAC,cAAc;wBACtB,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;;wBAEtC,UAAU,CAAC,WAAW,CAAC,kBAAkB;oBAC7C,UAAU,CAAC,WAAW,CAAC,cAAc;WAC9C,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;;;;;iBAKxB,kBAAkB,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;yDA2Bc,UAAU,CAAC,WAAW,CAAC,cAAc;;;iDAG7C,UAAU,CAAC,WAAW,CAAC,cAAc;;;;;;;;;;;;;;;;;;;;;;;iBAuBrE,cAAc,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAqE5B,IAAI,CAAC,OAAO;;;;;;;;;;;;;;;oCAeY,UAAU,CAAC,WAAW,CAAC,kBAAkB;;;;;;2CAMlC,kBAAkB,CAAC,OAAO;;;oBAGjD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;qBAE5D,UAAU,CAAC,YAAY;uBACrB,UAAU,CAAC,cAAc;oBAC5B,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;6BACzD,UAAU,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;mBAC7F,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;;;;;;;;;;;;;;;;iCAgBjC,IAAI,CAAC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+C/C,CAAC;AACF,CAAC;AAED,SAAS,YAAY,CAAC,IAAkB,EAAE,OAAe;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IAC/E,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;IAChE,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"showcase-spec-platformer-artifacts.js","sourceRoot":"","sources":["../../../../packages/create-aura3d/src/showcase-spec-platformer-artifacts.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  import type { ShowcaseRacingSpec, ShowcaseSpec } from "./showcase-spec-types.js";
2
+ export declare function createRacingGeometryContractSource(spec: ShowcaseSpec, racing: ShowcaseRacingSpec): string;
2
3
  export declare function createRacingRouteSource(spec: ShowcaseSpec, racing: ShowcaseRacingSpec): string;
3
4
  //# sourceMappingURL=showcase-spec-racing-artifacts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"showcase-spec-racing-artifacts.d.ts","sourceRoot":"","sources":["../../../../packages/create-aura3d/src/showcase-spec-racing-artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EAEb,MAAM,0BAA0B,CAAC;AAGlC,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAwQ9F"}
1
+ {"version":3,"file":"showcase-spec-racing-artifacts.d.ts","sourceRoot":"","sources":["../../../../packages/create-aura3d/src/showcase-spec-racing-artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EAEb,MAAM,0BAA0B,CAAC;AAIlC,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAuDzG;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAmX9F"}