Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 18 additions & 26 deletions examples/application-scenes/hud-overlay-scene.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Auto-generated from hud-overlay-scene.ts — edit the .ts source, not this file.
import { Application, Color, Graphics, Scene, Text } from '@codexo/exojs';
import { Application, Color, Graphics, Label, ProgressBar, Scene } from '@codexo/exojs';
const app = new Application({
canvas: {
width: 1280,
Expand All @@ -8,18 +8,31 @@ const app = new Application({
sizingMode: 'fit',
},
clearColor: Color.black,
loader: {
basePath: 'assets/',
},
});
/**
* A screen-fixed HUD on `scene.ui` sits above the world automatically — no
* separate overlay scene or stack. The world (a spinning arc) is drawn from
* `scene.root`; the HUD (a label + a live health bar) lives on `scene.ui` and
* is auto-rendered on top.
*/
class GameScene extends Scene {
angle = 0;
time = 0;
ring;
health;
init() {
this.ring = new Graphics();
const title = new Label('HUD Overlay', { fontSize: 22 });
title.anchorIn(this.ui, 'top-left', 18, 14);
this.ui.addChild(title);
this.health = new ProgressBar({ width: 240, height: 12, value: 1 });
this.health.anchorIn(this.ui, 'top-left', 18, 48);
this.ui.addChild(this.health);
}
update(delta) {
this.angle += delta.seconds * 90;
this.time += delta.seconds;
this.health.value = (Math.sin(this.time) + 1) / 2;
}
draw(context) {
const { width, height } = this.app.canvas;
Expand All @@ -31,25 +44,4 @@ class GameScene extends Scene {
context.render(this.ring);
}
}
class HudScene extends Scene {
bar;
text;
init() {
this.bar = new Graphics();
this.text = new Text('HUD Overlay', { fillColor: Color.white, fontSize: 22 });
this.text.setPosition(18, 14);
}
draw(context) {
const { width } = this.app.canvas;
this.bar.clear();
this.bar.fillColor = new Color(0, 0, 0, 0.45);
this.bar.drawRectangle(0, 0, width, 56);
this.bar.fillColor = new Color(80, 220, 120);
this.bar.drawRectangle(18, 40, 220, 8);
context.render(this.bar);
context.render(this.text);
}
}
const gameScene = new GameScene();
const hudScene = new HudScene();
void app.start(gameScene).then(() => app.scene.pushScene(hudScene, { mode: 'overlay' }));
void app.start(new GameScene());
51 changes: 20 additions & 31 deletions examples/application-scenes/hud-overlay-scene.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Application, Color, Graphics, Scene, Text } from '@codexo/exojs';
import { Application, Color, Graphics, Label, ProgressBar, Scene } from '@codexo/exojs';

const app = new Application({
canvas: {
Expand All @@ -8,21 +8,36 @@ const app = new Application({
sizingMode: 'fit',
},
clearColor: Color.black,
loader: {
basePath: 'assets/',
},
});

/**
* A screen-fixed HUD on `scene.ui` sits above the world automatically — no
* separate overlay scene or stack. The world (a spinning arc) is drawn from
* `scene.root`; the HUD (a label + a live health bar) lives on `scene.ui` and
* is auto-rendered on top.
*/
class GameScene extends Scene {
private angle = 0;
private time = 0;
private ring!: Graphics;
private health!: ProgressBar;

override init(): void {
this.ring = new Graphics();

const title = new Label('HUD Overlay', { fontSize: 22 });
title.anchorIn(this.ui, 'top-left', 18, 14);
this.ui.addChild(title);

this.health = new ProgressBar({ width: 240, height: 12, value: 1 });
this.health.anchorIn(this.ui, 'top-left', 18, 48);
this.ui.addChild(this.health);
}

override update(delta): void {
this.angle += delta.seconds * 90;
this.time += delta.seconds;
this.health.value = (Math.sin(this.time) + 1) / 2;
}

override draw(context): void {
Expand All @@ -37,30 +52,4 @@ class GameScene extends Scene {
}
}

class HudScene extends Scene {
private bar!: Graphics;
private text!: Text;

override init(): void {
this.bar = new Graphics();
this.text = new Text('HUD Overlay', { fillColor: Color.white, fontSize: 22 });
this.text.setPosition(18, 14);
}

override draw(context): void {
const { width } = this.app.canvas;

this.bar.clear();
this.bar.fillColor = new Color(0, 0, 0, 0.45);
this.bar.drawRectangle(0, 0, width, 56);
this.bar.fillColor = new Color(80, 220, 120);
this.bar.drawRectangle(18, 40, 220, 8);
context.render(this.bar);
context.render(this.text);
}
}

const gameScene = new GameScene();
const hudScene = new HudScene();

void app.start(gameScene).then(() => app.scene.pushScene(hudScene, { mode: 'overlay' }));
void app.start(new GameScene());
6 changes: 2 additions & 4 deletions examples/application-scenes/pause-and-resume.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const app = new Application({
},
});
class PauseResumeScene extends Scene {
paused = false;
sprite;
label;
async load(loader) {
Expand All @@ -27,14 +26,13 @@ class PauseResumeScene extends Scene {
this.label.setAnchor(0.5, 0);
this.label.setPosition(width / 2, 16);
this.inputs.onTrigger(Keyboard.Space, () => {
// scene.paused skips update() + systems each frame; drawing continues.
this.paused = !this.paused;
this.label.text = this.paused ? 'Paused (draw running)' : 'Running';
});
}
update(delta) {
if (this.paused) {
return;
}
// Not called while paused — the SceneManager skips a paused scene's update().
this.sprite.rotate(delta.seconds * 180);
}
draw(context) {
Expand Down
7 changes: 2 additions & 5 deletions examples/application-scenes/pause-and-resume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const app = new Application({
});

class PauseResumeScene extends Scene {
private paused = false;
private sprite!: Sprite;
private label!: Text;

Expand All @@ -33,16 +32,14 @@ class PauseResumeScene extends Scene {
this.label.setPosition(width / 2, 16);

this.inputs.onTrigger(Keyboard.Space, () => {
// scene.paused skips update() + systems each frame; drawing continues.
this.paused = !this.paused;
this.label.text = this.paused ? 'Paused (draw running)' : 'Running';
});
}

override update(delta): void {
if (this.paused) {
return;
}

// Not called while paused — the SceneManager skips a paused scene's update().
this.sprite.rotate(delta.seconds * 180);
}

Expand Down
2 changes: 1 addition & 1 deletion examples/examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"path": "application-scenes/hud-overlay-scene.js",
"language": "typescript",
"title": "Hud Overlay Scene",
"description": "Layer a fixed-position HUD Scene on top of a world Scene using separate cameras and render priorities.",
"description": "Build a screen-fixed HUD on scene.ui — a Label and a live ProgressBar auto-rendered above the world, with no separate scene or stack.",
"backend": "core",
"tags": [
"hud",
Expand Down
68 changes: 36 additions & 32 deletions examples/showcase/pause-blur.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Auto-generated from pause-blur.ts — edit the .ts source, not this file.
import { Application, BlurFilter, Color, Keyboard, Scene, Sprite, Text, Texture } from '@codexo/exojs';
import { Application, BlurFilter, Color, Keyboard, Label, Panel, Scene, Sprite, Texture } from '@codexo/exojs';
import { mountControls } from '@examples/runtime';
const app = new Application({
canvas: {
Expand All @@ -15,9 +15,18 @@ const app = new Application({
});
const PAUSE_BLUR_RADIUS = 6;
const PAUSE_FADE_SECONDS = 0.35;
/**
* Pause without a scene stack: a pause overlay lives on `scene.ui` (always
* above the world) and is toggled together with `scene.paused`, which skips the
* scene's `update` + systems while it keeps drawing. The blur tween runs on the
* app-level TweenManager, so it still animates while the scene is frozen.
*/
class GameScene extends Scene {
sprite;
time = 0;
blur = new BlurFilter({ radius: 0, quality: 2 });
pausePanel;
pauseLabel;
hud;
async load(loader) {
await loader.load(Texture, { ship: 'image/ship-a.png' });
Expand All @@ -26,52 +35,47 @@ class GameScene extends Scene {
const { width, height } = this.app.canvas;
this.sprite = new Sprite(loader.get(Texture, 'ship')).setAnchor(0.5).setScale(2).setPosition(width / 2, height / 2);
this.addChild(this.sprite);
// Pause overlay on the UI layer, hidden until paused.
this.pausePanel = new Panel({ width: 420, height: 140, cornerRadius: 18, color: new Color(0, 0, 0, 0.6) });
this.pausePanel.anchorIn(this.ui, 'center');
this.pausePanel.visible = false;
this.ui.addChild(this.pausePanel);
this.pauseLabel = new Label('PAUSED', { fontSize: 56, fontWeight: 'bold' });
this.pauseLabel.anchorIn(this.ui, 'center');
this.pauseLabel.visible = false;
this.ui.addChild(this.pauseLabel);
this.hud = mountControls({
title: 'Pause Blur',
controls: [{ keys: 'Esc', action: 'pause / resume' }],
hint: 'Press Esc to pause — the scene blurs up behind the menu.',
});
this.inputs.onTrigger(Keyboard.Escape, async () => {
if (pauseScene.app !== null)
return;
await this.app.scene.pushScene(pauseScene, { mode: 'overlay' });
});
this.inputs.onTrigger(Keyboard.Escape, () => this.togglePause());
}
update(delta) {
// Not called while paused — the SceneManager skips update() + systems.
this.time += delta.seconds;
this.sprite.setRotation(this.time * 80);
}
draw(context) {
context.backend.clear(new Color(20, 24, 34));
context.render(this.root);
}
}
class PauseScene extends Scene {
blur;
text;
init() {
const { width, height } = this.app.canvas;
// Start fully sharp and tween the radius up so the blur genuinely fades
// in rather than snapping on. The global TweenManager keeps ticking while
// this overlay scene is on the stack.
this.blur = new BlurFilter({ radius: 0, quality: 2 });
gameScene.root.filters = [this.blur];
this.tweens.create(this.blur).to({ radius: PAUSE_BLUR_RADIUS }, PAUSE_FADE_SECONDS).start();
this.text = new Text('PAUSED', { fillColor: Color.white, fontSize: 64, fontWeight: 'bold', align: 'center' });
this.text.setAnchor(0.5, 0.5);
this.text.setPosition(width / 2, height / 2);
this.inputs.onTrigger(Keyboard.Escape, async () => {
await this.app.scene.popScene();
});
}
draw(context) {
context.render(this.text);
}
destroy() {
gameScene.root.clearFilters();
this.root.clearFilters();
super.destroy();
}
togglePause() {
this.paused = !this.paused;
this.pausePanel.visible = this.paused;
this.pauseLabel.visible = this.paused;
if (this.paused) {
this.blur.radius = 0;
this.root.filters = [this.blur];
this.tweens.create(this.blur).to({ radius: PAUSE_BLUR_RADIUS }, PAUSE_FADE_SECONDS).start();
}
else {
this.root.clearFilters();
}
}
}
const gameScene = new GameScene();
const pauseScene = new PauseScene();
void app.start(gameScene);
void app.start(new GameScene());
Loading
Loading