Update look of quick slot and action buttons
This commit is contained in:
BIN
public/assets/ui/icons/icon_backpack.png
Normal file
BIN
public/assets/ui/icons/icon_backpack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 197 B |
BIN
public/assets/ui/icons/icon_map.png
Normal file
BIN
public/assets/ui/icons/icon_map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 181 B |
BIN
public/assets/ui/icons/icon_menu.png
Normal file
BIN
public/assets/ui/icons/icon_menu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 181 B |
BIN
public/assets/ui/icons/icon_search.png
Normal file
BIN
public/assets/ui/icons/icon_search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 B |
BIN
public/assets/ui/icons/icon_stats.png
Normal file
BIN
public/assets/ui/icons/icon_stats.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 168 B |
BIN
public/assets/ui/icons/icon_wait.png
Normal file
BIN
public/assets/ui/icons/icon_wait.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 B |
@@ -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");
|
||||||
|
|
||||||
|
|||||||
99
src/ui/components/ActionButtonComponent.ts
Normal file
99
src/ui/components/ActionButtonComponent.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user