Un sistema de entrada flexible y configurable para Phaser 3 que proporciona compatibilidad con teclado y gamepad arcade Unraf (USB GENERIC).
- Mapeo configurable de teclas de teclado
- Compatibilidad fija con gamepad arcade Unraf
- Detección simultánea de entrada de teclado y gamepad
- Métodos para detectar pulsaciones continuas y momentáneas
- Constantes predefinidas para evitar hardcodear strings
- API simple y consistente para cualquier tipo de juego
El gamepad arcade Unraf es reconocido por el sistema como USB GENERIC y cuenta con:
- Norte (B3) - Botón superior
- Este (B1) - Botón derecho
- Sur (B0) - Botón inferior
- Oeste (B2) - Botón izquierdo
- Eje X (axis 0): Valores de -1 (izquierda) a 1 (derecha)
- Eje Y (axis 1): Valores de -1 (arriba) a 1 (abajo)
| Comando | Descripción |
|---|---|
npm install |
Instala las dependencias del proyecto |
npm run dev |
Inicia un servidor web de desarrollo |
npm run build |
Crea una compilación de producción en la carpeta dist |
npm run dev-nolog |
Inicia un servidor web de desarrollo sin enviar datos anónimos (ver "Sobre log.js" abajo) |
npm run build-nolog |
Crea una compilación de producción en la carpeta dist sin enviar datos anónimos (ver "Sobre log.js" abajo) |
import InputSystem, { INPUT_ACTIONS } from "./utils/InputSystem.js";
// En tu escena de Phaser
export class GameScene extends Scene {
create() {
// Inicializar el sistema de entrada
this.inputSystem = new InputSystem(this.input);
// Configurar controles de teclado
this.inputSystem.configureKeyboard({
[INPUT_ACTIONS.NORTH]: [Phaser.Input.Keyboard.KeyCodes.SPACE],
[INPUT_ACTIONS.SOUTH]: [Phaser.Input.Keyboard.KeyCodes.X],
[INPUT_ACTIONS.EAST]: [Phaser.Input.Keyboard.KeyCodes.D],
[INPUT_ACTIONS.WEST]: [Phaser.Input.Keyboard.KeyCodes.A]
});
}
update() {
// Verificar entrada
if (this.inputSystem.isPressed(INPUT_ACTIONS.NORTH)) {
// Acción continua
this.player.jump();
}
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.SOUTH)) {
// Acción única por pulsación
this.openMenu();
}
}
}import { INPUT_ACTIONS } from "./utils/InputSystem.js";
// Movimientos direccionales
INPUT_ACTIONS.UP // Movimiento hacia arriba (eje Y negativo)
INPUT_ACTIONS.DOWN // Movimiento hacia abajo (eje Y positivo)
INPUT_ACTIONS.LEFT // Movimiento hacia la izquierda (eje X negativo)
INPUT_ACTIONS.RIGHT // Movimiento hacia la derecha (eje X positivo)
// Botones del gamepad
INPUT_ACTIONS.NORTH // Botón norte (B3)
INPUT_ACTIONS.EAST // Botón este (B1)
INPUT_ACTIONS.SOUTH // Botón sur (B0)
INPUT_ACTIONS.WEST // Botón oeste (B2)export class GameScene extends Scene {
create() {
this.inputSystem = new InputSystem(this.input);
// Configurar controles WASD + flechas
this.inputSystem.configureKeyboard({
[INPUT_ACTIONS.UP]: [Phaser.Input.Keyboard.KeyCodes.W, Phaser.Input.Keyboard.KeyCodes.UP],
[INPUT_ACTIONS.DOWN]: [Phaser.Input.Keyboard.KeyCodes.S, Phaser.Input.Keyboard.KeyCodes.DOWN],
[INPUT_ACTIONS.LEFT]: [Phaser.Input.Keyboard.KeyCodes.A, Phaser.Input.Keyboard.KeyCodes.LEFT],
[INPUT_ACTIONS.RIGHT]: [Phaser.Input.Keyboard.KeyCodes.D, Phaser.Input.Keyboard.KeyCodes.RIGHT]
});
this.player = this.add.sprite(400, 300, 'player');
this.playerSpeed = 200;
}
update(time, delta) {
const deltaTime = delta / 1000;
let velocityX = 0;
let velocityY = 0;
// Movimiento horizontal
if (this.inputSystem.isPressed(INPUT_ACTIONS.LEFT)) {
velocityX = -this.playerSpeed;
} else if (this.inputSystem.isPressed(INPUT_ACTIONS.RIGHT)) {
velocityX = this.playerSpeed;
}
// Movimiento vertical
if (this.inputSystem.isPressed(INPUT_ACTIONS.UP)) {
velocityY = -this.playerSpeed;
} else if (this.inputSystem.isPressed(INPUT_ACTIONS.DOWN)) {
velocityY = this.playerSpeed;
}
// Aplicar movimiento
this.player.x += velocityX * deltaTime;
this.player.y += velocityY * deltaTime;
}
}export class MenuScene extends Scene {
create() {
this.inputSystem = new InputSystem(this.input);
// Configurar navegación de menú
this.inputSystem.configureKeyboardByString({
[INPUT_ACTIONS.UP]: ['W', 'UP'],
[INPUT_ACTIONS.DOWN]: ['S', 'DOWN'],
[INPUT_ACTIONS.NORTH]: ['ENTER', 'SPACE'], // Confirmar
[INPUT_ACTIONS.SOUTH]: ['ESC', 'X'] // Cancelar
});
this.menuItems = ['Nuevo Juego', 'Cargar', 'Opciones', 'Salir'];
this.selectedIndex = 0;
}
update() {
// Navegación del menú (solo una vez por pulsación)
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.UP)) {
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
this.updateMenuDisplay();
}
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.DOWN)) {
this.selectedIndex = Math.min(this.menuItems.length - 1, this.selectedIndex + 1);
this.updateMenuDisplay();
}
// Confirmar selección
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.NORTH)) {
this.selectMenuItem(this.selectedIndex);
}
// Cancelar/Volver
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.SOUTH)) {
this.goBack();
}
}
}export class PlatformerScene extends Scene {
create() {
this.inputSystem = new InputSystem(this.input);
// Controles típicos de plataformero
this.inputSystem.configureKeyboard({
[INPUT_ACTIONS.LEFT]: [Phaser.Input.Keyboard.KeyCodes.A, Phaser.Input.Keyboard.KeyCodes.LEFT],
[INPUT_ACTIONS.RIGHT]: [Phaser.Input.Keyboard.KeyCodes.D, Phaser.Input.Keyboard.KeyCodes.RIGHT],
[INPUT_ACTIONS.NORTH]: [Phaser.Input.Keyboard.KeyCodes.SPACE], // Saltar
[INPUT_ACTIONS.SOUTH]: [Phaser.Input.Keyboard.KeyCodes.S], // Agacharse
[INPUT_ACTIONS.EAST]: [Phaser.Input.Keyboard.KeyCodes.E], // Interactuar
[INPUT_ACTIONS.WEST]: [Phaser.Input.Keyboard.KeyCodes.Q] // Habilidad especial
});
}
update() {
// Movimiento horizontal continuo
if (this.inputSystem.isPressed(INPUT_ACTIONS.LEFT)) {
this.player.setVelocityX(-200);
this.player.flipX = true;
} else if (this.inputSystem.isPressed(INPUT_ACTIONS.RIGHT)) {
this.player.setVelocityX(200);
this.player.flipX = false;
} else {
this.player.setVelocityX(0);
}
// Salto (una sola vez por pulsación)
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.NORTH) && this.player.body.onFloor()) {
this.player.setVelocityY(-500);
}
// Agacharse (continuo)
if (this.inputSystem.isPressed(INPUT_ACTIONS.SOUTH)) {
this.player.body.setSize(32, 16); // Hitbox más pequeña
} else {
this.player.body.setSize(32, 32); // Hitbox normal
}
// Interacciones (una vez por pulsación)
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.EAST)) {
this.interactWithNearbyObjects();
}
if (this.inputSystem.isJustPressed(INPUT_ACTIONS.WEST)) {
this.useSpecialAbility();
}
}
}// Configuración para jugadores zurdos
inputSystem.configureKeyboard({
[INPUT_ACTIONS.UP]: [Phaser.Input.Keyboard.KeyCodes.UP],
[INPUT_ACTIONS.DOWN]: [Phaser.Input.Keyboard.KeyCodes.DOWN],
[INPUT_ACTIONS.LEFT]: [Phaser.Input.Keyboard.KeyCodes.LEFT],
[INPUT_ACTIONS.RIGHT]: [Phaser.Input.Keyboard.KeyCodes.RIGHT],
[INPUT_ACTIONS.NORTH]: [Phaser.Input.Keyboard.KeyCodes.NUMPAD_ZERO],
[INPUT_ACTIONS.SOUTH]: [Phaser.Input.Keyboard.KeyCodes.NUMPAD_ONE]
});
// Configuración estilo FPS
inputSystem.configureKeyboardByString({
[INPUT_ACTIONS.UP]: ['W'],
[INPUT_ACTIONS.DOWN]: ['S'],
[INPUT_ACTIONS.LEFT]: ['A'],
[INPUT_ACTIONS.RIGHT]: ['D'],
[INPUT_ACTIONS.NORTH]: ['SPACE'], // Saltar
[INPUT_ACTIONS.SOUTH]: ['C'], // Agacharse
[INPUT_ACTIONS.EAST]: ['E'], // Usar/Interactuar
[INPUT_ACTIONS.WEST]: ['Q'] // Habilidad
});
// Configuración para arcade clásico
inputSystem.configureKeyboardByString({
[INPUT_ACTIONS.NORTH]: ['Z'],
[INPUT_ACTIONS.SOUTH]: ['X'],
[INPUT_ACTIONS.EAST]: ['C'],
[INPUT_ACTIONS.WEST]: ['V']
});Verifica si una acción está siendo presionada actualmente (continua).
if (inputSystem.isPressed(INPUT_ACTIONS.NORTH)) {
// Se ejecuta mientras el botón esté presionado
}Verifica si una acción fue presionada en este frame específico (momentánea).
if (inputSystem.isJustPressed(INPUT_ACTIONS.NORTH)) {
// Se ejecuta solo una vez por pulsación
}Configura controles de teclado usando KeyCodes de Phaser.
inputSystem.configureKeyboard({
[INPUT_ACTIONS.NORTH]: [Phaser.Input.Keyboard.KeyCodes.SPACE]
});Configura controles de teclado usando strings (más fácil).
inputSystem.configureKeyboardByString({
[INPUT_ACTIONS.NORTH]: ['SPACE', 'ENTER']
});Configura un mapeo individual.
inputSystem.setMapping(INPUT_ACTIONS.NORTH, {
keyboard: [Phaser.Input.Keyboard.KeyCodes.SPACE]
});Obtiene la configuración completa actual.
Obtiene las teclas asignadas a una acción específica.
Obtiene todos los mapeos de teclado actuales.
El InputSystem maneja automáticamente el gamepad Unraf cuando se conecta. Los controles de gamepad son fijos y no requieren configuración:
- Joystick: Mapea automáticamente a UP, DOWN, LEFT, RIGHT
- Botones: Mapea automáticamente a NORTH, EAST, SOUTH, WEST
// No necesitas configurar nada para gamepad
// Funciona automáticamente cuando se conecta
this.inputSystem = new InputSystem(this.input);
// Ambos funcionarán simultáneamente:
// - Teclado: teclas configuradas por ti
// - Gamepad: mapeo fijo del arcade Unrafexport class ControlsScene extends Scene {
create() {
this.inputSystem = new InputSystem(this.input);
// Mostrar controles actuales
const currentMappings = this.inputSystem.getAllKeyboardMappings();
Object.keys(currentMappings).forEach(action => {
const keys = currentMappings[action];
console.log(`${action}: ${keys.join(', ')}`);
});
}
// Permitir al usuario cambiar controles
remapControl(action, newKeys) {
this.inputSystem.configureKeyboard({
[action]: newKeys
});
}
}update() {
// Verificar qué dispositivo se está usando
if (this.inputSystem.isKeyboardPressed(INPUT_ACTIONS.NORTH)) {
this.showKeyboardPrompts();
} else if (this.inputSystem.isGamepadPressed(INPUT_ACTIONS.NORTH)) {
this.showGamepadPrompts();
}
}- Gamepad fijo: Los controles de gamepad no deben modificarse para mantener compatibilidad con Unraf
- Teclado configurable: Solo los controles de teclado son configurables por el desarrollador
- Inicialización: Siempre inicializa el InputSystem en el método
create()de tu escena - Constantes: Usa siempre
INPUT_ACTIONSen lugar de strings hardcodeados - Performance: Los métodos
isPressed()eisJustPressed()son optimizados para uso enupdate()
Para contribuir al desarrollo del InputSystem:
- Fork el repositorio
- Crea una rama para tu feature
- Mantén la compatibilidad con gamepad Unraf
- Actualiza la documentación
Este proyecto está bajo la licencia MIT. Ver archivo LICENSE para más detalles.