UNPKG

hud-gamepad

Version:

A Heads Up Display (HUD) for Gamepads, Keyboards, and more

191 lines (170 loc) 5.85 kB
import { Layout, ButtonType, button_offset } from '../utils/layout.js'; import { colors } from '../utils/colors.js'; import { Button, ButtonsLayout } from '../components/button.js'; import { Joystick } from '../components/joystick.js'; import { stage } from '../components/stage.js'; export class Controller { constructor() { this.position = {}; this.buttonsLayout = []; this.buttons = []; this.joystick = null; this.layout = Layout.BOTTOM_RIGHT; this.start = false; this.select = false; this.buttonMap = new Map(); this.stateMap = {}; this.config = {}; } init(config) { if (!config) return; this.config = { ...config }; this.layout = config.layout || Layout.BOTTOM_RIGHT; this.start = config.start || false; this.select = config.select || false; // Clear existing buttons this.buttons = []; this.buttonsLayout = []; // Initialize button layout if (config.buttons) { let index = Math.min(config.buttons.length - 1, Object.keys(ButtonsLayout).length - 1); index = index > 4 ? 4 : index; this.buttonsLayout = [...ButtonsLayout[Object.keys(ButtonsLayout)[index]]]; config.buttons.forEach((button, n) => { if (button.name) this.buttonsLayout[n].name = button.name; if (button.color) this.buttonsLayout[n].color = button.color; if (button.key) this.buttonsLayout[n].key = button.key; }); } else { this.buttonsLayout = [...ButtonsLayout.FOUR_BUTTONS]; } // Calculate shift for button positioning const shift = this.buttonsLayout.reduce((acc, button) => { if (button.r) { acc += button.r; if (this.layout === Layout.TOP_LEFT) { button.y -= button.r * 2; } } return acc; }, 0); // Set controller position based on layout this.setPosition(shift); // Add start/select buttons if configured const { width, height } = stage.getDimensions(); if (this.start || this.select) { // Adjust Y position based on layout const yOffset = this.layout.includes('TOP') ? button_offset.y : height - button_offset.y; const buttonConfig = new Map([ [ButtonType.START, { x: width / 2, y: yOffset - 15, w: 50, h: 15, color: colors.black, name: ButtonType.START, key: typeof this.start === 'object' ? this.start.key : 'Enter' // Use provided key or default }], [ButtonType.SELECT, { x: width / 2, y: yOffset - 15, w: 50, h: 15, color: colors.black, name: ButtonType.SELECT, key: typeof this.select === 'object' ? this.select.key : 'Shift' // Use provided key or default }] ]); if (this.start) { const startConfig = buttonConfig.get(ButtonType.START); // Merge any provided config if (typeof this.start === 'object') { Object.assign(startConfig, this.start); } this.buttonsLayout.push(startConfig); } if (this.select) { const selectConfig = buttonConfig.get(ButtonType.SELECT); // Merge any provided config if (typeof this.select === 'object') { Object.assign(selectConfig, this.select); } this.buttonsLayout.push(selectConfig); } } // Initialize buttons this.initButtons(); // Initialize joystick if configured if (config.joystick) { this.joystick = new Joystick(); this.joystick.init(this.position); ["x-dir", "y-dir", "x-axis", "y-axis"].forEach(key => this.stateMap[key] = 0); } } setPosition(shift) { const { width, height } = stage.getDimensions(); const positions = { [Layout.TOP_LEFT]: { x: shift + button_offset.x, y: button_offset.y }, [Layout.TOP_RIGHT]: { x: width - button_offset.x, y: button_offset.y }, [Layout.BOTTOM_LEFT]: { x: shift + button_offset.x, y: height - button_offset.y }, [Layout.BOTTOM_RIGHT]: { x: width - button_offset.x, y: height - button_offset.y } }; this.position = positions[this.layout]; } initButtons() { // Clear existing buttons array this.buttons = []; const ctx = stage.getContext(); const { width } = stage.getDimensions(); this.buttonsLayout.forEach(config => { const buttonConfig = { ...config }; if (buttonConfig.r) { buttonConfig.x = this.position.x - config.x; buttonConfig.y = this.position.y - config.y; const r = buttonConfig.r; buttonConfig.hit = { x: [buttonConfig.x - r, buttonConfig.x + r * 2], y: [buttonConfig.y - r, buttonConfig.y + r * 2], active: false }; } else { // For start/select buttons buttonConfig.x = buttonConfig.x - buttonConfig.w; if (this.start && this.select) { const offset = buttonConfig.h * 2; buttonConfig.x = buttonConfig.name === "select" ? width / 2 - buttonConfig.w - offset : width / 2; } buttonConfig.hit = { x: [buttonConfig.x, buttonConfig.x + buttonConfig.w], y: [buttonConfig.y, buttonConfig.y + buttonConfig.h], active: false }; } this.stateMap[buttonConfig.name] = 0; this.buttons.push(new Button(ctx, buttonConfig)); }); } draw() { if (this.buttons && this.buttons.length > 0) { this.buttons.forEach(button => { if (button) button.draw(); }); } if (this.joystick) { this.joystick.draw(); } } getState() { return this.stateMap; } updateState(inputState) { Object.assign(this.stateMap, inputState); } resetStates() { Object.keys(this.stateMap).forEach(key => { this.stateMap[key] = 0; }); } }