Skip to content

Commit 85e23f0

Browse files
author
DavidQ
committed
Autodiscover and autodraw fullscreen bezel and background images
BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND
1 parent 33e2140 commit 85e23f0

12 files changed

Lines changed: 552 additions & 70 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,46 @@ MODEL: GPT-5.4
22
REASONING: high
33

44
COMMAND:
5-
Create `BUILD_PR_LEVEL_10_15_TEMPLATE_ASSET_STRUCTURE_AND_FULLSCREEN_BEZEL_FOUNDATION` as one small bundled PR with exactly these changes:
6-
7-
1. Normalize `games/_template/assets/` to match the current Asteroids asset directory structure:
8-
- audio/
9-
- fonts/
10-
- images/
11-
- palettes/
12-
- parallax/
13-
- parallax/data/.gitkeep
14-
- sprites/
15-
- sprites/data/.gitkeep
16-
- tilemaps/
17-
- tilemaps/data/.gitkeep
18-
- tilesets/
19-
- vectors/
20-
- vectors/data/.gitkeep
21-
- preserve `.gitkeep` where needed
22-
- do NOT copy Asteroids-specific content into `_template`
23-
24-
2. Add a minimal fullscreen bezel foundation that is clearly NOT Parallax:
25-
- use Asteroids `games/Asteroids/assets/images/bezel.png` as the first candidate asset
26-
- bezel only applies in fullscreen mode
27-
- bezel is screen-space, not world-space
28-
- implement as a dedicated explicit surface/class/module name such as `FullscreenBezelOverlay` or equivalent clear name
29-
- support draw mode contract with:
30-
- default `overlay` (draw last)
31-
- optional `underlay` support point for future use
32-
- keep scope minimal and engine-safe
33-
34-
3. Add/update focused tests or validation coverage for:
35-
- `_template` directory scaffold exists
36-
- bezel path/contract resolves correctly
37-
- fullscreen-only gating is covered
38-
- no Parallax coupling is introduced
39-
40-
4. Update roadmap status only by adding the bezel foundation item in the appropriate high-level roadmap area without rewriting unrelated roadmap text.
41-
42-
5. Final packaging step is REQUIRED:
5+
Create `BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND` as one small testable PR.
6+
7+
Implement convention-based autodiscovery and autodraw for these optional files:
8+
9+
- `games/<gameId>/assets/images/background.png`
10+
- `games/<gameId>/assets/images/bezel.png`
11+
12+
Requirements:
13+
1. `background.png`
14+
- if present, draw automatically
15+
- draw before game/world content
16+
- no user-added game code required
17+
18+
2. `bezel.png`
19+
- if present, draw automatically
20+
- only draw in fullscreen mode
21+
- draw in screen space
22+
- draw after gameplay/HUD by default
23+
- no user-added game code required
24+
25+
3. Architecture rules
26+
- do NOT use Parallax for bezel
27+
- keep implementation minimal and shared where appropriate
28+
- do not require explicit per-game registration
29+
- do not break games that do not have these files
30+
31+
4. Validation
32+
- add/update focused tests or validation coverage for:
33+
- autodetect present file
34+
- no-op when file missing
35+
- fullscreen gating for bezel
36+
- background draw-before-world behavior
37+
- verify Asteroids bezel candidate works from:
38+
`games/Asteroids/assets/images/bezel.png`
39+
40+
5. Final packaging step is REQUIRED
4341
- package ALL changed files into this exact repo-structured ZIP:
44-
`<project folder>/tmp/BUILD_PR_LEVEL_10_15_TEMPLATE_ASSET_STRUCTURE_AND_FULLSCREEN_BEZEL_FOUNDATION.zip`
42+
`<project folder>/tmp/BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND.zip`
4543

4644
Hard rules:
4745
- no commit-only result
4846
- no missing ZIP
49-
- no repo-wide unrelated edits
50-
- keep changes surgical
47+
- no unrelated repo changes

docs/dev/COMMIT_COMMENT.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Normalize _template asset structure and add fullscreen bezel foundation
2-
BUILD_PR_LEVEL_10_15_TEMPLATE_ASSET_STRUCTURE_AND_FULLSCREEN_BEZEL_FOUNDATION
1+
Autodiscover and autodraw fullscreen bezel and background images
2+
BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND

docs/dev/NEXT_COMMAND.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
BUILD_PR_LEVEL_10_16_ASTEROIDS_FULLSCREEN_BEZEL_IMPLEMENTATION_CANDIDATE
1+
BUILD_PR_LEVEL_10_17_AUTODISCOVER_IMAGE_RENDER_VALIDATION
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
- Bundled _template asset scaffold normalization with fullscreen bezel foundation
2-
- Declared bezel as dedicated screen-space overlay, not Parallax
3-
- Set Asteroids bezel.png as first candidate asset
4-
- Enforced required Codex ZIP packaging output
1+
- Added zero-config autodiscovery direction for background.png and bezel.png
2+
- Background draws automatically before world render
3+
- Bezel draws automatically in fullscreen after gameplay/HUD
4+
- No per-game user code required
5+
- Codex ZIP output explicitly required
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
- `_template` asset folders match Asteroids structure
2-
- `.gitkeep` files preserved where needed
3-
- No Asteroids-specific content copied into `_template`
4-
- Bezel is fullscreen-only
5-
- Bezel is screen-space, not camera/parallax space
6-
- Default draw mode is overlay, with explicit future underlay hook
7-
- Focused validation/tests added
8-
- Output ZIP created at `<project folder>/tmp/BUILD_PR_LEVEL_10_15_TEMPLATE_ASSET_STRUCTURE_AND_FULLSCREEN_BEZEL_FOUNDATION.zip`
1+
- `background.png` autodetects and draws without manual wiring
2+
- `bezel.png` autodetects and draws without manual wiring
3+
- Bezel only renders in fullscreen
4+
- Missing files cause no runtime failure
5+
- Asteroids bezel candidate is covered
6+
- Output ZIP created at `<project folder>/tmp/BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND.zip`
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND
2+
3+
## Purpose
4+
Implement zero-config asset autodiscovery for fullscreen bezel and optional background image rendering.
5+
6+
## Problem
7+
The bezel is not showing, and the current direction still relies too much on explicit setup.
8+
If a game already has:
9+
- `assets/images/bezel.png`
10+
- or `assets/images/background.png`
11+
12+
the runtime should detect and draw them automatically with no game-specific user code.
13+
14+
## Scope
15+
16+
### Automatic discovery rules
17+
For each game, autodetect these optional image assets under:
18+
`games/<gameId>/assets/images/`
19+
20+
Recognized filenames:
21+
- `bezel.png`
22+
- `background.png`
23+
24+
No additional game wiring should be required.
25+
26+
### Render behavior
27+
28+
#### Background
29+
If `background.png` exists:
30+
- draw automatically
31+
- treat as screen-sized image content
32+
- draw before world/gameplay
33+
- keep behavior minimal and stable
34+
- no camera/parallax dependency unless separately designed later
35+
36+
#### Bezel
37+
If `bezel.png` exists:
38+
- draw automatically
39+
- bezel should only render in fullscreen mode
40+
- bezel is screen-space only
41+
- draw after gameplay/HUD by default
42+
- no user-added per-game code required
43+
44+
### Render order
45+
Default order:
46+
1. optional autodiscovered `background.png`
47+
2. game/world render
48+
3. HUD/overlay as currently defined
49+
4. optional autodiscovered fullscreen `bezel.png`
50+
51+
### Architectural rules
52+
- Do not route bezel through Parallax
53+
- Do not require per-game bootstrap code to enable bezel/background autodraw
54+
- Keep detection convention-based and minimal
55+
- Keep scope engine-safe and surgical
56+
- Prefer a small shared image-discovery/render helper if needed
57+
58+
## Testable Outcome
59+
- If `games/Asteroids/assets/images/bezel.png` exists, bezel appears automatically in fullscreen
60+
- If `games/<gameId>/assets/images/background.png` exists, background appears automatically
61+
- Games without either file continue to run without error
62+
- No manual game code required to enable either behavior
63+
- PR returns repo-structured ZIP at:
64+
`<project folder>/tmp/BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND.zip`
65+
66+
## Non-Goals
67+
- No generalized layered art system
68+
- No editor changes
69+
- No parallax/background system redesign

games/Asteroids/game/AsteroidsGameScene.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import ShipDebrisSystem from '../systems/ShipDebrisSystem.js';
1313
import AsteroidsAttractAdapter from './AsteroidsAttractAdapter.js';
1414
import AsteroidsHighScoreService from '../systems/AsteroidsHighScoreService.js';
1515
import AsteroidsInitialsEntry from '../systems/AsteroidsInitialsEntry.js';
16-
import FullscreenBezelOverlay from './FullscreenBezelOverlay.js';
1716
import {
1817
ASTEROIDS_GAME_OVER_AUTO_EXIT_SECONDS, ASTEROIDS_GAME_OVER_RETURN_MODE } from "../rules/flowRules.js";
1918
import { ASTEROIDS_GAME_OVER_RETURN_STATUS } from "../rules/flowContent.js";
@@ -135,8 +134,6 @@ export default class AsteroidsGameScene extends Scene {
135134
this.beatTimer = 0;
136135
this.nextBeatId = 'beat1';
137136
this.scoreFlashTime = 0;
138-
this.isFullscreenActive = false;
139-
this.fullscreenBezelOverlay = new FullscreenBezelOverlay();
140137
this.initialsEntry = new AsteroidsInitialsEntry();
141138
this.attractAdapter = new AsteroidsAttractAdapter({ scene: this });
142139
this.currentInput = null;
@@ -495,7 +492,6 @@ export default class AsteroidsGameScene extends Scene {
495492
update(dtSeconds, engine) {
496493
this.debugFrame += 1;
497494
this.currentInput = engine.input ?? null;
498-
this.isFullscreenActive = engine?.fullscreen?.getState?.().active === true;
499495
const enterPressed = engine.input?.isDown('Enter');
500496
const onePressed = engine.input?.isDown('Digit1');
501497
const twoPressed = engine.input?.isDown('Digit2');
@@ -698,10 +694,6 @@ export default class AsteroidsGameScene extends Scene {
698694
this.world.starfield.forEach((star) => {
699695
renderer.drawRect(star.x, star.y, star.size, star.size, '#94a3b8');
700696
});
701-
this.fullscreenBezelOverlay.render(renderer, {
702-
fullscreenActive: this.isFullscreenActive,
703-
stage: 'underlay',
704-
});
705697

706698
if (this.session.mode !== 'menu') {
707699
const flashOn = this.session.isTurnIntroActive()
@@ -824,11 +816,6 @@ export default class AsteroidsGameScene extends Scene {
824816
});
825817
}
826818

827-
this.fullscreenBezelOverlay.render(renderer, {
828-
fullscreenActive: this.isFullscreenActive,
829-
stage: 'overlay',
830-
});
831-
832819
if (this.devConsoleIntegration) {
833820
this.devConsoleIntegration.render(renderer, {
834821
worldStages: ['parallax', 'entities', 'sprite-effects', 'vector-overlay'],

src/engine/core/Engine.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import RuntimeMetrics from './RuntimeMetrics.js';
99
import FrameClock from './FrameClock.js';
1010
import FixedTicker from './FixedTicker.js';
1111
import EventBus from '../events/EventBus.js';
12-
import { FullscreenService } from '../runtime/index.js';
12+
import { AutoDiscoveredGameImageLayers, FullscreenService } from '../runtime/index.js';
1313
import { AudioService } from '../audio/index.js';
1414
import { Logger } from '../logging/index.js';
1515
import { SettingsSystem } from '../release/index.js';
@@ -26,6 +26,7 @@ export default class Engine {
2626
frameClock = null,
2727
fixedTicker = null,
2828
fullscreen = null,
29+
autoImageLayers = null,
2930
audio = null,
3031
logger = null,
3132
} = {}) {
@@ -51,6 +52,9 @@ export default class Engine {
5152
documentRef: globalThis.document ?? null,
5253
target: canvas,
5354
});
55+
this.autoImageLayers = autoImageLayers || new AutoDiscoveredGameImageLayers({
56+
documentRef: globalThis.document ?? null
57+
});
5458
this.audio = audio || new AudioService();
5559
this.logger = logger || new Logger({ channel: 'engine' });
5660
this.settings = new SettingsSystem({
@@ -145,9 +149,12 @@ export default class Engine {
145149
updateDurationMs = performance.now() - updateStart;
146150

147151
const renderStart = performance.now();
152+
this.autoImageLayers?.renderBackground?.(this.renderer);
148153
if (this.scene && typeof this.scene.render === 'function') {
149154
this.scene.render(this.renderer, this);
150155
}
156+
const fullscreenActive = this.fullscreen?.getState?.().active === true;
157+
this.autoImageLayers?.renderBezel?.(this.renderer, { fullscreenActive });
151158
renderDurationMs = performance.now() - renderStart;
152159

153160
this.metrics.recordFrame({

0 commit comments

Comments
 (0)