Update look of quick slot and action buttons

This commit is contained in:
Peter Stockings
2026-01-07 18:17:05 +11:00
parent d01dd8a4fc
commit 47e15ccf5c
10 changed files with 109 additions and 101 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

View File

@@ -5,8 +5,8 @@ import { MenuComponent } from "./components/MenuComponent";
import { InventoryOverlay } from "./components/InventoryOverlay"; import { InventoryOverlay } from "./components/InventoryOverlay";
import { CharacterOverlay } from "./components/CharacterOverlay"; import { CharacterOverlay } from "./components/CharacterOverlay";
import { DeathOverlay } from "./components/DeathOverlay"; import { DeathOverlay } from "./components/DeathOverlay";
import { PersistentButtonsComponent } from "./components/PersistentButtonsComponent";
import { QuickSlotComponent } from "./components/QuickSlotComponent"; import { QuickSlotComponent } from "./components/QuickSlotComponent";
import { ActionButtonComponent } from "./components/ActionButtonComponent";
export default class GameUI extends Phaser.Scene { export default class GameUI extends Phaser.Scene {
private hud: HudComponent; private hud: HudComponent;
@@ -14,8 +14,8 @@ export default class GameUI extends Phaser.Scene {
private inventory: InventoryOverlay; private inventory: InventoryOverlay;
private character: CharacterOverlay; private character: CharacterOverlay;
private death: DeathOverlay; private death: DeathOverlay;
private persistentButtons: PersistentButtonsComponent;
private quickSlots: QuickSlotComponent; private quickSlots: QuickSlotComponent;
private actionButtons: ActionButtonComponent;
constructor() { constructor() {
super({ key: "GameUI" }); super({ key: "GameUI" });
@@ -24,8 +24,8 @@ export default class GameUI extends Phaser.Scene {
this.inventory = new InventoryOverlay(this); this.inventory = new InventoryOverlay(this);
this.character = new CharacterOverlay(this); this.character = new CharacterOverlay(this);
this.death = new DeathOverlay(this); this.death = new DeathOverlay(this);
this.persistentButtons = new PersistentButtonsComponent(this);
this.quickSlots = new QuickSlotComponent(this); this.quickSlots = new QuickSlotComponent(this);
this.actionButtons = new ActionButtonComponent(this);
} }
@@ -35,8 +35,8 @@ export default class GameUI extends Phaser.Scene {
this.inventory.create(); this.inventory.create();
this.character.create(); this.character.create();
this.death.create(); this.death.create();
this.persistentButtons.create();
this.quickSlots.create(); this.quickSlots.create();
this.actionButtons.create();
const gameScene = this.scene.get("GameScene"); const gameScene = this.scene.get("GameScene");

View File

@@ -0,0 +1,99 @@
import Phaser from "phaser";
export class ActionButtonComponent {
private scene: Phaser.Scene;
private container!: Phaser.GameObjects.Container;
private iconsLoaded = false;
private static readonly ACTIONS = [
{ icon: "icon_wait", label: "WAIT", event: "player-wait" },
{ icon: "icon_search", label: "SEARCH", event: "player-search" },
{ icon: "icon_map", label: "MAP", event: "toggle-minimap" },
{ icon: "icon_backpack", label: "BACKPACK", event: "toggle-inventory" },
{ icon: "icon_stats", label: "STATS", event: "toggle-character" },
{ icon: "icon_menu", label: "MENU", event: "toggle-menu" }
];
private static readonly BUTTON_SIZE = 40;
private static readonly BUTTON_SPACING = 4;
private static readonly LABEL_HEIGHT = 15;
private static readonly ICON_SCALE_NORMAL = 1.5;
private static readonly ICON_SCALE_HOVER = 1.7;
private static readonly COLOR_BG = 0x2a1f3d;
private static readonly COLOR_BORDER_NORMAL = 0xD4AF37;
private static readonly COLOR_BORDER_HOVER = 0xFFD700;
constructor(scene: Phaser.Scene) {
this.scene = scene;
}
preload() {
const iconPath = "/assets/ui/icons/icon_";
["wait", "search", "menu", "backpack", "stats", "map"].forEach(name => {
this.scene.load.image(`icon_${name}`, `${iconPath}${name}.png`);
});
this.scene.load.once("complete", () => { this.iconsLoaded = true; });
this.scene.load.start();
}
create() {
if (!this.iconsLoaded) {
this.preload();
this.scene.time.delayedCall(100, () => this.createButtons());
} else {
this.createButtons();
}
}
private createButtons() {
const { width, height } = this.scene.scale;
const { ACTIONS, BUTTON_SIZE, BUTTON_SPACING, LABEL_HEIGHT } = ActionButtonComponent;
const totalWidth = (BUTTON_SIZE + BUTTON_SPACING) * ACTIONS.length - BUTTON_SPACING;
this.container = this.scene.add.container(
width / 2 - totalWidth / 2,
height - BUTTON_SIZE - LABEL_HEIGHT - 10
);
this.container.setScrollFactor(0).setDepth(1500);
ACTIONS.forEach((action, i) => {
const x = i * (BUTTON_SIZE + BUTTON_SPACING);
const graphics = this.scene.add.graphics();
const icon = this.scene.add.sprite(BUTTON_SIZE / 2, BUTTON_SIZE / 2, action.icon).setScale(ActionButtonComponent.ICON_SCALE_NORMAL);
const label = this.scene.add.text(BUTTON_SIZE / 2, BUTTON_SIZE + 3, action.label, {
fontSize: "9px",
color: "#D4AF37",
fontStyle: "bold"
}).setOrigin(0.5, 0);
const button = this.scene.add.container(x, 0, [graphics, icon, label]);
this.container.add(button);
this.drawButton(graphics, false);
button.setInteractive(new Phaser.Geom.Rectangle(0, 0, BUTTON_SIZE, BUTTON_SIZE), Phaser.Geom.Rectangle.Contains);
button.on("pointerover", () => {
this.drawButton(graphics, true);
icon.setScale(ActionButtonComponent.ICON_SCALE_HOVER);
});
button.on("pointerout", () => {
this.drawButton(graphics, false);
icon.setScale(ActionButtonComponent.ICON_SCALE_NORMAL);
});
button.on("pointerdown", () => {
this.scene.scene.get("GameScene").events.emit(action.event);
});
});
}
private drawButton(graphics: Phaser.GameObjects.Graphics, hover: boolean) {
const { BUTTON_SIZE, COLOR_BG, COLOR_BORDER_NORMAL, COLOR_BORDER_HOVER } = ActionButtonComponent;
graphics.clear();
graphics.fillStyle(COLOR_BG, 0.95);
graphics.fillRect(0, 0, BUTTON_SIZE, BUTTON_SIZE);
graphics.lineStyle(2, hover ? COLOR_BORDER_HOVER : COLOR_BORDER_NORMAL, 1);
graphics.strokeRect(0, 0, BUTTON_SIZE, BUTTON_SIZE);
}
}

View File

@@ -1,95 +0,0 @@
import Phaser from "phaser";
export class PersistentButtonsComponent {
private scene: Phaser.Scene;
private container!: Phaser.GameObjects.Container;
constructor(scene: Phaser.Scene) {
this.scene = scene;
}
create() {
const { height } = this.scene.scale;
this.container = this.scene.add.container(20, height - 20);
this.container.setScrollFactor(0).setDepth(1500);
const btnStyle = {
fontSize: "14px",
color: "#ffffff",
backgroundColor: "#1a1a1a",
padding: { x: 10, y: 6 },
fontStyle: "bold"
};
const createBtn = (x: number, text: string, event: string) => {
const btn = this.scene.add.text(x, 0, text, btnStyle)
.setOrigin(0, 1)
.setInteractive({ useHandCursor: true });
btn.on("pointerover", () => btn.setBackgroundColor("#333333"));
btn.on("pointerout", () => btn.setBackgroundColor("#1a1a1a"));
btn.on("pointerdown", () => {
btn.setBackgroundColor("#444444");
const gameScene = this.scene.scene.get("GameScene");
gameScene.events.emit(event);
});
btn.on("pointerup", () => btn.setBackgroundColor("#333333"));
this.container.add(btn);
return btn;
};
createBtn(0, "MENU (ESC)", "toggle-menu");
createBtn(105, "STATS (C)", "toggle-character");
createBtn(200, "BACKPACK (I)", "toggle-inventory");
createBtn(320, "MAP (M)", "toggle-minimap");
// Right-aligned buttons
const rightContainer = this.scene.add.container(this.scene.scale.width - 20, height - 20);
rightContainer.setScrollFactor(0).setDepth(1500);
const waitBtn = this.scene.add.text(0, 0, "🕒", {
fontSize: "24px",
color: "#ffffff",
backgroundColor: "#1a1a1a",
padding: { x: 10, y: 6 },
fontStyle: "bold"
})
.setOrigin(1, 1)
.setInteractive({ useHandCursor: true });
const searchBtn = this.scene.add.text(-40, 0, "🔍", { // Offset to the left of wait button
fontSize: "24px",
color: "#ffffff",
backgroundColor: "#1a1a1a",
padding: { x: 10, y: 6 },
fontStyle: "bold"
})
.setOrigin(1, 1)
.setInteractive({ useHandCursor: true });
waitBtn.on("pointerover", () => waitBtn.setBackgroundColor("#333333"));
waitBtn.on("pointerout", () => waitBtn.setBackgroundColor("#1a1a1a"));
waitBtn.on("pointerdown", () => {
waitBtn.setBackgroundColor("#444444");
const gameScene = this.scene.scene.get("GameScene");
gameScene.events.emit("player-wait");
});
waitBtn.on("pointerup", () => waitBtn.setBackgroundColor("#333333"));
searchBtn.on("pointerover", () => searchBtn.setBackgroundColor("#333333"));
searchBtn.on("pointerout", () => searchBtn.setBackgroundColor("#1a1a1a"));
searchBtn.on("pointerdown", () => {
searchBtn.setBackgroundColor("#444444");
// Implementing search visual logic later, for now just log
console.log("Searching...");
const gameScene = this.scene.scene.get("GameScene");
gameScene.events.emit("player-search");
});
searchBtn.on("pointerup", () => searchBtn.setBackgroundColor("#333333"));
rightContainer.add(waitBtn);
rightContainer.add(searchBtn);
}
}

View File

@@ -17,9 +17,13 @@ export class QuickSlotComponent {
const slotSize = 48; const slotSize = 48;
const slotSpacing = 4; const slotSpacing = 4;
const totalWidth = (slotSize + slotSpacing) * 4 - slotSpacing; const totalWidth = (slotSize + slotSpacing) * 4 - slotSpacing;
const actionButtonHeight = 40 + 10; // Button height + spacing
// Position bottom center // Position above action buttons
this.container = this.scene.add.container(width / 2 - totalWidth / 2, height - slotSize - 20); this.container = this.scene.add.container(
width / 2 - totalWidth / 2,
height - slotSize - actionButtonHeight - 20
);
this.container.setScrollFactor(0).setDepth(1500); this.container.setScrollFactor(0).setDepth(1500);
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {