slop game oder so
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Senkrechte Menüliste aus Canvas-Texten, bedienbar mit Maus und Tastatur.
|
||||
* @module ui/MenuList
|
||||
*/
|
||||
|
||||
import { oncePerEvent } from '../systems/keyboard.js';
|
||||
import { UI } from './theme.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} MenuItem
|
||||
* @property {string} label
|
||||
* @property {() => void} onSelect
|
||||
*/
|
||||
|
||||
export class MenuList {
|
||||
/**
|
||||
* @param {Phaser.Scene} scene
|
||||
* @param {number} x Mittelpunkt
|
||||
* @param {number} y Oberkante des ersten Eintrags
|
||||
* @param {MenuItem[]} items
|
||||
* @param {{spacing?: number, fontSize?: number}} [options]
|
||||
*/
|
||||
constructor(scene, x, y, items, options = {}) {
|
||||
this.scene = scene;
|
||||
this.items = items;
|
||||
this.audio = scene.registry.get('services')?.audio ?? null;
|
||||
this.index = 0;
|
||||
this.enabled = true;
|
||||
const spacing = options.spacing ?? 58;
|
||||
const fontSize = options.fontSize ?? 30;
|
||||
|
||||
this.texts = items.map((item, i) => {
|
||||
const text = scene.add
|
||||
.text(x, y + i * spacing, item.label.toUpperCase(), {
|
||||
...UI.text,
|
||||
fontSize: `${fontSize}px`,
|
||||
color: UI.colors.cream,
|
||||
})
|
||||
.setOrigin(0.5)
|
||||
.setInteractive({ useHandCursor: true });
|
||||
text.on('pointerover', () => {
|
||||
if (this.enabled) this.focus(i);
|
||||
});
|
||||
text.on('pointerup', () => {
|
||||
if (this.enabled) this.select(i);
|
||||
});
|
||||
return text;
|
||||
});
|
||||
|
||||
this.keys = scene.input.keyboard.addKeys('UP,DOWN,W,S,ENTER,SPACE');
|
||||
this.onKeyDown = oncePerEvent((event) => {
|
||||
if (!this.enabled || event.repeat) return;
|
||||
if (event.code === 'ArrowUp' || event.code === 'KeyW') this.focus(this.index - 1);
|
||||
else if (event.code === 'ArrowDown' || event.code === 'KeyS') this.focus(this.index + 1);
|
||||
else if (event.code === 'Enter' || event.code === 'Space' || event.code === 'NumpadEnter') this.select(this.index);
|
||||
});
|
||||
scene.input.keyboard.on('keydown', this.onKeyDown);
|
||||
scene.events.once('shutdown', () => this.destroy());
|
||||
this.focus(0);
|
||||
}
|
||||
|
||||
/** @param {number} index */
|
||||
focus(index) {
|
||||
const count = this.items.length;
|
||||
const next = (index + count) % count;
|
||||
if (next !== this.index) this.audio?.play('ui-click');
|
||||
this.index = next;
|
||||
this.texts.forEach((text, i) => {
|
||||
const active = i === this.index;
|
||||
text.setColor(active ? UI.colors.gold : UI.colors.cream);
|
||||
text.setScale(active ? 1.08 : 1);
|
||||
// Großbuchstaben wie auf einem Plakat; die Texte selbst bleiben normal geschrieben.
|
||||
const label = this.items[i].label.toUpperCase();
|
||||
text.setText(active ? `» ${label} «` : label);
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {number} index */
|
||||
select(index) {
|
||||
this.focus(index);
|
||||
this.audio?.play('ui-confirm');
|
||||
this.items[index].onSelect();
|
||||
}
|
||||
|
||||
/** @param {boolean} enabled */
|
||||
setEnabled(enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.scene.input.keyboard?.off('keydown', this.onKeyDown);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user