| name | dev-phaser-ui-creation |
| description | UI containers, text objects, buttons, and DOM element integration |
Phaser UI Creation
"Build responsive game UI with Phaser's UI components."
Before/After: HTML DOM UI vs Phaser UI System
❌ Before: Pure HTML DOM UI
class GameUI {
private container: HTMLElement;
private scoreElement: HTMLElement;
private healthFillElement: HTMLElement;
private buttons: HTMLElement[] = [];
constructor() {
this.container = document.createElement('div');
this.container.id = 'game-ui';
this.container.style.position = 'absolute';
this.container.style.top = '0';
this.container.style.left = '0';
this.container.style.width = '100%';
this.container.style.pointerEvents = 'none';
document.body.appendChild(this.container);
this.scoreElement = document.createElement('div');
this.scoreElement.className = 'score-display';
this.scoreElement.textContent = 'Score: 0';
this.container.appendChild(this.scoreElement);
const healthBar = document.createElement('div');
healthBar.className = 'health-bar';
healthBar.innerHTML = `
<div class="health-bg">
<div class="health-fill"></div>
</div>
`;
this.container.appendChild(healthBar);
this.healthFillElement = healthBar.querySelector('.health-fill')!;
const style = document.createElement('style');
style.textContent = `
.score-display {
position: absolute;
top: 16px;
left: 16px;
font-size: 32px;
font-family: Arial;
color: white;
text-shadow: 2px 2px 0 black;
}
.health-bar {
position: absolute;
top: 60px;
left: 16px;
width: 200px;
height: 20px;
}
.health-bg {
width: 100%;
height: 100%;
background: #333;
border: 2px solid white;
}
.health-fill {
width: 100%;
height: 100%;
background: #0f0;
transition: width 0.2s;
}
`;
document.head.appendChild(style);
const startButton = document.createElement('button');
startButton.textContent = 'Start Game';
startButton.style.pointerEvents = 'auto';
startButton.onclick = () => this.onStartGame();
this.container.appendChild(startButton);
this.buttons.push(startButton);
}
updateScore(score: number) {
this.scoreElement.textContent = `Score: ${score}`;
}
updateHealth(current: number, max: number) {
const percent = (current / max) * 100;
this.healthFillElement.style.width = `${percent}%`;
}
destroy() {
this.container.remove();
}
}
✅ After: Phaser UI System
export class UIScene extends Phaser.Scene {
private scoreText!: Phaser.GameObjects.Text;
private healthBar!: Phaser.GameObjects.Graphics;
create() {
this.scoreText = this.add.text(16, 16, 'Score: 0', {
fontSize: '32px',
fontFamily: 'Arial',
color: '#ffffff',
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: 4
});
this.healthBar = this.add.graphics();
this.updateHealth(100, 100);
const startBtn = this.add.(, , , {
: ,
: ,
: ,
: { : , : }
});
startBtn.();
startBtn.({ : });
startBtn.(, {
..({
: startBtn,
: ,
:
});
startBtn.({ : });
});
startBtn.(, {
..({
: startBtn,
: ,
:
});
startBtn.({ : });
});
startBtn.(, {
..();
});
...([., ., startBtn]);
}
() {
..();
..();
..(, , , );
percent = current / max;
color = percent > ? :
percent > ? : ;
..(color);
..(, , * percent, );
..(, );
..(, , , );
}
() {
..();
..({
: .,
: ,
: ,
:
});
}
}
When to Use This Skill
Use when:
- Creating game HUDs and menus
- Building interactive buttons
- Displaying score and health
- Implementing dialog boxes
- Integrating HTML/CSS UI overlays
Quick Start
create() {
const score = this.add.text(16, 16, 'Score: 0', {
fontSize: '32px',
color: '#fff'
});
const button = this.add.text(400, 300, 'Start', {
backgroundColor: '#00f',
padding: { x: 10, y: 5 }
});
button.setOrigin(0.5);
button.setInteractive();
button.on('pointerdown', () => this.startGame());
}
Decision Framework
| Need | Use |
|---|
| Simple text | add.text() |
| Styling text | Text style config |
| Interactive UI | setInteractive() |
| Complex layouts | Container or HTML overlay |
| Responsive UI | Scale manager + resize events |
Progressive Guide
Level 1: Text and Basic UI
export class UIScene extends Phaser.Scene {
private scoreText!: Phaser.GameObjects.Text;
private healthBar!: Phaser.GameObjects.Graphics;
private health = 100;
private maxHealth = 100;
create() {
this.scoreText = this.add.text(16, 16, "Score: 0", {
fontSize: "32px",
fontFamily: "Arial",
color: "#ffffff",
fontStyle: "bold",
});
this.healthBar = this.add.graphics();
this.updateHealthBar();
const timerText = this.add.text(this.scale.width - , , , {
: ,
: ,
});
timerText.(, );
}
() {
..();
}
() {
..();
..();
..(, , , );
healthPercent = . / .;
color =
healthPercent >
?
: healthPercent >
?
: ;
..(color);
..(, , * healthPercent, );
..(, );
..(, , , );
}
}
Level 2: Interactive Buttons
export class MenuScene extends Phaser.Scene {
create() {
const buttonStyle = {
backgroundColor: "#4444ff",
color: "#ffffff",
fontSize: "24px",
padding: { x: 20, y: 10 },
borderRadius: 5,
};
const hoverStyle = {
backgroundColor: "#6666ff",
color: "#ffff00",
};
const startBtn = this.createButton(400, 200, "Start Game", buttonStyle);
startBtn.on("pointerdown", () => {
this.scene.start("GameScene");
});
const optionsBtn = this.createButton(400, 280, "Options", buttonStyle);
optionsBtn.on("pointerdown", () => {
this..();
});
quitBtn = .(, , , buttonStyle);
quitBtn.(, {
..();
});
}
(
: ,
: ,
: ,
: ,
): .. {
button = ..(x, y, text, style);
button.();
button.({ : });
button.(, {
button.({ : , : });
button.();
});
button.(, {
button.({ : , : });
button.();
});
button.(, {
button.();
});
button.(, {
button.();
});
button;
}
}
Level 3: UI Containers
export class UIScene extends Phaser.Scene {
create() {
const hudContainer = this.add.container(0, 0);
const healthBg = this.add.rectangle(20, 20, 200, 20, 0x333333);
const healthFill = this.add.rectangle(20, 20, 180, 16, 0x00ff00);
healthFill.setOrigin(0, 0.5);
const healthText = this.add.text(120, 20, "100/100", {
fontSize: "14px",
color: "#fff",
});
healthText.setOrigin(0.5);
const scoreLabel = this.add.(, , , {
: ,
: ,
});
scoreValue = ..(, , , {
: ,
: ,
});
hudContainer.([
healthBg,
healthFill,
healthText,
scoreLabel,
scoreValue,
]);
hudContainer.();
}
}
Level 4: Dialog Box
export class DialogScene extends Phaser.Scene {
private dialogBox!: Phaser.GameObjects.Container;
private dialogText!: Phaser.GameObjects.Text;
private continueText!: Phaser.GameObjects.Text;
private isTyping = false;
private typingSpeed = 30;
create() {
const overlay = this.add.rectangle(
this.scale.width / 2,
this.scale.height / 2,
this.scale.width,
this.scale.height,
0x000000,
0.5,
);
const dialogBg = this.add.rectangle(
this.scale. / ,
.. - ,
.. - ,
,
,
);
dialogBg.(, );
. = ..(
.. / ,
.. - ,
,
{
: ,
: ,
: ,
: { : .. - },
},
);
..(, );
. = ..(
.. / ,
.. - ,
,
{
: ,
: ,
},
);
..();
..();
. = ..(, );
..([dialogBg, ., .]);
..();
spaceKey = ..!.();
spaceKey.(, .());
}
() {
..();
..();
. = ;
index = ;
timer = ..({
: .,
: text. - ,
: {
index++;
..(text.(, index));
(index >= text.) {
. = ;
..();
timer.();
}
},
});
}
() {
(.) {
..();
. = ;
..();
} {
..();
}
}
}
Level 5: HTML DOM Integration
export class GameScene extends Phaser.Scene {
private domElement!: HTMLElement;
create() {
this.domElement = this.add
.dom(this.scale.width / 2, this.scale.height / 2)
.createElement("div");
this.domElement.setHTML(`
<div class="ui-overlay">
<h1>Game Paused</h1>
<button id="resume-btn">Resume</button>
<button id="quit-btn">Quit</button>
</div>
`);
this.domElement.setStyle(`
.ui-overlay {
background: rgba(0, 0, 0, 0.8);
padding: 40px;
border-radius: 10px;
text-align: center;
color: white;
font-family: Arial, sans-serif;
}
h1 { margin: 0 0 20px 0; }
button {
padding: 10px 20px;
margin: 5px;
font-size: 16px;
cursor: pointer;
background: #4444ff;
color: white;
border: none;
border-radius: 5px;
}
button:hover {
background: #6666ff;
}
`);
this.domElement.setVisible(false);
..().(, {
.();
});
..().(, {
.();
});
}
() {
..();
..();
}
() {
..();
..();
}
() {
..();
}
}
Anti-Patterns
❌ DON'T:
- Create UI elements in update()
- Forget to set scrollFactor(0) for HUD
- Use hardcoded positions for responsive UI
- Ignore touch sizes on mobile
- Create too many DOM elements
- Mix coordinate systems arbitrarily
✅ DO:
- Create UI once in create()
- Fix HUD with scrollFactor(0)
- Use scale manager for positioning
- Make buttons large enough for touch
- Minimize DOM usage
- Keep UI coordinates consistent
Code Patterns
Responsive UI Positioning
create() {
const scoreText = this.add.text(
this.scale.width * 0.02,
this.scale.height * 0.02,
'Score: 0'
);
this.scale.on('resize', (gameSize: Phaser.Structs.Size) => {
scoreText.setPosition(
gameSize.width * 0.02,
gameSize.height * 0.02
);
});
}
Text Styling Presets
const TEXT_STYLES = {
HEADER: {
fontSize: "48px",
fontFamily: "Arial",
color: "#ffffff",
fontStyle: "bold",
stroke: "#000000",
strokeThickness: 4,
},
BODY: {
fontSize: "18px",
fontFamily: "Arial",
color: "#cccccc",
align: "center",
},
BUTTON: {
fontSize: "24px",
fontFamily: "Arial",
color: "#ffffff",
backgroundColor: "#4444ff",
padding: { x: 20, y: 10 },
},
};
Custom Button Class
class Button extends Phaser.GameObjects.Container {
private background: Phaser.GameObjects.Rectangle;
private text: Phaser.GameObjects.Text;
constructor(
scene: Phaser.Scene,
x: number,
y: number,
width: number,
height: number,
text: string,
onClick: () => void,
) {
super(scene, x, y);
scene.add.existing(this);
this.background = scene.add.rectangle(0, 0, width, height, 0x4444ff);
this.background.setStrokeStyle(2, 0x6666ff);
this.background.setInteractive({ useHandCursor: });
. = scene..(, , text, {
: ,
: ,
});
..();
.([., .]);
..(, .());
..(, .());
..(, .());
..(, {
.();
();
});
}
() {
..();
}
() {
..();
}
() {
.();
}
() {
.();
}
}
UI Text Style Properties
| Property | Type | Description |
|---|
fontFamily | string | Font name |
fontSize | string | Size with unit (e.g., '24px') |
color | string | Text color (hex) |
fontStyle | string | 'bold', 'italic', etc. |
align | string | 'left', 'center', 'right' |
backgroundColor | string | Background color |
padding | object | { x, y } padding |
lineSpacing | number | Space between lines |
stroke | string | Outline color |
strokeThickness | number | Outline width |
shadow | object | Shadow config |
wordWrap | object | { width } for wrapping |
Checklist
Reference