Skip to content

Commit de6e1d2

Browse files
author
DavidQ
committed
Normalize Asteroids runtime object rendering to shared manifest object IDs - PR_26133_129-asteroids-manifest-runtime-normalization
1 parent c5de5dc commit de6e1d2

13 files changed

Lines changed: 260 additions & 384 deletions
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# PR_26133_129 Asteroids Manifest Runtime Normalization
2+
3+
## Summary
4+
- Normalized Asteroids gameplay/demo/attract object rendering to exact Object Vector Studio V2 manifest object IDs.
5+
- Removed the active tag-based runtime lookup module and stopped passing `tags`/`runtimeRole` options into Asteroids object rendering.
6+
- Updated gameplay collision/profile geometry to resolve by exact manifest object ID instead of tag-derived runtime keys.
7+
- Preserved ship flame flicker, asteroid scale tuning, and the PR_26133_128 non-object effect/UI draw systems.
8+
9+
## Final Active Object IDs
10+
- Bullet: `object.asteroids.bullet`
11+
- Ship: `object.asteroids.ship`
12+
- Large asteroid: `object.asteroids.large-asteroid`
13+
- Medium asteroid: `object.asteroids.medium-asteroid`
14+
- Small asteroid: `object.asteroids.small-asteroid`
15+
- Large UFO: `object.asteroids.large-ufo`
16+
- Small UFO: `object.asteroids.small-ufo`
17+
18+
## Removed Aliases And Indirect Lookup Paths
19+
- Deleted `games/Asteroids/game/asteroidsObjectTags.js`.
20+
- Removed active imports/usages of:
21+
- `ASTEROIDS_RUNTIME_OBJECT_TAGS`
22+
- `ASTEROID_SIZE_RUNTIME_OBJECT_KEYS`
23+
- `runtimeObjectTagOptions`
24+
- `resolveAsteroidsTaggedObject`
25+
- `validateAsteroidsRuntimeObjectTags`
26+
- `objectsByKey`
27+
- Removed tag/runtime-role options from gameplay/demo/attract render calls.
28+
- Replaced runtime cache expectations with direct object-ID cache messages.
29+
- Replaced runtime object validation with exact required object ID validation.
30+
31+
## Remaining Intentional Non-Object Draw Systems
32+
- Starfield background pixels: procedural background effect.
33+
- HUD/menu/high-score text: UI text.
34+
- Attract text panels and demo trail pixels: UI/effect drawing.
35+
- Pause and initials overlays: modal UI.
36+
- Particle effects: transient randomized effects.
37+
- Ship debris fragments: runtime destruction effect using manifest-derived ship hull points from PR_26133_128.
38+
39+
## Validation
40+
- PASS: targeted Asteroids node validation:
41+
- `AsteroidsValidation`
42+
- `AsteroidsPresentation`
43+
- `AsteroidsVectorTransforms`
44+
- `AsteroidsPlatformDemo`
45+
- `AsteroidsAssetReferenceAdoption`
46+
- PASS: targeted Workspace Manager V2/Asteroids Playwright validation:
47+
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list -g "loads Object Vector Studio V2 runtime assets into Asteroids gameplay rendering"`
48+
- PASS: `git diff --check`
49+
- Playwright impacted: Yes. This PR changes Asteroids runtime object lookup/render behavior and validates Asteroids launch, attract/demo object rendering, gameplay object rendering, and bullet rotation via the targeted browser slice.
50+
- Full samples smoke test skipped as requested; this PR only touches Asteroids runtime normalization.
51+
52+
## Manual Validation
53+
- Open `/games/Asteroids/index.html`.
54+
- Let attract/demo run; expected: ship, UFO, and asteroids render through Object Vector Studio V2 manifest object IDs.
55+
- Start gameplay; expected: ship, bullets, asteroids, UFOs, and life icons render from the same manifest object renderer path.
56+
- Fire bullets at multiple ship angles; expected: `object.asteroids.bullet` preserves manifest styling and rotates with the shot direction.

games/Asteroids/game/AsteroidsAttractAdapter.js

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,6 @@ AsteroidsAttractAdapter.js
77
import { clamp } from '../../../src/shared/utils/mathUtils.js';
88
import { ASTEROIDS_OBJECT_GEOMETRY_IDS } from './asteroidsObjectGeometryManifest.js';
99

10-
const ASTEROID_RENDER_BINDINGS = Object.freeze({
11-
large: Object.freeze({
12-
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidLarge,
13-
objectKey: 'asteroidLarge',
14-
}),
15-
medium: Object.freeze({
16-
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidMedium,
17-
objectKey: 'asteroidMedium',
18-
}),
19-
small: Object.freeze({
20-
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidSmall,
21-
objectKey: 'asteroidSmall',
22-
}),
23-
});
24-
2510
function estimateTextWidth(text, fontPx) {
2611
return String(text ?? '').length * (fontPx * 0.62);
2712
}
@@ -104,17 +89,15 @@ export default class AsteroidsAttractAdapter {
10489
return clamp(timing.alpha, 0, 1);
10590
}
10691

107-
drawManifestAsteroid(renderer, sizeKey, options = {}) {
108-
const binding = ASTEROID_RENDER_BINDINGS[sizeKey];
109-
if (!binding) {
92+
drawManifestAsteroid(renderer, objectId, options = {}) {
93+
if (!objectId) {
11094
return;
11195
}
11296

11397
this.scene?.drawObjectVectorAsset?.(renderer, 'asteroids', {
114-
...this.scene.objectVectorTagOptions(binding.objectKey),
11598
elapsedMs: this.scene.objectVectorPlaybackMs,
11699
fps: 12,
117-
objectId: binding.objectId,
100+
objectId,
118101
requireManifestBinding: true,
119102
stateId: 'active',
120103
...options,
@@ -153,7 +136,6 @@ export default class AsteroidsAttractAdapter {
153136
}
154137

155138
this.scene?.drawObjectVectorAsset?.(renderer, 'ship', {
156-
...this.scene.objectVectorTagOptions('ship'),
157139
elapsedMs: this.scene.objectVectorPlaybackMs,
158140
fps: 12,
159141
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.ship,
@@ -164,16 +146,16 @@ export default class AsteroidsAttractAdapter {
164146
x: 328,
165147
y: 348,
166148
});
167-
this.drawManifestAsteroid(renderer, 'large', {
149+
this.drawManifestAsteroid(renderer, ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidLarge, {
168150
x: 632,
169151
y: 352,
170152
});
171-
this.drawManifestAsteroid(renderer, 'medium', {
153+
this.drawManifestAsteroid(renderer, ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidMedium, {
172154
rotation: 0.34,
173155
x: 698,
174156
y: 316,
175157
});
176-
this.drawManifestAsteroid(renderer, 'small', {
158+
this.drawManifestAsteroid(renderer, ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidSmall, {
177159
rotation: -0.46,
178160
x: 594,
179161
y: 306,
@@ -247,7 +229,6 @@ export default class AsteroidsAttractAdapter {
247229
const x = 480 + Math.cos(this.demoTime * 0.7) * 220;
248230
const y = 340 + Math.sin(this.demoTime * 1.1) * 130;
249231
this.scene?.drawObjectVectorAsset?.(renderer, 'ship', {
250-
...this.scene.objectVectorTagOptions('ship'),
251232
elapsedMs: this.scene.objectVectorPlaybackMs,
252233
fps: 12,
253234
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.ship,
@@ -258,23 +239,22 @@ export default class AsteroidsAttractAdapter {
258239
y,
259240
});
260241

261-
this.drawManifestAsteroid(renderer, 'large', {
242+
this.drawManifestAsteroid(renderer, ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidLarge, {
262243
rotation: this.demoTime * 0.25,
263244
x: 480 + Math.sin(this.demoTime * 0.5) * 250,
264245
y: 330 + Math.cos(this.demoTime * 0.9) * 120,
265246
});
266-
this.drawManifestAsteroid(renderer, 'medium', {
247+
this.drawManifestAsteroid(renderer, ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidMedium, {
267248
rotation: -this.demoTime * 0.31,
268249
x: 520 + Math.sin((this.demoTime * 0.78) + 1.4) * 190,
269250
y: 356 + Math.cos((this.demoTime * 0.64) + 0.7) * 96,
270251
});
271-
this.drawManifestAsteroid(renderer, 'small', {
252+
this.drawManifestAsteroid(renderer, ASTEROIDS_OBJECT_GEOMETRY_IDS.asteroidSmall, {
272253
rotation: this.demoTime * 0.42,
273254
x: 430 + Math.sin((this.demoTime * 0.92) + 2.2) * 154,
274255
y: 318 + Math.cos((this.demoTime * 0.72) + 2.7) * 78,
275256
});
276257
this.scene?.drawObjectVectorAsset?.(renderer, 'ufo', {
277-
...this.scene.objectVectorTagOptions('ufoLarge'),
278258
elapsedMs: this.scene.objectVectorPlaybackMs,
279259
fps: 12,
280260
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.ufoLarge,

games/Asteroids/game/AsteroidsGameScene.js

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ import AsteroidsHighScoreService from '../systems/AsteroidsHighScoreService.js';
1515
import AsteroidsInitialsEntry from '../systems/AsteroidsInitialsEntry.js';
1616
import { createAsteroidGeometryProfilesFromObjectVectorAssets } from './asteroidObjectGeometry.js';
1717
import {
18-
ASTEROID_SIZE_RUNTIME_OBJECT_KEYS,
19-
runtimeObjectTagOptions,
20-
validateAsteroidsRuntimeObjectTags
21-
} from './asteroidsObjectTags.js';
18+
ASTEROIDS_OBJECT_GEOMETRY_IDS,
19+
ASTEROIDS_ASTEROID_SIZE_OBJECT_IDS,
20+
ASTEROIDS_UFO_TYPE_OBJECT_IDS,
21+
validateAsteroidsRuntimeObjectIds
22+
} from './asteroidsObjectGeometryManifest.js';
2223
import {
2324
ASTEROIDS_GAME_OVER_AUTO_EXIT_SECONDS, ASTEROIDS_GAME_OVER_RETURN_MODE
2425
} from "../rules/flowRules.js";
@@ -89,16 +90,16 @@ export default class AsteroidsGameScene extends Scene {
8990
this.objectVectorAssets = options.objectVectorAssets || null;
9091
this.objectVectorRuntime = options.objectVectorRuntime || null;
9192
this.objectGeometry = options.objectGeometry || null;
92-
if (!this.objectGeometry?.objectsById || !this.objectGeometry?.objectsByKey) {
93+
if (!this.objectGeometry?.objectsById) {
9394
const message = 'Asteroids Object Vector manifest validation failed: object geometry was not loaded from game.manifest.json.';
9495
console.error(message);
9596
throw new Error(message);
9697
}
9798
this.objectVectorRuntimeObjectValidation = this.objectVectorAssets
98-
? validateAsteroidsRuntimeObjectTags([...this.objectVectorAssets.objectsById.values()], {
99+
? validateAsteroidsRuntimeObjectIds(this.objectVectorAssets.objectsById, {
99100
logger: this.objectVectorRuntime,
100101
})
101-
: { errors: [], objectsByKey: {}, ok: false, warnings: [] };
102+
: { errors: [], objectIds: [], ok: false, warnings: [] };
102103
if (this.objectVectorAssets && !this.objectVectorRuntimeObjectValidation.ok) {
103104
const message = validationFailureMessage(this.objectVectorRuntimeObjectValidation);
104105
this.objectVectorRuntime?.log?.('FAIL', message, {
@@ -737,12 +738,11 @@ export default class AsteroidsGameScene extends Scene {
737738
}
738739

739740
this.world.asteroids.forEach((asteroid) => {
740-
const objectKey = ASTEROID_SIZE_RUNTIME_OBJECT_KEYS[asteroid.size];
741+
const objectId = ASTEROIDS_ASTEROID_SIZE_OBJECT_IDS[asteroid.size] || "";
741742
this.drawObjectVectorAsset(renderer, "asteroids", {
742-
...this.objectVectorTagOptions(objectKey),
743743
elapsedMs: this.objectVectorPlaybackMs,
744744
fps: 12,
745-
objectId: this.objectVectorRuntimeObjectValidation.objectsByKey[objectKey]?.id || "",
745+
objectId,
746746
requireManifestBinding: true,
747747
rotation: asteroid.angle,
748748
stateId: "active",
@@ -752,12 +752,11 @@ export default class AsteroidsGameScene extends Scene {
752752
});
753753

754754
if (this.world.ufo) {
755-
const objectKey = this.world.ufo.type === "small" ? "ufoSmall" : "ufoLarge";
755+
const objectId = ASTEROIDS_UFO_TYPE_OBJECT_IDS[this.world.ufo.type] || ASTEROIDS_OBJECT_GEOMETRY_IDS.ufoLarge;
756756
this.drawObjectVectorAsset(renderer, "ufo", {
757-
...this.objectVectorTagOptions(objectKey),
758757
elapsedMs: this.objectVectorPlaybackMs,
759758
fps: 12,
760-
objectId: this.objectVectorRuntimeObjectValidation.objectsByKey[objectKey]?.id || "",
759+
objectId,
761760
requireManifestBinding: true,
762761
stateId: "active",
763762
x: this.world.ufo.x,
@@ -767,8 +766,7 @@ export default class AsteroidsGameScene extends Scene {
767766

768767
this.world.bullets.forEach((bullet) => {
769768
this.drawObjectVectorAsset(renderer, "bullet", {
770-
...this.objectVectorTagOptions("bullet"),
771-
objectId: this.objectVectorRuntimeObjectValidation.objectsByKey.bullet?.id || "",
769+
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.bullet,
772770
requireManifestBinding: true,
773771
rotation: bullet.angle,
774772
stateId: "active",
@@ -779,8 +777,7 @@ export default class AsteroidsGameScene extends Scene {
779777

780778
this.world.ufoBullets.forEach((bullet) => {
781779
this.drawObjectVectorAsset(renderer, "bullet", {
782-
...this.objectVectorTagOptions("bullet"),
783-
objectId: this.objectVectorRuntimeObjectValidation.objectsByKey.bullet?.id || "",
780+
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.bullet,
784781
requireManifestBinding: true,
785782
rotation: bullet.angle,
786783
stateId: "active",
@@ -793,10 +790,9 @@ export default class AsteroidsGameScene extends Scene {
793790

794791
if (this.world.shipActive && !this.session.isTurnIntroActive() && this.session.mode !== 'menu') {
795792
this.drawObjectVectorAsset(renderer, "ship", {
796-
...this.objectVectorTagOptions("ship"),
797793
elapsedMs: this.objectVectorPlaybackMs,
798794
fps: 12,
799-
objectId: this.objectVectorRuntimeObjectValidation.objectsByKey.ship?.id || "",
795+
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.ship,
800796
requireManifestBinding: true,
801797
rotation: this.world.ship.angle,
802798
stateId: this.world.ship.thrusting && this.session.mode === 'playing' ? "move" : "idle",
@@ -872,10 +868,6 @@ export default class AsteroidsGameScene extends Scene {
872868
this.publishObjectVectorRuntimeDiagnostics();
873869
}
874870

875-
objectVectorTagOptions(objectKey) {
876-
return runtimeObjectTagOptions(objectKey);
877-
}
878-
879871
drawLives(renderer, centerX, y, lives) {
880872
if (!lives) {
881873
return;
@@ -884,10 +876,9 @@ export default class AsteroidsGameScene extends Scene {
884876
const startX = centerX - ((lives - 1) * LIFE_SPACING) / 2;
885877
Array.from({ length: lives }).forEach((_, index) => {
886878
this.drawObjectVectorAsset(renderer, "shipLife", {
887-
...this.objectVectorTagOptions("ship"),
888879
elapsedMs: this.objectVectorPlaybackMs,
889880
fps: 12,
890-
objectId: this.objectVectorRuntimeObjectValidation.objectsByKey.ship?.id || "",
881+
objectId: ASTEROIDS_OBJECT_GEOMETRY_IDS.ship,
891882
requireManifestBinding: true,
892883
rotation: -Math.PI / 2,
893884
scale: 1.05,
@@ -900,12 +891,12 @@ export default class AsteroidsGameScene extends Scene {
900891

901892
drawObjectVectorAsset(renderer, renderKey, options) {
902893
if (!this.objectVectorRuntime || !this.objectVectorAssets) {
903-
this.recordObjectVectorRenderFailure(renderKey, options.assetId || options.objectId || options.runtimeRole, "validated Object Vector runtime assets are not loaded");
894+
this.recordObjectVectorRenderFailure(renderKey, options.assetId || options.objectId, "validated Object Vector runtime assets are not loaded");
904895
return false;
905896
}
906897
const result = this.objectVectorRuntime.renderObject(renderer, this.objectVectorAssets, options);
907898
if (!result.ok) {
908-
this.recordObjectVectorRenderFailure(renderKey, options.assetId || options.objectId || options.runtimeRole, "runtime render returned failed result");
899+
this.recordObjectVectorRenderFailure(renderKey, options.assetId || options.objectId, "runtime render returned failed result");
909900
return false;
910901
}
911902
this.objectVectorRenderCounts[renderKey] = (this.objectVectorRenderCounts[renderKey] || 0) + 1;

games/Asteroids/game/AsteroidsWorld.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { distance } from '../../../src/shared/utils/mathUtils.js';
1313
import { randomRange } from '../utils/math.js';
1414
import { sanitizeFiniteNumber, sanitizePositiveNumber } from '../../../src/shared/math/numberNormalization.js';
1515
import {
16+
ASTEROIDS_OBJECT_GEOMETRY_IDS,
1617
requireAsteroidsObjectGeometryPoints,
1718
} from './asteroidsObjectGeometryManifest.js';
1819

@@ -149,11 +150,11 @@ export default class AsteroidsWorld {
149150
if (!this.objectGeometry?.objectsById) {
150151
throw new Error('AsteroidsWorld requires manifest-loaded Object Vector geometry for ship, UFO, asteroid, and bullet gameplay geometry.');
151152
}
152-
this.bulletCollisionPoints = requireAsteroidsObjectGeometryPoints(this.objectGeometry, 'bullet', 'bullet object geometry');
153-
this.shipCollisionPoints = requireAsteroidsObjectGeometryPoints(this.objectGeometry, 'ship', 'ship object geometry');
153+
this.bulletCollisionPoints = requireAsteroidsObjectGeometryPoints(this.objectGeometry, ASTEROIDS_OBJECT_GEOMETRY_IDS.bullet, 'bullet object geometry');
154+
this.shipCollisionPoints = requireAsteroidsObjectGeometryPoints(this.objectGeometry, ASTEROIDS_OBJECT_GEOMETRY_IDS.ship, 'ship object geometry');
154155
this.ufoCollisionPoints = {
155-
large: requireAsteroidsObjectGeometryPoints(this.objectGeometry, 'ufoLarge', 'large UFO object geometry'),
156-
small: requireAsteroidsObjectGeometryPoints(this.objectGeometry, 'ufoSmall', 'small UFO object geometry'),
156+
large: requireAsteroidsObjectGeometryPoints(this.objectGeometry, ASTEROIDS_OBJECT_GEOMETRY_IDS.ufoLarge, 'large UFO object geometry'),
157+
small: requireAsteroidsObjectGeometryPoints(this.objectGeometry, ASTEROIDS_OBJECT_GEOMETRY_IDS.ufoSmall, 'small UFO object geometry'),
157158
};
158159
this.rng = typeof rng === 'function' ? rng : Math.random;
159160
this.bounds = sanitizeBounds(bounds);

games/Asteroids/game/asteroidObjectGeometry.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
2-
ASTEROID_SIZE_RUNTIME_OBJECT_KEYS,
3-
resolveAsteroidsTaggedObject,
4-
} from './asteroidsObjectTags.js';
2+
ASTEROIDS_ASTEROID_SIZE_OBJECT_IDS,
3+
} from './asteroidsObjectGeometryManifest.js';
54

65
const ASTEROID_SIZE_LABELS = Object.freeze({
76
1: 'SML',
@@ -61,13 +60,36 @@ function extractPrimaryPolygonPoints(object) {
6160
return asArray(shape?.geometry?.points).map(cleanPoint);
6261
}
6362

63+
function logProfileFailure(logger, message, details = {}) {
64+
if (!logger) {
65+
return;
66+
}
67+
if (typeof logger.log === 'function') {
68+
logger.log('FAIL', message, details);
69+
return;
70+
}
71+
logger.error?.(message, details);
72+
}
73+
6474
function createProfilesFromObjects(objects, options = {}) {
6575
const profiles = {};
76+
const objectsById = new Map(asArray(objects).map((object) => [object?.id, object]).filter(([id]) => id));
6677

67-
Object.entries(ASTEROID_SIZE_RUNTIME_OBJECT_KEYS).forEach(([size, objectKey]) => {
68-
const object = resolveAsteroidsTaggedObject(objects, objectKey, options);
78+
Object.entries(ASTEROIDS_ASTEROID_SIZE_OBJECT_IDS).forEach(([size, objectId]) => {
79+
const object = objectsById.get(objectId);
80+
if (!object) {
81+
logProfileFailure(options.logger, `Asteroids asteroid geometry profile ${objectId} is missing from Object Vector manifest objects.`, {
82+
objectId,
83+
size,
84+
});
85+
return;
86+
}
6987
const points = centerPoints(extractPrimaryPolygonPoints(object));
7088
if (points.length < 3) {
89+
logProfileFailure(options.logger, `Asteroids asteroid geometry profile ${objectId} must contain visible polygon geometry with at least 3 points.`, {
90+
objectId,
91+
size,
92+
});
7193
return;
7294
}
7395
const sizeId = Number(size);

0 commit comments

Comments
 (0)