UNPKG

phaser3-merged-input

Version:

A Phaser 3 plugin to handle input from keyboard, gamepad & mouse, allowing for easy key definition and multiplayer input

357 lines (285 loc) 15.5 kB
import circlebutton from './button'; export default class Demo extends Phaser.Scene { preload() { // Input controller scene this.scene.launch('InputController') this.load.multiatlas('gamepad', 'assets/gamepad.json', 'assets'); } create() { // Get the input controller scene, which contains our player objects this.inputController = this.scene.get('InputController'); // Get our player objects this.player1 = this.inputController.player1; this.player2 = this.inputController.player2; // Set up an array of player objects this.players = [this.player1, this.player2] // Set up some gamepad sprites for testing // We can check for button numbers, or mapped button names. For the sprites, we'll use mapped button names. this.player1.sprites = { 'dpad': this.add.image(100, 350, 'gamepad', 'XboxOne_Dpad').setScale(2), 'RC_S': this.add.image(370, 420, 'gamepad', 'XboxOne_A'), 'RC_E': this.add.image(440, 350, 'gamepad', 'XboxOne_B'), 'RC_W': this.add.image(300, 350, 'gamepad', 'XboxOne_X'), 'RC_N': this.add.image(370, 280, 'gamepad', 'XboxOne_Y'), 'LB': this.add.image(70, 200, 'gamepad', 'XboxOne_LB'), 'RB': this.add.image(430, 200, 'gamepad', 'XboxOne_RB'), 'LT': this.add.image(70, 130, 'gamepad', 'XboxOne_LT'), 'RT': this.add.image(430, 130, 'gamepad', 'XboxOne_RT') } this.player2.sprites = { 'dpad': this.add.image(800, 350, 'gamepad', 'XboxOne_Dpad').setScale(2), 'RC_S': this.add.image(1070, 420, 'gamepad', 'XboxOne_A'), 'RC_E': this.add.image(1140, 350, 'gamepad', 'XboxOne_B'), 'RC_W': this.add.image(1000, 350, 'gamepad', 'XboxOne_X'), 'RC_N': this.add.image(1070, 280, 'gamepad', 'XboxOne_Y'), 'LB': this.add.image(770, 200, 'gamepad', 'XboxOne_LB'), 'RB': this.add.image(1130, 200, 'gamepad', 'XboxOne_RB'), 'LT': this.add.image(770, 130, 'gamepad', 'XboxOne_LT'), 'RT': this.add.image(1130, 130, 'gamepad', 'XboxOne_RT') } // Now we'll add some graphics to represent the actual button numbers. // How these map to friendly button names may differ per device, so the plugin attempts to map them according to the pad ID. this.player1.buttonGraphics = {} for (let i=0; i<=16; i++) { this.player1.buttonGraphics['B' + i] = new circlebutton({scene: this, x:100 + (i > 7 ? (i - 8.5) * 50 : (i * 50)), y:i > 7 ? 550 : 500, text:i}); this.add.existing(this.player1.buttonGraphics['B' + i]) } this.player2.buttonGraphics = {} for (let i=0; i<=16; i++) { this.player2.buttonGraphics['B' + i] = new circlebutton({scene: this, x:790 + (i > 7 ? (i - 8.5) * 50 : (i * 50)), y:i > 7 ? 550 : 500, text:i}); this.add.existing(this.player2.buttonGraphics['B' + i]) } // Set up some debug text this.player1Text = this.add.text(50, 600, '', { fontFamily: 'Arial', fontSize: 14, color: '#00ff00' }); this.player1Combos = this.add.text(50, 730, '', { fontFamily: 'Arial', fontSize: 16, color: '#00ff00' }); this.player2Text = this.add.text(50, 800, '', { fontFamily: 'Arial', fontSize: 14, color: '#00ff00' }); this.player2Combos = this.add.text(50, 930, '', { fontFamily: 'Arial', fontSize: 16, color: '#00ff00' }); // Instructions this.instructions1 = this.add.text(50, 20, ['Directions: WASD', 'Buttons: 1-0'], { fontFamily: 'Arial', fontSize: 14, color: '#00ff00' }); this.instructions1 = this.add.text(740, 20, ['Directions: Cursors', 'Buttons: Numpad 1-0'], { fontFamily: 'Arial', fontSize: 14, color: '#00ff00' }); // Set positions for the players this.player1.setPosition(250, 150); console.log(this.player1); // Draw a graphic to represent the players this.player1.sprites.player = this.add.graphics({x: this.player1.position.x, y: this.player1.position.y}); this.player1.sprites.player.fillStyle(0x00ff00, 1); this.player1.sprites.player.fillCircle(0, 0, 20); // Text for bearing this.player1.sprites.bearingText = this.add.text(this.player1.position.x, this.player1.position.y - 30, '', { fontFamily: 'Arial', fontSize: 14, color: '#00ff00' }); this.player1.sprites.bearingText.setOrigin(0.5, 0.5); // Set the bearing text this.player1.sprites.bearingText.setText('N'); // Add an arrow to represent the direction this.player1.sprites.arrow = this.add.graphics({x: this.player1.position.x, y: this.player1.position.y}); this.player1.sprites.arrow.fillStyle(0xff0000, 1); this.player1.sprites.arrow.fillTriangle(-10, -10, 10, -10, 0, 20); // Add a line graphic inside the arrow to show the snapped direction this.player1.sprites.line = this.add.graphics({x: this.player1.position.x, y: this.player1.position.y}); this.player1.sprites.line.lineStyle(2, 0xffffff, 1); this.player1.sprites.line.lineBetween(0, 0, 0, 20); this.player2.setPosition(950, 150); // Draw a graphic to represent the players this.player2.sprites.player = this.add.graphics({x: this.player2.position.x, y: this.player2.position.y}); this.player2.sprites.player.fillStyle(0x0000ff, 1); this.player2.sprites.player.fillCircle(0, 0, 20); // Add an arrow to represent the direction this.player2.sprites.arrow = this.add.graphics({x: this.player2.position.x, y: this.player2.position.y}); this.player2.sprites.arrow.fillStyle(0xff0000, 1); this.player2.sprites.arrow.fillTriangle(-10, -10, 10, -10, 0, 20); // Add a line graphic inside the arrow to show the snapped direction this.player2.sprites.line = this.add.graphics({x: this.player2.position.x, y: this.player2.position.y}); this.player2.sprites.line.lineStyle(2, 0xffffff, 1); this.player2.sprites.line.lineBetween(0, 0, 0, 20); // Text for bearing this.player2.sprites.bearingText = this.add.text(this.player2.position.x, this.player2.position.y - 30, '', { fontFamily: 'Arial', fontSize: 14, color: '#00ff00' }); this.player2.sprites.bearingText.setOrigin(0.5, 0.5); // Set the bearing text this.player2.sprites.bearingText.setText('N'); /** * Some examples of creating button combos */ this.input.keyboard.createCombo([38, 38, 40, 40, 37, 39, 37, 39, 66, 65], { resetOnMatch: true }).name = 'Konami code - Keyboard'; this.inputController.mergedInput.createButtonCombo(this.player1, ['UP', 'UP', 'DOWN', 'DOWN', 'LEFT', 'RIGHT', 'LEFT', 'RIGHT', 'RC_E', 'RC_S'], { resetOnMatch: true }).name = 'Konami code - Gamepad'; this.inputController.mergedInput.createButtonCombo(this.player1, ['B12', 'B13'], { resetOnMatch: true, maxKeyDelay: 1000, }).name = 'Button ID test'; this.input.keyboard.on('keycombomatch', event => { this.player1Combos.setText(`KEY COMBO: ${event.name}`) console.log(`${event.name} entered!`); }); this.inputController.mergedInput.events.on('buttoncombomatch', event => { this[`player${event.player.index + 1}Combos`].setText(`BUTTON COMBO: ${event.combo.name}`) console.log(`${event.combo.name} entered! - Player: ${event.player.index}`); }); } /** * Check player movement */ checkMovement() { return this; } update() { // Loop through player objects for (let thisPlayer of this.players) { // Reset dpad frame thisPlayer.sprites.dpad.setFrame('XboxOne_Dpad'); // Show dpad frame for direction input. (Diagonal input is supported, but can't easily be shown with these sprites) if (thisPlayer.direction.UP > 0) { thisPlayer.sprites.dpad.setFrame('XboxOne_Dpad_Up'); } if (thisPlayer.direction.RIGHT > 0) { thisPlayer.sprites.dpad.setFrame('XboxOne_Dpad_Right'); } if (thisPlayer.direction.DOWN > 0) { thisPlayer.sprites.dpad.setFrame('XboxOne_Dpad_Down'); } if (thisPlayer.direction.LEFT > 0) { thisPlayer.sprites.dpad.setFrame('XboxOne_Dpad_Left'); } // Check the button NUMBER values to correspond with the button graphics for (let thisButton in thisPlayer.buttons) { if (typeof thisPlayer.buttonGraphics[thisButton] !== 'undefined') { if (thisPlayer.buttons[thisButton] > 0) { thisPlayer.buttonGraphics[thisButton].circle.setFillStyle(0xcc0000, 1) } else { thisPlayer.buttonGraphics[thisButton].circle.setFillStyle(0x6666ff, 1) } } } // Check the MAPPED button values to correspond with the sprites we created for (let thisButton in thisPlayer.buttons_mapped) { if (typeof thisPlayer.sprites[thisButton] !== 'undefined') { if (thisPlayer.buttons_mapped[thisButton] > 0) { this.tintButton(thisPlayer, thisButton); } else { thisPlayer.sprites[thisButton].clearTint(); } } } } this.player1Text.setText([ 'Player 1', 'Gamepad: ' + (typeof this.player1.gamepad.index === 'undefined' ? 'Press a button to connect' : this.player1.gamepad.id), 'Directions: ' + JSON.stringify(this.player1.direction), 'Buttons: ' + JSON.stringify(this.player1.buttons), 'Mouse: ' + JSON.stringify(this.player1.pointer), 'Timers: ' + JSON.stringify(this.player1.timers), 'Interaction: ' + JSON.stringify(this.player1.interaction), `isDown: ${this.player1.isDown(Object.keys(this.player1.buttons_mapped))}, ${this.player1.isDown(Object.keys(this.player1.buttons))}`, 'Internal: ' + JSON.stringify(this.player1.internal) ]); // Check to see if the mouse is moving, in which case we'll use that for direction changes if (this.player1.pointer.TIMESTAMP > this.player1.direction.TIMESTAMP) { // Update the visual pointer this.player1.sprites.bearingText.setText(this.player1.pointer.BEARING || 'E'); this.player1.sprites.arrow.rotation = Phaser.Math.DegToRad(this.player1.pointer.DEGREES - 90); this.player1.sprites.line.rotation = Phaser.Math.DegToRad(this.player1.pointer.BEARING_DEGREES - 90); } else { // Update the visual pointer this.player1.sprites.bearingText.setText(this.player1.direction.BEARING_LAST || 'E'); this.player1.sprites.arrow.rotation = Phaser.Math.DegToRad(this.player1.direction.DEGREES_LAST - 90); this.player1.sprites.line.rotation = Phaser.Math.DegToRad(this.player1.direction.BEARING_DEGREES_LAST - 90); } this.player2Text.setText([ 'Player 2', 'Gamepad: ' + (typeof this.player2.gamepad.index === 'undefined' ? 'Press a button to connect' : this.player2.gamepad.id), 'Directions: ' + JSON.stringify(this.player2.direction), 'Buttons: ' + JSON.stringify(this.player2.buttons), 'Mouse: ' + JSON.stringify(this.player2.pointer), 'Timers: ' + JSON.stringify(this.player2.timers), 'Interaction: ' + JSON.stringify(this.player2.interaction), `isDown: ${this.player2.isDown(Object.keys(this.player2.buttons_mapped))}, ${this.player2.isDown(Object.keys(this.player2.buttons))}`, 'Internal: ' + JSON.stringify(this.player2.internal) ]); // Update the bearing text this.player2.sprites.bearingText.setText(this.player2.direction.BEARING_LAST || 'E'); this.player2.sprites.arrow.rotation = Phaser.Math.DegToRad(this.player2.direction.DEGREES_LAST - 90); this.player2.sprites.line.rotation = Phaser.Math.DegToRad(this.player2.direction.BEARING_DEGREES_LAST - 90); /** * Some logging of player helper functions */ /* // Here we check if certain buttons were pressed in this update step. if (this.player1.interaction_mapped.isPressed(['LC_N','START','RC_S','RC_N'])) { console.log(`mapped - isPressed: ${this.player1.interaction_mapped.isPressed(['LC_N', 'START', 'RC_S', 'RC_N'])}`) } // Here we check if certain buttons are held down in this update step. if (this.player1.interaction_mapped.isDown(['LC_N', 'RC_S', 'RC_N'])) { console.log(`mapped - isDown: ${this.player1.interaction_mapped.isDown(['LC_N', 'RC_S', 'RC_N'])}`) } // Here we check if certain buttons are held down for a given duration in this update step. if (this.player1.interaction_mapped.checkDown(['LC_N'], 1000)) { console.log(`mapped checkDown: ${this.player1.interaction_mapped.checkDown(['LC_N'], 1000)}`) } // Here we check if certain buttons are held down in this update step. if (this.player1.interaction.isPressed(['DOWN', 'B1'])) { console.log(`raw - isPressed: ${this.player1.interaction.isPressed(['DOWN', 'B1'])}`) } // Here we check if certain buttons are held down in this update step. if (this.player1.interaction.isDown(['DOWN', 'B1'])) { console.log(`raw - isDown: ${this.player1.interaction.isDown(['DOWN', 'B1'])}`) } // Here we check if certain buttons are held down for a given duration in this update step. if (this.player1.interaction.checkDown(['DOWN'], 1000, true)) { console.log(`raw checkDown: ${this.player1.interaction.checkDown(['DOWN'], 1000, true)}`) } // Generic button (mapped / unmapped) isPressed function if (this.player1.isPressed(['RIGHT', 'LC_W', 'B2'])) { console.log(`generic - isPressed: ${this.player1.isPressed(['RIGHT', 'LC_W', 'B2'])}`) } // Generic button (mapped / unmapped) isDown function if (this.player1.isDown(['RIGHT', 'LC_W', 'B2'])) { console.log(`generic - isDown: ${this.player1.isDown(['RIGHT', 'LC_W', 'B2'])}`) } // Generic button (mapped / unmapped) isReleased function if (this.player1.isReleased(['RIGHT', 'LC_W', 'B2'])) { console.log(`generic - isReleased: ${this.player1.isReleased(['RIGHT', 'LC_W', 'B2'])}`) } */ // Here we check if certain buttons are held down for a given duration in this update step. if (this.player1.checkDown(['M1','LEFT'], 1000, false)) { console.log(`generic checkDown: LEFT`) } // Mouse pointer check /* if (this.player1.isPressed(['M1', 'M2'])) { console.log(`isPressed: ${this.player1.interaction.isPressed(['M1', 'M2'])}`) } */ // this.debugView.value = this.inputController.mergedInput.debug().input; } tintButton(player, button){ player.sprites[button].setTint(0xff0000); } }