Skip to content

Commit 8636c1b

Browse files
author
DavidQ
committed
Split canvas background and HTML fullscreen bezel into separate classes
BUILD_PR_LEVEL_10_17_HTML_FULLSCREEN_BEZEL_AND_CANVAS_BACKGROUND_SPLIT
1 parent 85e23f0 commit 8636c1b

16 files changed

Lines changed: 714 additions & 476 deletions

docs/dev/CODEX_COMMANDS.md

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

44
COMMAND:
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`
5+
Create `BUILD_PR_LEVEL_10_17_HTML_FULLSCREEN_BEZEL_AND_CANVAS_BACKGROUND_SPLIT` as one small testable PR.
6+
7+
Implement the correction exactly as follows:
8+
9+
1. Add a dedicated canvas class/module named `backgroundImage`
10+
- autodiscover: `games/<gameId>/assets/images/background.png`
11+
- render into the canvas
12+
- draw immediately after screen clear
13+
- draw before ALL other game/world content
14+
- keep logic independent from bezel logic
15+
16+
2. Add a dedicated HTML-overlay class/module named `fullscreenBezel`
17+
- autodiscover: `games/<gameId>/assets/images/bezel.png`
18+
- render at the HTML/container level above the canvas
19+
- only show when fullscreen is active
20+
- hide when not fullscreen
21+
- no canvas drawing for bezel
22+
- no Parallax coupling
23+
24+
3. Remove any incorrect coupling between background and bezel behavior
25+
- separate classes
26+
- separate render paths
27+
- separate responsibilities
28+
29+
4. Validation/tests
30+
- cover background draw order: after clear, before world render
31+
- cover bezel fullscreen-only visibility
32+
- cover bezel attachment to HTML/container layer rather than canvas
33+
- verify Asteroids uses:
34+
- `games/Asteroids/assets/images/background.png` when present
35+
- `games/Asteroids/assets/images/bezel.png` in fullscreen
3936

4037
5. Final packaging step is REQUIRED
4138
- package ALL changed files into this exact repo-structured ZIP:
42-
`<project folder>/tmp/BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND.zip`
39+
`<project folder>/tmp/BUILD_PR_LEVEL_10_17_HTML_FULLSCREEN_BEZEL_AND_CANVAS_BACKGROUND_SPLIT.zip`
4340

4441
Hard rules:
4542
- no commit-only result

docs/dev/COMMIT_COMMENT.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Autodiscover and autodraw fullscreen bezel and background images
2-
BUILD_PR_LEVEL_10_16_AUTODISCOVER_FULLSCREEN_BEZEL_AND_BACKGROUND
1+
Split canvas background and HTML fullscreen bezel into separate classes
2+
BUILD_PR_LEVEL_10_17_HTML_FULLSCREEN_BEZEL_AND_CANVAS_BACKGROUND_SPLIT

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_17_AUTODISCOVER_IMAGE_RENDER_VALIDATION
1+
BUILD_PR_LEVEL_10_18_FULLSCREEN_BEZEL_VISIBILITY_VALIDATION
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
1+
- Split bezel and background into separate explicit systems
2+
- backgroundImage is canvas-rendered after clear and before all other content
3+
- fullscreenBezel is HTML-layer rendered above canvas in fullscreen only
4+
- Removed coupling between background and bezel behavior
55
- Codex ZIP output explicitly required
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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`
1+
- `backgroundImage` exists as separate class/module
2+
- `fullscreenBezel` exists as separate class/module
3+
- Background draws after clear and before world render
4+
- Bezel is HTML-level, not canvas-level
5+
- Bezel only appears in fullscreen
6+
- Missing files do not break runtime
7+
- Output ZIP created at `<project folder>/tmp/BUILD_PR_LEVEL_10_17_HTML_FULLSCREEN_BEZEL_AND_CANVAS_BACKGROUND_SPLIT.zip`
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# BUILD_PR_LEVEL_10_17_HTML_FULLSCREEN_BEZEL_AND_CANVAS_BACKGROUND_SPLIT
2+
3+
## Purpose
4+
Correct the implementation contract by separating bezel and background into two distinct systems with clear render ownership.
5+
6+
## Problem
7+
The current behavior is wrong for your intended architecture:
8+
- fullscreen bezel is not reliably visible
9+
- background and bezel were coupled too closely
10+
- bezel should render at the HTML level, not inside the game canvas
11+
- background should render inside the canvas, immediately after clear and before everything else
12+
13+
## Required Architecture
14+
15+
### 1. `backgroundImage`
16+
Create a dedicated class/module named:
17+
- `backgroundImage`
18+
19+
Responsibilities:
20+
- discover optional `games/<gameId>/assets/images/background.png`
21+
- load and draw the image into the canvas
22+
- render only after the canvas is cleared
23+
- render before world, gameplay, sprites, HUD, and any other canvas content
24+
- remain independent from fullscreen bezel logic
25+
26+
Canvas render order:
27+
1. clear canvas
28+
2. draw `backgroundImage` if present
29+
3. draw all normal game/world content
30+
4. draw existing HUD/canvas overlays as currently defined
31+
32+
### 2. `fullscreenBezel`
33+
Create a dedicated class/module named:
34+
- `fullscreenBezel`
35+
36+
Responsibilities:
37+
- discover optional `games/<gameId>/assets/images/bezel.png`
38+
- render as an HTML-layer element above the canvas
39+
- only activate while fullscreen is active
40+
- visually cover part of the canvas as a bezel/overlay
41+
- do not draw through canvas render pipeline
42+
- do not depend on Parallax
43+
44+
HTML-level behavior:
45+
- attach bezel element to the game container or fullscreen host element
46+
- size/position bezel to the fullscreen viewport/container
47+
- hide bezel when not fullscreen
48+
- no per-game manual code required
49+
50+
## Scope
51+
- Split the current autodiscovery/render path into two explicit classes
52+
- Rename responsibilities clearly:
53+
- `backgroundImage`
54+
- `fullscreenBezel`
55+
- Keep zero-config asset discovery by filename convention:
56+
- `assets/images/background.png`
57+
- `assets/images/bezel.png`
58+
59+
## Testable Outcome
60+
- `background.png` draws to canvas after clear and before anything else
61+
- `bezel.png` appears as HTML overlay in fullscreen
62+
- bezel and background have separate classes and separate code paths
63+
- games without either file continue without error
64+
- PR returns repo-structured ZIP at:
65+
`<project folder>/tmp/BUILD_PR_LEVEL_10_17_HTML_FULLSCREEN_BEZEL_AND_CANVAS_BACKGROUND_SPLIT.zip`
66+
67+
## Non-Goals
68+
- No parallax redesign
69+
- No generalized DOM overlay framework beyond bezel needs
70+
- No user-authored registration code
5.75 KB
Loading

src/engine/core/Engine.js

Lines changed: 17 additions & 5 deletions
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 { AutoDiscoveredGameImageLayers, FullscreenService } from '../runtime/index.js';
12+
import { backgroundImage, fullscreenBezel, 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,7 +26,8 @@ export default class Engine {
2626
frameClock = null,
2727
fixedTicker = null,
2828
fullscreen = null,
29-
autoImageLayers = null,
29+
backgroundImageLayer = null,
30+
fullscreenBezelLayer = null,
3031
audio = null,
3132
logger = null,
3233
} = {}) {
@@ -52,7 +53,11 @@ export default class Engine {
5253
documentRef: globalThis.document ?? null,
5354
target: canvas,
5455
});
55-
this.autoImageLayers = autoImageLayers || new AutoDiscoveredGameImageLayers({
56+
this.backgroundImageLayer = backgroundImageLayer || new backgroundImage({
57+
documentRef: globalThis.document ?? null
58+
});
59+
this.fullscreenBezelLayer = fullscreenBezelLayer || new fullscreenBezel({
60+
canvas,
5661
documentRef: globalThis.document ?? null
5762
});
5863
this.audio = audio || new AudioService();
@@ -102,6 +107,9 @@ export default class Engine {
102107
if (this.fullscreen && typeof this.fullscreen.attach === 'function') {
103108
this.fullscreen.attach(this.canvas);
104109
}
110+
if (this.fullscreenBezelLayer && typeof this.fullscreenBezelLayer.attach === 'function') {
111+
this.fullscreenBezelLayer.attach();
112+
}
105113

106114
this.frameClock.reset();
107115
this.fixedTicker.reset();
@@ -123,6 +131,9 @@ export default class Engine {
123131
if (this.fullscreen && typeof this.fullscreen.detach === 'function') {
124132
this.fullscreen.detach();
125133
}
134+
if (this.fullscreenBezelLayer && typeof this.fullscreenBezelLayer.detach === 'function') {
135+
this.fullscreenBezelLayer.detach();
136+
}
126137
}
127138

128139
tick(now) {
@@ -149,12 +160,13 @@ export default class Engine {
149160
updateDurationMs = performance.now() - updateStart;
150161

151162
const renderStart = performance.now();
152-
this.autoImageLayers?.renderBackground?.(this.renderer);
163+
this.renderer.clear();
164+
this.backgroundImageLayer?.render?.(this.renderer);
153165
if (this.scene && typeof this.scene.render === 'function') {
154166
this.scene.render(this.renderer, this);
155167
}
156168
const fullscreenActive = this.fullscreen?.getState?.().active === true;
157-
this.autoImageLayers?.renderBezel?.(this.renderer, { fullscreenActive });
169+
this.fullscreenBezelLayer?.sync?.({ fullscreenActive });
158170
renderDurationMs = performance.now() - renderStart;
159171

160172
this.metrics.recordFrame({

0 commit comments

Comments
 (0)