create-aura3d 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +0 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/templates/fighting-game/README.md +11 -5
- package/templates/fighting-game/package.json +1 -1
- package/templates/fighting-game/playwright.config.ts +4 -4
- package/templates/fighting-game/src/game/fighters.ts +16 -4
- package/templates/fighting-game/src/game/stage.ts +4 -4
- package/templates/fighting-game/src/main.ts +32 -22
- package/templates/fighting-game/tests/gameplay-smoke.spec.ts +12 -0
- package/templates/fighting-game/tests/route-health.spec.ts +5 -0
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ export function createA3DProject(options) {
|
|
|
28
28
|
const packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
29
29
|
packageJson.dependencies = {
|
|
30
30
|
...(packageJson.dependencies ?? {}),
|
|
31
|
-
"@aura3d/engine": options.packageVersion ?? "1.0.
|
|
31
|
+
"@aura3d/engine": options.packageVersion ?? "1.0.6"
|
|
32
32
|
};
|
|
33
33
|
writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
34
34
|
return {
|
package/package.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
This template demonstrates Aura3D's public game runtime APIs:
|
|
4
4
|
|
|
5
|
-
- `
|
|
5
|
+
- `createGameApp(...)` for one mounted Aura game app with lifecycle evidence.
|
|
6
6
|
- `.runtime(game.runtimeNode("id"))` for mutable fighter and camera nodes.
|
|
7
|
-
- `
|
|
7
|
+
- `gameApp.input` / `gameApp.inputController(...)` for runtime-owned keyboard/gamepad input.
|
|
8
8
|
- `game.inputReplay(...)` and `game.inputReplayDriver(...)` for deterministic replay smoke paths.
|
|
9
9
|
- `game.kinematicBody(...)` and `game.jumpAssist(...)` for 2.5D movement, gravity, coyote time, jump buffering, dash, and knockback.
|
|
10
10
|
- `game.combatWorld(...)` for hitboxes, hurtboxes, guard, hit-stop, stun, recovery, and typed combat events.
|
|
@@ -43,8 +43,9 @@ Controls:
|
|
|
43
43
|
Add fighter assets:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
npx @aura3d/cli@latest assets
|
|
47
|
-
npx @aura3d/cli@latest assets
|
|
46
|
+
npx @aura3d/cli@latest assets search "animated humanoid fighting character" --profile fighting-character --json
|
|
47
|
+
npx @aura3d/cli@latest assets resolve "animated humanoid fighting character" --name playerFighter --profile fighting-character
|
|
48
|
+
npx @aura3d/cli@latest assets resolve "animated humanoid fighting character" --name rivalFighter --profile fighting-character
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
The CLI writes typed definitions to `src/aura-assets.ts`. The starter imports
|
|
@@ -55,9 +56,14 @@ or `unsafeModelUrl(...)`.
|
|
|
55
56
|
Before claiming the route is asset-ready, run:
|
|
56
57
|
|
|
57
58
|
```bash
|
|
58
|
-
npx @aura3d/cli@latest assets validate-game
|
|
59
|
+
npx @aura3d/cli@latest assets validate-game --profile fighting-character --json
|
|
59
60
|
```
|
|
60
61
|
|
|
62
|
+
If the catalog returns no production-ready fighting-character candidate, keep
|
|
63
|
+
the route in placeholder mode and do not invent a GLB URL. The refusal is a
|
|
64
|
+
valid safety result; production proof requires two distinct typed fighter
|
|
65
|
+
assets that pass the profile gate.
|
|
66
|
+
|
|
61
67
|
The source-level readiness declaration in `src/game/stage.ts` is not launch
|
|
62
68
|
evidence. Before claiming build, package, visual, accessibility, or launch
|
|
63
69
|
readiness, archive the matching command output, runtime evidence JSON,
|
|
@@ -2,11 +2,11 @@ import { defineConfig } from "@playwright/test";
|
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
webServer: {
|
|
5
|
-
command: "npm run dev",
|
|
6
|
-
url: "http://127.0.0.1:
|
|
7
|
-
reuseExistingServer:
|
|
5
|
+
command: "npm run dev -- --port 5186",
|
|
6
|
+
url: "http://127.0.0.1:5186",
|
|
7
|
+
reuseExistingServer: false
|
|
8
8
|
},
|
|
9
9
|
use: {
|
|
10
|
-
baseURL: "http://127.0.0.1:
|
|
10
|
+
baseURL: "http://127.0.0.1:5186"
|
|
11
11
|
}
|
|
12
12
|
});
|
|
@@ -34,9 +34,10 @@ export const REQUIRED_FIGHTER_CLIPS: readonly FighterClip[] = [
|
|
|
34
34
|
];
|
|
35
35
|
|
|
36
36
|
export const publicAssetInstructions = [
|
|
37
|
-
"npx @aura3d/cli@latest assets
|
|
38
|
-
"npx @aura3d/cli@latest assets
|
|
39
|
-
"npx @aura3d/cli@latest assets
|
|
37
|
+
"npx @aura3d/cli@latest assets search \"animated humanoid fighting character\" --profile fighting-character --json",
|
|
38
|
+
"npx @aura3d/cli@latest assets resolve \"animated humanoid fighting character\" --name playerFighter --profile fighting-character",
|
|
39
|
+
"npx @aura3d/cli@latest assets resolve \"animated humanoid fighting character\" --name rivalFighter --profile fighting-character",
|
|
40
|
+
"npx @aura3d/cli@latest assets validate-game --profile fighting-character --json"
|
|
40
41
|
] as const;
|
|
41
42
|
|
|
42
43
|
export const fighterAnimationClips: readonly AuraNamedAnimationClipDefinition<FighterClip>[] = REQUIRED_FIGHTER_CLIPS.map((id) => ({
|
|
@@ -82,7 +83,7 @@ export function createFighterAnimationController(
|
|
|
82
83
|
): AnimationController<FighterClip> {
|
|
83
84
|
return new AnimationController<FighterClip>({
|
|
84
85
|
id: `${id}-animation-controller`,
|
|
85
|
-
...(asset ? { clipRegistry: asset } : {}),
|
|
86
|
+
...(asset ? { clipRegistry: animationClipRegistry(asset) } : {}),
|
|
86
87
|
clips: fighterAnimationClips,
|
|
87
88
|
requiredClips: REQUIRED_FIGHTER_CLIPS,
|
|
88
89
|
suppressRootMotion: true,
|
|
@@ -93,6 +94,17 @@ export function createFighterAnimationController(
|
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
function animationClipRegistry(asset: AuraAssetRef<"model">) {
|
|
98
|
+
const metadata: Record<string, unknown> = { ...(asset.metadata ?? {}) };
|
|
99
|
+
return {
|
|
100
|
+
id: asset.id,
|
|
101
|
+
name: asset.id,
|
|
102
|
+
url: asset.url,
|
|
103
|
+
hash: asset.hash,
|
|
104
|
+
metadata
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
96
108
|
export function createFighterNode(
|
|
97
109
|
id: FighterId,
|
|
98
110
|
assetKey: FighterAssetKey,
|
|
@@ -30,12 +30,12 @@ export const fightingRouteReadiness = {
|
|
|
30
30
|
entry: "src/main.ts",
|
|
31
31
|
sourceFiles: ["src/main.ts", "src/aura-assets.ts", "src/game/fighters.ts", "src/game/moves.ts", "src/game/stage.ts"],
|
|
32
32
|
publicEngineApis: [
|
|
33
|
-
"
|
|
33
|
+
"createGameApp",
|
|
34
34
|
"scene",
|
|
35
35
|
"model(typed AuraAssetRef)",
|
|
36
36
|
"game.runtimeNode",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
37
|
+
"gameApp.onFrame",
|
|
38
|
+
"gameApp.input",
|
|
39
39
|
"game.evidence",
|
|
40
40
|
"game.kinematicBody",
|
|
41
41
|
"game.jumpAssist",
|
|
@@ -57,7 +57,7 @@ export const fightingRouteReadiness = {
|
|
|
57
57
|
gameplaySmokeSpec: "tests/gameplay-smoke.spec.ts",
|
|
58
58
|
localCommandsNotRunByTemplate: ["npm run typecheck", "npm run build", "npm run test"],
|
|
59
59
|
evidenceRequiredBeforeLaunchReady: [
|
|
60
|
-
"npx @aura3d/cli@latest assets validate-game output",
|
|
60
|
+
"npx @aura3d/cli@latest assets validate-game --profile fighting-character --json output",
|
|
61
61
|
"npm run test scaffold output proving route load and replay hit",
|
|
62
62
|
"browser screenshot showing a typed GLB fighter moved by runtime node mutation",
|
|
63
63
|
"archived collectGameRuntimeEvidence/app.evidence JSON under tests/reports/game-runtime/",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { camera,
|
|
1
|
+
import { camera, createGameApp, effects, game, lights, scene, ui } from "@aura3d/engine";
|
|
2
2
|
import { assets } from "./aura-assets";
|
|
3
3
|
import {
|
|
4
4
|
animationLayer,
|
|
@@ -25,6 +25,7 @@ import "./styles.css";
|
|
|
25
25
|
type Aura3DGameWindow = Window & {
|
|
26
26
|
__AURA3D_GAME_DEBUG__?: unknown;
|
|
27
27
|
__AURA3D_GAME_EVIDENCE__?: unknown;
|
|
28
|
+
__AURA3D_GAME_RUNTIME__?: unknown;
|
|
28
29
|
__AURA3D_GAME_REPLAY__?: unknown;
|
|
29
30
|
__AURA3D_GAME_SOURCE__?: unknown;
|
|
30
31
|
};
|
|
@@ -112,6 +113,18 @@ const accessibilitySources = [
|
|
|
112
113
|
})
|
|
113
114
|
];
|
|
114
115
|
|
|
116
|
+
const inputOptions = {
|
|
117
|
+
actions: {
|
|
118
|
+
...fightingControls.actions,
|
|
119
|
+
guard: ["KeyQ", "GamepadLB"],
|
|
120
|
+
heavy: ["KeyK", "GamepadY"],
|
|
121
|
+
pause: ["Escape", "GamepadStart"]
|
|
122
|
+
},
|
|
123
|
+
axes: fightingControls.axes,
|
|
124
|
+
bufferMs: 150,
|
|
125
|
+
gamepad: true
|
|
126
|
+
} as const;
|
|
127
|
+
|
|
115
128
|
const arena = scene()
|
|
116
129
|
.background("#10071c")
|
|
117
130
|
.addMany(fightingStage.nodes)
|
|
@@ -120,29 +133,20 @@ const arena = scene()
|
|
|
120
133
|
.addMany([
|
|
121
134
|
effects.bloom({ intensity: 0.32 }),
|
|
122
135
|
lights.studio({ intensity: 1.15 }),
|
|
123
|
-
lights.directional({ name: "rim light", color: "#80ffd4", intensity: 0.7 }).position(0, 4, 3)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
]);
|
|
136
|
+
lights.directional({ name: "rim light", color: "#80ffd4", intensity: 0.7 }).position(0, 4, 3)
|
|
137
|
+
])
|
|
138
|
+
.camera(camera.perspective({ position: [0, 1.75, 5.8], target: [0, 0.85, 0], fov: 42 }));
|
|
127
139
|
|
|
128
|
-
const
|
|
140
|
+
const gameApp = createGameApp("#app", {
|
|
129
141
|
diagnostics: { overlay: true, performancePanel: true },
|
|
142
|
+
input: inputOptions,
|
|
143
|
+
loop: { fixedDt: 1 / 60 },
|
|
130
144
|
scene: arena
|
|
131
145
|
});
|
|
132
146
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
guard: ["KeyQ", "GamepadLB"],
|
|
137
|
-
heavy: ["KeyK", "GamepadY"],
|
|
138
|
-
pause: ["Escape", "GamepadStart"]
|
|
139
|
-
},
|
|
140
|
-
axes: fightingControls.axes,
|
|
141
|
-
bufferMs: 150,
|
|
142
|
-
gamepad: true
|
|
143
|
-
} as const;
|
|
144
|
-
|
|
145
|
-
const input = app.input(inputOptions);
|
|
147
|
+
const app = gameApp.app;
|
|
148
|
+
const input = gameApp.input;
|
|
149
|
+
if (!input) throw new Error("create-aura3d fighting-game template failed to create runtime-owned input.");
|
|
146
150
|
const replayInput = game.input({ ...inputOptions, autoListen: false, gamepad: false });
|
|
147
151
|
const openingReplay = game.inputReplay(
|
|
148
152
|
[
|
|
@@ -196,6 +200,11 @@ gameWindow.__AURA3D_GAME_SOURCE__ = {
|
|
|
196
200
|
template: "fighting-game",
|
|
197
201
|
package: "create-aura3d",
|
|
198
202
|
publicEngineApi: true,
|
|
203
|
+
lifecycle: {
|
|
204
|
+
kind: gameApp.kind,
|
|
205
|
+
usesCreateGameApp: true,
|
|
206
|
+
runtimeEvidenceGlobal: "__AURA3D_GAME_RUNTIME__"
|
|
207
|
+
},
|
|
199
208
|
typedAssetPattern: "src/aura-assets.ts",
|
|
200
209
|
typedAssetKeys: REQUIRED_FIGHTER_ASSETS,
|
|
201
210
|
missingAssets: missingFighterAssets,
|
|
@@ -223,7 +232,7 @@ ui.onClick(replayButton, () => {
|
|
|
223
232
|
|
|
224
233
|
ui.onClick(pauseButton, () => setPaused(!paused));
|
|
225
234
|
|
|
226
|
-
|
|
235
|
+
gameApp.onFrame(({ dt }) => {
|
|
227
236
|
const activeInput = replayActive ? replayInput : input;
|
|
228
237
|
if (replayActive) {
|
|
229
238
|
replayDriver.step(dt);
|
|
@@ -360,6 +369,7 @@ app.onFrame(({ dt }) => {
|
|
|
360
369
|
totalHitCount,
|
|
361
370
|
liveInputEvents: input.recorded()
|
|
362
371
|
};
|
|
372
|
+
gameWindow.__AURA3D_GAME_RUNTIME__ = gameApp.evidence;
|
|
363
373
|
gameWindow.__AURA3D_GAME_DEBUG__ = game.debug.overlay({
|
|
364
374
|
runtime: app.runtime,
|
|
365
375
|
input: activeInput,
|
|
@@ -397,8 +407,8 @@ function setPaused(next: boolean): void {
|
|
|
397
407
|
paused = next;
|
|
398
408
|
ui.setPressed(pauseButton, paused);
|
|
399
409
|
ui.setText(pauseButton, paused ? "Resume" : "Pause");
|
|
400
|
-
if (paused)
|
|
401
|
-
else
|
|
410
|
+
if (paused) gameApp.pause();
|
|
411
|
+
else gameApp.resume();
|
|
402
412
|
}
|
|
403
413
|
|
|
404
414
|
function syncFighterAnimation(controller: ReturnType<typeof createFighterAnimationController>, clip: FighterClip, dt: number): void {
|
|
@@ -4,11 +4,23 @@ test("input replay produces runtime evidence and a hit declaration", async ({ pa
|
|
|
4
4
|
await page.goto("/");
|
|
5
5
|
await page.click("#hud-replay-button");
|
|
6
6
|
await page.waitForFunction(() => Boolean((window as any).__AURA3D_GAME_EVIDENCE__?.systems?.inputPlan));
|
|
7
|
+
await page.waitForFunction(() => Boolean((window as any).__AURA3D_GAME_RUNTIME__?.kind === "aura-game-app-runtime-evidence"));
|
|
7
8
|
await page.waitForFunction(() => ((window as any).__AURA3D_GAME_REPLAY__?.hitCount ?? 0) > 0);
|
|
8
9
|
const evidence = await page.evaluate(() => (window as any).__AURA3D_GAME_EVIDENCE__);
|
|
10
|
+
const runtime = await page.evaluate(() => (window as any).__AURA3D_GAME_RUNTIME__);
|
|
9
11
|
const replay = await page.evaluate(() => (window as any).__AURA3D_GAME_REPLAY__);
|
|
10
12
|
const source = await page.evaluate(() => (window as any).__AURA3D_GAME_SOURCE__);
|
|
11
13
|
|
|
14
|
+
expect(runtime).toMatchObject({
|
|
15
|
+
status: "running",
|
|
16
|
+
running: true,
|
|
17
|
+
started: true,
|
|
18
|
+
startCount: 1,
|
|
19
|
+
inputControllers: 1,
|
|
20
|
+
activeInputControllers: 1
|
|
21
|
+
});
|
|
22
|
+
expect(runtime.frame).toBeGreaterThan(0);
|
|
23
|
+
expect(runtime.loop.frame).toBeGreaterThan(0);
|
|
12
24
|
expect(evidence.systems.mutableNodes).toBeTruthy();
|
|
13
25
|
expect(evidence.systems.inputPlan).toBeTruthy();
|
|
14
26
|
expect(evidence.systems.physicsPlan).toBeTruthy();
|
|
@@ -7,5 +7,10 @@ test("fighting-game route loads", async ({ page }) => {
|
|
|
7
7
|
await page.waitForFunction(() => Boolean((window as any).__AURA3D_GAME_SOURCE__?.readiness));
|
|
8
8
|
const source = await page.evaluate(() => (window as any).__AURA3D_GAME_SOURCE__);
|
|
9
9
|
expect(source.readiness.route).toBe("/");
|
|
10
|
+
expect(source.lifecycle).toMatchObject({
|
|
11
|
+
kind: "aura-game-app-runtime",
|
|
12
|
+
usesCreateGameApp: true,
|
|
13
|
+
runtimeEvidenceGlobal: "__AURA3D_GAME_RUNTIME__"
|
|
14
|
+
});
|
|
10
15
|
expect(source.readiness.buildDeclarations.routeHealthSpec).toBe("tests/route-health.spec.ts");
|
|
11
16
|
});
|