mirror of
https://github.com/photonstorm/phaser
synced 2024-12-23 19:43:28 +00:00
1261 lines
40 KiB
JavaScript
1261 lines
40 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @copyright 2018 Photon Storm Ltd.
|
|
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
|
*/
|
|
|
|
var Class = require('../utils/Class');
|
|
var CONST = require('./const');
|
|
var EventEmitter = require('eventemitter3');
|
|
var Gamepad = require('./gamepad/GamepadManager');
|
|
var Keyboard = require('./keyboard/KeyboardManager');
|
|
var Mouse = require('./mouse/MouseManager');
|
|
var Pointer = require('./Pointer');
|
|
var Rectangle = require('../geom/rectangle/Rectangle');
|
|
var Touch = require('./touch/TouchManager');
|
|
var TransformMatrix = require('../gameobjects/components/TransformMatrix');
|
|
var TransformXY = require('../math/TransformXY');
|
|
|
|
/**
|
|
* @classdesc
|
|
* The Input Manager is responsible for handling all of the input related systems in a single Phaser Game instance.
|
|
*
|
|
* Based on the Game Config it will create handlers for mouse, touch, keyboard and gamepad support.
|
|
*
|
|
* It then manages the event queue, pointer creation and general hit test related operations.
|
|
*
|
|
* You rarely need to interact with the Input Manager directly, and as such, all of its properties and methods
|
|
* should be considered private. Instead, you should use the Input Plugin, which is a Scene level system, responsible
|
|
* for dealing with all input events for a Scene.
|
|
*
|
|
* @class InputManager
|
|
* @memberOf Phaser.Input
|
|
* @constructor
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Game} game - The Game instance that owns the Input Manager.
|
|
* @param {object} config - The Input Configuration object, as set in the Game Config.
|
|
*/
|
|
var InputManager = new Class({
|
|
|
|
initialize:
|
|
|
|
function InputManager (game, config)
|
|
{
|
|
/**
|
|
* The Game instance that owns the Input Manager.
|
|
* A Game only maintains on instance of the Input Manager at any time.
|
|
*
|
|
* @name Phaser.Input.InputManager#game
|
|
* @type {Phaser.Game}
|
|
* @readOnly
|
|
* @since 3.0.0
|
|
*/
|
|
this.game = game;
|
|
|
|
/**
|
|
* The Canvas that is used for all DOM event input listeners.
|
|
*
|
|
* @name Phaser.Input.InputManager#canvas
|
|
* @type {HTMLCanvasElement}
|
|
* @since 3.0.0
|
|
*/
|
|
this.canvas;
|
|
|
|
/**
|
|
* The Input Configuration object, as set in the Game Config.
|
|
*
|
|
* @name Phaser.Input.InputManager#config
|
|
* @type {object}
|
|
* @since 3.0.0
|
|
*/
|
|
this.config = config;
|
|
|
|
/**
|
|
* If set, the Input Manager will run its update loop every frame.
|
|
*
|
|
* @name Phaser.Input.InputManager#enabled
|
|
* @type {boolean}
|
|
* @default true
|
|
* @since 3.0.0
|
|
*/
|
|
this.enabled = true;
|
|
|
|
/**
|
|
* The Event Emitter instance that the Input Manager uses to emit events from.
|
|
*
|
|
* @name Phaser.Input.InputManager#events
|
|
* @type {Phaser.Events.EventEmitter}
|
|
* @since 3.0.0
|
|
*/
|
|
this.events = new EventEmitter();
|
|
|
|
/**
|
|
* A standard FIFO queue for the native DOM events waiting to be handled by the Input Manager.
|
|
*
|
|
* @name Phaser.Input.InputManager#queue
|
|
* @type {array}
|
|
* @default []
|
|
* @since 3.0.0
|
|
*/
|
|
this.queue = [];
|
|
|
|
/**
|
|
* DOM Callbacks container.
|
|
*
|
|
* @name Phaser.Input.InputManager#domCallbacks
|
|
* @private
|
|
* @type {object}
|
|
* @since 3.10.0
|
|
*/
|
|
this.domCallbacks = { up: [], down: [], move: [], upOnce: [], downOnce: [], moveOnce: [] };
|
|
|
|
/**
|
|
* Are there any up callbacks defined?
|
|
*
|
|
* @name Phaser.Input.InputManager#_hasUpCallback
|
|
* @private
|
|
* @type {boolean}
|
|
* @since 3.10.0
|
|
*/
|
|
this._hasUpCallback = false;
|
|
|
|
/**
|
|
* Are there any down callbacks defined?
|
|
*
|
|
* @name Phaser.Input.InputManager#_hasDownCallback
|
|
* @private
|
|
* @type {boolean}
|
|
* @since 3.10.0
|
|
*/
|
|
this._hasDownCallback = false;
|
|
|
|
/**
|
|
* Are there any move callbacks defined?
|
|
*
|
|
* @name Phaser.Input.InputManager#_hasMoveCallback
|
|
* @private
|
|
* @type {boolean}
|
|
* @since 3.10.0
|
|
*/
|
|
this._hasMoveCallback = false;
|
|
|
|
/**
|
|
* A reference to the Keyboard Manager class, if enabled via the `input.keyboard` Game Config property.
|
|
*
|
|
* @name Phaser.Input.InputManager#keyboard
|
|
* @type {?Phaser.Input.Keyboard.KeyboardManager}
|
|
* @since 3.0.0
|
|
*/
|
|
this.keyboard = (config.inputKeyboard) ? new Keyboard(this) : null;
|
|
|
|
/**
|
|
* A reference to the Mouse Manager class, if enabled via the `input.mouse` Game Config property.
|
|
*
|
|
* @name Phaser.Input.InputManager#mouse
|
|
* @type {?Phaser.Input.Mouse.MouseManager}
|
|
* @since 3.0.0
|
|
*/
|
|
this.mouse = (config.inputMouse) ? new Mouse(this) : null;
|
|
|
|
/**
|
|
* A reference to the Touch Manager class, if enabled via the `input.touch` Game Config property.
|
|
*
|
|
* @name Phaser.Input.InputManager#touch
|
|
* @type {Phaser.Input.Touch.TouchManager}
|
|
* @since 3.0.0
|
|
*/
|
|
this.touch = (config.inputTouch) ? new Touch(this) : null;
|
|
|
|
/**
|
|
* A reference to the Gamepad Manager class, if enabled via the `input.gamepad` Game Config property.
|
|
*
|
|
* @name Phaser.Input.InputManager#gamepad
|
|
* @type {Phaser.Input.Gamepad.GamepadManager}
|
|
* @since 3.0.0
|
|
*/
|
|
this.gamepad = (config.inputGamepad) ? new Gamepad(this) : null;
|
|
|
|
/**
|
|
* An array of Pointers that have been added to the game.
|
|
* The first entry is reserved for the Mouse Pointer, the rest are Touch Pointers.
|
|
*
|
|
* By default there is 1 touch pointer enabled. If you need more use the `addPointer` method to start them,
|
|
* or set the `input.activePointers` property in the Game Config.
|
|
*
|
|
* @name Phaser.Input.InputManager#pointers
|
|
* @type {Phaser.Input.Pointer[]}
|
|
* @since 3.10.0
|
|
*/
|
|
this.pointers = [];
|
|
|
|
/**
|
|
* The number of touch objects activated and being processed each update.
|
|
*
|
|
* You can change this by either calling `addPointer` at run-time, or by
|
|
* setting the `input.activePointers` property in the Game Config.
|
|
*
|
|
* @name Phaser.Input.InputManager#pointersTotal
|
|
* @type {integer}
|
|
* @readOnly
|
|
* @since 3.10.0
|
|
*/
|
|
this.pointersTotal = config.inputActivePointers;
|
|
|
|
for (var i = 0; i <= this.pointersTotal; i++)
|
|
{
|
|
this.pointers.push(new Pointer(this, i));
|
|
}
|
|
|
|
/**
|
|
* The mouse has its own unique Pointer object, which you can reference directly if making a _desktop specific game_.
|
|
* If you are supporting both desktop and touch devices then do not use this property, instead use `activePointer`
|
|
* which will always map to the most recently interacted pointer.
|
|
*
|
|
* @name Phaser.Input.InputManager#mousePointer
|
|
* @type {?Phaser.Input.Pointer}
|
|
* @since 3.10.0
|
|
*/
|
|
this.mousePointer = (config.inputMouse) ? this.pointers[0] : null;
|
|
|
|
/**
|
|
* The most recently active Pointer object.
|
|
*
|
|
* If you've only 1 Pointer in your game then this will accurately be either the first finger touched, or the mouse.
|
|
*
|
|
* If your game doesn't need to support multi-touch then you can safely use this property in all of your game
|
|
* code and it will adapt to be either the mouse or the touch, based on device.
|
|
*
|
|
* @name Phaser.Input.InputManager#activePointer
|
|
* @type {Phaser.Input.Pointer}
|
|
* @since 3.0.0
|
|
*/
|
|
this.activePointer = this.pointers[0];
|
|
|
|
/**
|
|
* Reset every frame. Set to `true` if any of the Pointers are dirty this frame.
|
|
*
|
|
* @name Phaser.Input.InputManager#dirty
|
|
* @type {boolean}
|
|
* @since 3.10.0
|
|
*/
|
|
this.dirty = false;
|
|
|
|
/**
|
|
* The Scale factor being applied to input coordinates.
|
|
*
|
|
* @name Phaser.Input.InputManager#scale
|
|
* @type { { x:number, y:number } }
|
|
* @since 3.0.0
|
|
*/
|
|
this.scale = { x: 1, y: 1 };
|
|
|
|
/**
|
|
* If the top-most Scene in the Scene List receives an input it will stop input from
|
|
* propagating any lower down the scene list, i.e. if you have a UI Scene at the top
|
|
* and click something on it, that click will not then be passed down to any other
|
|
* Scene below. Disable this to have input events passed through all Scenes, all the time.
|
|
*
|
|
* @name Phaser.Input.InputManager#globalTopOnly
|
|
* @type {boolean}
|
|
* @default true
|
|
* @since 3.0.0
|
|
*/
|
|
this.globalTopOnly = true;
|
|
|
|
/**
|
|
* An internal flag that controls if the Input Manager will ignore or process native DOM events this frame.
|
|
* Set via the InputPlugin.stopPropagation method.
|
|
*
|
|
* @name Phaser.Input.InputManager#ignoreEvents
|
|
* @type {boolean}
|
|
* @default false
|
|
* @since 3.0.0
|
|
*/
|
|
this.ignoreEvents = false;
|
|
|
|
/**
|
|
* The bounds of the Input Manager, used for pointer hit test calculations.
|
|
*
|
|
* @name Phaser.Input.InputManager#bounds
|
|
* @type {Phaser.Geom.Rectangle}
|
|
* @since 3.0.0
|
|
*/
|
|
this.bounds = new Rectangle();
|
|
|
|
/**
|
|
* A re-cycled point-like object to store hit test values in.
|
|
*
|
|
* @name Phaser.Input.InputManager#_tempPoint
|
|
* @type {{x:number,y:number}}
|
|
* @private
|
|
* @since 3.0.0
|
|
*/
|
|
this._tempPoint = { x: 0, y: 0 };
|
|
|
|
/**
|
|
* A re-cycled array to store hit results in.
|
|
*
|
|
* @name Phaser.Input.InputManager#_tempHitTest
|
|
* @type {array}
|
|
* @private
|
|
* @default []
|
|
* @since 3.0.0
|
|
*/
|
|
this._tempHitTest = [];
|
|
|
|
/**
|
|
* A re-cycled matrix used in hit test calculations.
|
|
*
|
|
* @name Phaser.Input.InputManager#_tempMatrix
|
|
* @type {Phaser.GameObjects.Components.TransformMatrix}
|
|
* @private
|
|
* @since 3.4.0
|
|
*/
|
|
this._tempMatrix = new TransformMatrix();
|
|
|
|
game.events.once('boot', this.boot, this);
|
|
},
|
|
|
|
/**
|
|
* The Boot handler is called by Phaser.Game when it first starts up.
|
|
* The renderer is available by now.
|
|
*
|
|
* @method Phaser.Input.InputManager#boot
|
|
* @protected
|
|
* @since 3.0.0
|
|
*/
|
|
boot: function ()
|
|
{
|
|
this.canvas = this.game.canvas;
|
|
|
|
this.updateBounds();
|
|
|
|
this.events.emit('boot');
|
|
|
|
this.game.events.on('prestep', this.update, this);
|
|
this.game.events.once('destroy', this.destroy, this);
|
|
},
|
|
|
|
/**
|
|
* Updates the Input Manager bounds rectangle to match the bounding client rectangle of the
|
|
* canvas element being used to track input events.
|
|
*
|
|
* @method Phaser.Input.InputManager#updateBounds
|
|
* @since 3.0.0
|
|
*/
|
|
updateBounds: function ()
|
|
{
|
|
var bounds = this.bounds;
|
|
|
|
var clientRect = this.canvas.getBoundingClientRect();
|
|
|
|
bounds.x = clientRect.left + window.pageXOffset - document.documentElement.clientLeft;
|
|
bounds.y = clientRect.top + window.pageYOffset - document.documentElement.clientTop;
|
|
bounds.width = clientRect.width;
|
|
bounds.height = clientRect.height;
|
|
},
|
|
|
|
/**
|
|
* Resizes the Input Manager internal values, including the bounds and scale factor.
|
|
*
|
|
* @method Phaser.Input.InputManager#resize
|
|
* @since 3.2.0
|
|
*/
|
|
resize: function ()
|
|
{
|
|
this.updateBounds();
|
|
|
|
// Game config size
|
|
var gw = this.game.config.width;
|
|
var gh = this.game.config.height;
|
|
|
|
// Actual canvas size
|
|
var bw = this.bounds.width;
|
|
var bh = this.bounds.height;
|
|
|
|
// Scale factor
|
|
this.scale.x = gw / bw;
|
|
this.scale.y = gh / bh;
|
|
},
|
|
|
|
/**
|
|
* Internal update loop, called automatically by the Game Step.
|
|
*
|
|
* @method Phaser.Input.InputManager#update
|
|
* @private
|
|
* @since 3.0.0
|
|
*
|
|
* @param {number} time - The time stamp value of this game step.
|
|
*/
|
|
update: function (time)
|
|
{
|
|
var i;
|
|
|
|
this.events.emit('update');
|
|
|
|
this.ignoreEvents = false;
|
|
|
|
this.dirty = false;
|
|
|
|
var len = this.queue.length;
|
|
|
|
var pointers = this.pointers;
|
|
|
|
for (i = 0; i < this.pointersTotal; i++)
|
|
{
|
|
pointers[i].reset();
|
|
}
|
|
|
|
if (!this.enabled || len === 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.dirty = true;
|
|
|
|
this.updateBounds();
|
|
|
|
this.scale.x = this.game.config.width / this.bounds.width;
|
|
this.scale.y = this.game.config.height / this.bounds.height;
|
|
|
|
// Clears the queue array, and also means we don't work on array data that could potentially
|
|
// be modified during the processing phase
|
|
var queue = this.queue.splice(0, len);
|
|
var mouse = this.mousePointer;
|
|
|
|
// Process the event queue, dispatching all of the events that have stored up
|
|
for (i = 0; i < len; i += 2)
|
|
{
|
|
var type = queue[i];
|
|
var event = queue[i + 1];
|
|
|
|
switch (type)
|
|
{
|
|
case CONST.MOUSE_DOWN:
|
|
mouse.down(event, time);
|
|
break;
|
|
|
|
case CONST.MOUSE_MOVE:
|
|
mouse.move(event, time);
|
|
break;
|
|
|
|
case CONST.MOUSE_UP:
|
|
mouse.up(event, time);
|
|
break;
|
|
|
|
case CONST.TOUCH_START:
|
|
this.startPointer(event, time);
|
|
break;
|
|
|
|
case CONST.TOUCH_MOVE:
|
|
this.updatePointer(event, time);
|
|
break;
|
|
|
|
case CONST.TOUCH_END:
|
|
this.stopPointer(event, time);
|
|
break;
|
|
|
|
case CONST.POINTER_LOCK_CHANGE:
|
|
this.events.emit('pointerlockchange', event, this.mouse.locked);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
// event.targetTouches = list of all touches on the TARGET ELEMENT (i.e. game dom element)
|
|
// event.touches = list of all touches on the ENTIRE DOCUMENT, not just the target element
|
|
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
|
|
|
|
/**
|
|
* Called by the main update loop when a Touch Start Event is received.
|
|
*
|
|
* @method Phaser.Input.InputManager#startPointer
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {TouchEvent} event - The native DOM event to be processed.
|
|
* @param {number} time - The time stamp value of this game step.
|
|
*/
|
|
startPointer: function (event, time)
|
|
{
|
|
var pointers = this.pointers;
|
|
|
|
for (var c = 0; c < event.changedTouches.length; c++)
|
|
{
|
|
var changedTouch = event.changedTouches[c];
|
|
|
|
for (var i = 1; i < this.pointersTotal; i++)
|
|
{
|
|
var pointer = pointers[i];
|
|
|
|
if (!pointer.active)
|
|
{
|
|
pointer.touchstart(changedTouch, time);
|
|
this.activePointer = pointer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Called by the main update loop when a Touch Move Event is received.
|
|
*
|
|
* @method Phaser.Input.InputManager#updatePointer
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {TouchEvent} event - The native DOM event to be processed.
|
|
* @param {number} time - The time stamp value of this game step.
|
|
*/
|
|
updatePointer: function (event, time)
|
|
{
|
|
var pointers = this.pointers;
|
|
|
|
for (var c = 0; c < event.changedTouches.length; c++)
|
|
{
|
|
var changedTouch = event.changedTouches[c];
|
|
|
|
for (var i = 1; i < this.pointersTotal; i++)
|
|
{
|
|
var pointer = pointers[i];
|
|
|
|
if (pointer.active && pointer.identifier === changedTouch.identifier)
|
|
{
|
|
pointer.touchmove(changedTouch, time);
|
|
this.activePointer = pointer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// For touch end its a list of the touch points that have been removed from the surface
|
|
// https://developer.mozilla.org/en-US/docs/DOM/TouchList
|
|
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
|
|
|
|
/**
|
|
* Called by the main update loop when a Touch End Event is received.
|
|
*
|
|
* @method Phaser.Input.InputManager#stopPointer
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {TouchEvent} event - The native DOM event to be processed.
|
|
* @param {number} time - The time stamp value of this game step.
|
|
*/
|
|
stopPointer: function (event, time)
|
|
{
|
|
var pointers = this.pointers;
|
|
|
|
for (var c = 0; c < event.changedTouches.length; c++)
|
|
{
|
|
var changedTouch = event.changedTouches[c];
|
|
|
|
for (var i = 1; i < this.pointersTotal; i++)
|
|
{
|
|
var pointer = pointers[i];
|
|
|
|
if (pointer.active && pointer.identifier === changedTouch.identifier)
|
|
{
|
|
pointer.touchend(changedTouch, time);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Adds new Pointer objects to the Input Manager.
|
|
*
|
|
* By default Phaser creates 2 pointer objects: `mousePointer` and `pointer1`.
|
|
*
|
|
* You can create more either by calling this method, or by setting the `input.activePointers` property
|
|
* in the Game Config.
|
|
*
|
|
* The first 10 pointers are available via the `InputPlugin.pointerX` properties, once they have been added
|
|
* via this method.
|
|
*
|
|
* @method Phaser.Input.InputManager#addPointer
|
|
* @since 3.10.0
|
|
*
|
|
* @param {integer} [quantity=1] The number of new Pointers to create.
|
|
*
|
|
* @return {Phaser.Input.Pointer[]} An array containing all of the new Pointer objects that were created.
|
|
*/
|
|
addPointer: function (quantity)
|
|
{
|
|
if (quantity === undefined) { quantity = 1; }
|
|
|
|
var output = [];
|
|
|
|
for (var i = 0; i < quantity; i++)
|
|
{
|
|
var id = this.pointers.length;
|
|
|
|
var pointer = new Pointer(this, id);
|
|
|
|
this.pointers.push(pointer);
|
|
|
|
this.pointersTotal++;
|
|
|
|
output.push(pointer);
|
|
}
|
|
|
|
return output;
|
|
},
|
|
|
|
/**
|
|
* Process any pending DOM callbacks.
|
|
*
|
|
* @method Phaser.Input.InputManager#processDomCallbacks
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {array} once - The isOnce callbacks to invoke.
|
|
* @param {array} every - The every frame callbacks to invoke.
|
|
* @param {any} event - The native DOM event that is passed to the callbacks.
|
|
*
|
|
* @return {boolean} `true` if there are callbacks still in the list, otherwise `false`.
|
|
*/
|
|
processDomCallbacks: function (once, every, event)
|
|
{
|
|
var i = 0;
|
|
|
|
for (i = 0; i < once.length; i++)
|
|
{
|
|
once[i](event);
|
|
}
|
|
|
|
for (i = 0; i < every.length; i++)
|
|
{
|
|
every[i](event);
|
|
}
|
|
|
|
once = [];
|
|
|
|
return (every.length > 0);
|
|
},
|
|
|
|
/**
|
|
* Queues a touch start event, as passed in by the TouchManager.
|
|
* Also dispatches any DOM callbacks for this event.
|
|
*
|
|
* @method Phaser.Input.InputManager#queueTouchStart
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {TouchEvent} event - The native DOM Touch event.
|
|
*/
|
|
queueTouchStart: function (event)
|
|
{
|
|
this.queue.push(CONST.TOUCH_START, event);
|
|
|
|
if (this._hasDownCallback)
|
|
{
|
|
var callbacks = this.domCallbacks;
|
|
|
|
this._hasDownCallback = this.processDomCallbacks(callbacks.downOnce, callbacks.down, event);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Queues a touch move event, as passed in by the TouchManager.
|
|
* Also dispatches any DOM callbacks for this event.
|
|
*
|
|
* @method Phaser.Input.InputManager#queueTouchMove
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {TouchEvent} event - The native DOM Touch event.
|
|
*/
|
|
queueTouchMove: function (event)
|
|
{
|
|
this.queue.push(CONST.TOUCH_MOVE, event);
|
|
|
|
if (this._hasMoveCallback)
|
|
{
|
|
var callbacks = this.domCallbacks;
|
|
|
|
this._hasMoveCallback = this.processDomCallbacks(callbacks.moveOnce, callbacks.move, event);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Queues a touch end event, as passed in by the TouchManager.
|
|
* Also dispatches any DOM callbacks for this event.
|
|
*
|
|
* @method Phaser.Input.InputManager#queueTouchEnd
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {TouchEvent} event - The native DOM Touch event.
|
|
*/
|
|
queueTouchEnd: function (event)
|
|
{
|
|
this.queue.push(CONST.TOUCH_END, event);
|
|
|
|
if (this._hasUpCallback)
|
|
{
|
|
var callbacks = this.domCallbacks;
|
|
|
|
this._hasUpCallback = this.processDomCallbacks(callbacks.upOnce, callbacks.up, event);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Queues a mouse down event, as passed in by the MouseManager.
|
|
* Also dispatches any DOM callbacks for this event.
|
|
*
|
|
* @method Phaser.Input.InputManager#queueMouseDown
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {MouseEvent} event - The native DOM Mouse event.
|
|
*/
|
|
queueMouseDown: function (event)
|
|
{
|
|
this.queue.push(CONST.MOUSE_DOWN, event);
|
|
|
|
if (this._hasDownCallback)
|
|
{
|
|
var callbacks = this.domCallbacks;
|
|
|
|
this._hasDownCallback = this.processDomCallbacks(callbacks.downOnce, callbacks.down, event);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Queues a mouse move event, as passed in by the MouseManager.
|
|
* Also dispatches any DOM callbacks for this event.
|
|
*
|
|
* @method Phaser.Input.InputManager#queueMouseMove
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {MouseEvent} event - The native DOM Mouse event.
|
|
*/
|
|
queueMouseMove: function (event)
|
|
{
|
|
this.queue.push(CONST.MOUSE_MOVE, event);
|
|
|
|
if (this._hasMoveCallback)
|
|
{
|
|
var callbacks = this.domCallbacks;
|
|
|
|
this._hasMoveCallback = this.processDomCallbacks(callbacks.moveOnce, callbacks.move, event);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Queues a mouse up event, as passed in by the MouseManager.
|
|
* Also dispatches any DOM callbacks for this event.
|
|
*
|
|
* @method Phaser.Input.InputManager#queueMouseUp
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {MouseEvent} event - The native DOM Mouse event.
|
|
*/
|
|
queueMouseUp: function (event)
|
|
{
|
|
this.queue.push(CONST.MOUSE_UP, event);
|
|
|
|
if (this._hasUpCallback)
|
|
{
|
|
var callbacks = this.domCallbacks;
|
|
|
|
this._hasUpCallback = this.processDomCallbacks(callbacks.upOnce, callbacks.up, event);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Adds a callback to be invoked whenever the native DOM `mouseup` or `touchend` events are received.
|
|
* By setting the `isOnce` argument you can control if the callback is called once,
|
|
* or every time the DOM event occurs.
|
|
*
|
|
* Callbacks passed to this method are invoked _immediately_ when the DOM event happens,
|
|
* within the scope of the DOM event handler. Therefore, they are considered as 'native'
|
|
* from the perspective of the browser. This means they can be used for tasks such as
|
|
* opening new browser windows, or anything which explicitly requires user input to activate.
|
|
* However, as a result of this, they come with their own risks, and as such should not be used
|
|
* for general game input, but instead be reserved for special circumstances.
|
|
*
|
|
* If all you're trying to do is execute a callback when a pointer is released, then
|
|
* please use the internal Input event system instead.
|
|
*
|
|
* Please understand that these callbacks are invoked when the browser feels like doing so,
|
|
* which may be entirely out of the normal flow of the Phaser Game Loop. Therefore, you should absolutely keep
|
|
* Phaser related operations to a minimum in these callbacks. For example, don't destroy Game Objects,
|
|
* change Scenes or manipulate internal systems, otherwise you run a very real risk of creating
|
|
* heisenbugs (https://en.wikipedia.org/wiki/Heisenbug) that prove a challenge to reproduce, never mind
|
|
* solve.
|
|
*
|
|
* @method Phaser.Input.InputManager#addUpCallback
|
|
* @since 3.10.0
|
|
*
|
|
* @param {function} callback - The callback to be invoked on this dom event.
|
|
* @param {boolean} [isOnce=true] - `true` if the callback will only be invoked once, `false` to call every time this event happens.
|
|
*
|
|
* @return {this} The Input Manager.
|
|
*/
|
|
addUpCallback: function (callback, isOnce)
|
|
{
|
|
if (isOnce === undefined) { isOnce = true; }
|
|
|
|
if (isOnce)
|
|
{
|
|
this.domCallbacks.upOnce.push(callback);
|
|
}
|
|
else
|
|
{
|
|
this.domCallbacks.up.push(callback);
|
|
}
|
|
|
|
this._hasUpCallback = true;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Adds a callback to be invoked whenever the native DOM `mousedown` or `touchstart` events are received.
|
|
* By setting the `isOnce` argument you can control if the callback is called once,
|
|
* or every time the DOM event occurs.
|
|
*
|
|
* Callbacks passed to this method are invoked _immediately_ when the DOM event happens,
|
|
* within the scope of the DOM event handler. Therefore, they are considered as 'native'
|
|
* from the perspective of the browser. This means they can be used for tasks such as
|
|
* opening new browser windows, or anything which explicitly requires user input to activate.
|
|
* However, as a result of this, they come with their own risks, and as such should not be used
|
|
* for general game input, but instead be reserved for special circumstances.
|
|
*
|
|
* If all you're trying to do is execute a callback when a pointer is down, then
|
|
* please use the internal Input event system instead.
|
|
*
|
|
* Please understand that these callbacks are invoked when the browser feels like doing so,
|
|
* which may be entirely out of the normal flow of the Phaser Game Loop. Therefore, you should absolutely keep
|
|
* Phaser related operations to a minimum in these callbacks. For example, don't destroy Game Objects,
|
|
* change Scenes or manipulate internal systems, otherwise you run a very real risk of creating
|
|
* heisenbugs (https://en.wikipedia.org/wiki/Heisenbug) that prove a challenge to reproduce, never mind
|
|
* solve.
|
|
*
|
|
* @method Phaser.Input.InputManager#addDownCallback
|
|
* @since 3.10.0
|
|
*
|
|
* @param {function} callback - The callback to be invoked on this dom event.
|
|
* @param {boolean} [isOnce=true] - `true` if the callback will only be invoked once, `false` to call every time this event happens.
|
|
*
|
|
* @return {this} The Input Manager.
|
|
*/
|
|
addDownCallback: function (callback, isOnce)
|
|
{
|
|
if (isOnce === undefined) { isOnce = true; }
|
|
|
|
if (isOnce)
|
|
{
|
|
this.domCallbacks.downOnce.push(callback);
|
|
}
|
|
else
|
|
{
|
|
this.domCallbacks.down.push(callback);
|
|
}
|
|
|
|
this._hasDownCallback = true;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Adds a callback to be invoked whenever the native DOM `mousemove` or `touchmove` events are received.
|
|
* By setting the `isOnce` argument you can control if the callback is called once,
|
|
* or every time the DOM event occurs.
|
|
*
|
|
* Callbacks passed to this method are invoked _immediately_ when the DOM event happens,
|
|
* within the scope of the DOM event handler. Therefore, they are considered as 'native'
|
|
* from the perspective of the browser. This means they can be used for tasks such as
|
|
* opening new browser windows, or anything which explicitly requires user input to activate.
|
|
* However, as a result of this, they come with their own risks, and as such should not be used
|
|
* for general game input, but instead be reserved for special circumstances.
|
|
*
|
|
* If all you're trying to do is execute a callback when a pointer is moved, then
|
|
* please use the internal Input event system instead.
|
|
*
|
|
* Please understand that these callbacks are invoked when the browser feels like doing so,
|
|
* which may be entirely out of the normal flow of the Phaser Game Loop. Therefore, you should absolutely keep
|
|
* Phaser related operations to a minimum in these callbacks. For example, don't destroy Game Objects,
|
|
* change Scenes or manipulate internal systems, otherwise you run a very real risk of creating
|
|
* heisenbugs (https://en.wikipedia.org/wiki/Heisenbug) that prove a challenge to reproduce, never mind
|
|
* solve.
|
|
*
|
|
* @method Phaser.Input.InputManager#addMoveCallback
|
|
* @since 3.10.0
|
|
*
|
|
* @param {function} callback - The callback to be invoked on this dom event.
|
|
* @param {boolean} [isOnce=false] - `true` if the callback will only be invoked once, `false` to call every time this event happens.
|
|
*
|
|
* @return {this} The Input Manager.
|
|
*/
|
|
addMoveCallback: function (callback, isOnce)
|
|
{
|
|
if (isOnce === undefined) { isOnce = false; }
|
|
|
|
if (isOnce)
|
|
{
|
|
this.domCallbacks.moveOnce.push(callback);
|
|
}
|
|
else
|
|
{
|
|
this.domCallbacks.move.push(callback);
|
|
}
|
|
|
|
this._hasMoveCallback = true;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Checks if the given Game Object should be considered as a candidate for input or not.
|
|
*
|
|
* Checks if the Game Object has an input component that is enabled, that it will render,
|
|
* and finally, if it has a parent, that the parent parent, or any ancestor, is visible or not.
|
|
*
|
|
* @method Phaser.Input.InputManager#inputCandidate
|
|
* @private
|
|
* @since 3.10.0
|
|
*
|
|
* @param {Phaser.GameObjects.GameObject} gameObject - The Game Object to test.
|
|
*
|
|
* @return {boolean} `true` if the Game Object should be considered for input, otherwise `false`.
|
|
*/
|
|
inputCandidate: function (gameObject)
|
|
{
|
|
var input = gameObject.input;
|
|
|
|
if (!input || !input.enabled || !gameObject.willRender())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var visible = true;
|
|
var parent = gameObject.parentContainer;
|
|
|
|
if (parent)
|
|
{
|
|
do
|
|
{
|
|
if (!parent.visible)
|
|
{
|
|
visible = false;
|
|
break;
|
|
}
|
|
|
|
parent = parent.parentContainer;
|
|
|
|
} while (parent);
|
|
}
|
|
|
|
return visible;
|
|
},
|
|
|
|
/**
|
|
* Performs a hit test using the given Pointer and camera, against an array of interactive Game Objects.
|
|
*
|
|
* The Game Objects are culled against the camera, and then the coordinates are translated into the local camera space
|
|
* and used to determine if they fall within the remaining Game Objects hit areas or not.
|
|
*
|
|
* If nothing is matched an empty array is returned.
|
|
*
|
|
* This method is called automatically by InputPlugin.hitTestPointer and doesn't usually need to be invoked directly.
|
|
*
|
|
* @method Phaser.Input.InputManager#hitTest
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Input.Pointer} pointer - The Pointer to test against.
|
|
* @param {array} gameObjects - An array of interactive Game Objects to check.
|
|
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera which is being tested against.
|
|
* @param {array} [output] - An array to store the results in. If not given, a new empty array is created.
|
|
*
|
|
* @return {array} An array of the Game Objects that were hit during this hit test.
|
|
*/
|
|
hitTest: function (pointer, gameObjects, camera, output)
|
|
{
|
|
if (output === undefined) { output = this._tempHitTest; }
|
|
|
|
var tempPoint = this._tempPoint;
|
|
var cameraW = camera.width;
|
|
var cameraH = camera.height;
|
|
|
|
output.length = 0;
|
|
|
|
var x = pointer.x;
|
|
var y = pointer.y;
|
|
|
|
if (!(x >= camera.x && y >= camera.y && x <= camera.x + cameraW && y <= camera.y + cameraH))
|
|
{
|
|
return output;
|
|
}
|
|
|
|
// Stores the world point inside of tempPoint
|
|
camera.getWorldPoint(x, y, tempPoint);
|
|
|
|
pointer.worldX = tempPoint.x;
|
|
pointer.worldY = tempPoint.y;
|
|
|
|
var culledGameObjects = camera.cull(gameObjects);
|
|
|
|
var point = { x: 0, y: 0 };
|
|
|
|
var res = this.game.config.resolution;
|
|
|
|
var matrix = this._tempMatrix;
|
|
|
|
for (var i = 0; i < culledGameObjects.length; i++)
|
|
{
|
|
var gameObject = culledGameObjects[i];
|
|
|
|
if (!this.inputCandidate(gameObject))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var px = tempPoint.x * res + (camera.scrollX * gameObject.scrollFactorX) - camera.scrollX;
|
|
var py = tempPoint.y * res + (camera.scrollY * gameObject.scrollFactorY) - camera.scrollY;
|
|
|
|
if (gameObject.parentContainer)
|
|
{
|
|
gameObject.getWorldTransformMatrix(matrix);
|
|
|
|
TransformXY(px, py, matrix.tx, matrix.ty, matrix.rotation, matrix.scaleX, matrix.scaleY, point);
|
|
}
|
|
else
|
|
{
|
|
TransformXY(px, py, gameObject.x, gameObject.y, gameObject.rotation, gameObject.scaleX, gameObject.scaleY, point);
|
|
}
|
|
|
|
if (this.pointWithinHitArea(gameObject, point.x, point.y))
|
|
{
|
|
output.push(gameObject);
|
|
}
|
|
}
|
|
|
|
return output;
|
|
},
|
|
|
|
/**
|
|
* Checks if the given x and y coordinate are within the hit area of the Game Object.
|
|
*
|
|
* This method assumes that the coordinate values have already been translated into the space of the Game Object.
|
|
*
|
|
* If the coordinates are within the hit area they are set into the Game Objects Input `localX` and `localY` properties.
|
|
*
|
|
* @method Phaser.Input.InputManager#pointWithinHitArea
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.GameObjects.GameObject} gameObject - The interactive Game Object to check against.
|
|
* @param {number} x - The translated x coordinate for the hit test.
|
|
* @param {number} y - The translated y coordinate for the hit test.
|
|
*
|
|
* @return {boolean} `true` if the coordinates were inside the Game Objects hit area, otherwise `false`.
|
|
*/
|
|
pointWithinHitArea: function (gameObject, x, y)
|
|
{
|
|
// Normalize the origin
|
|
x += gameObject.displayOriginX;
|
|
y += gameObject.displayOriginY;
|
|
|
|
var input = gameObject.input;
|
|
|
|
if (input && input.hitAreaCallback(input.hitArea, x, y, gameObject))
|
|
{
|
|
input.localX = x;
|
|
input.localY = y;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Checks if the given x and y coordinate are within the hit area of the Interactive Object.
|
|
*
|
|
* This method assumes that the coordinate values have already been translated into the space of the Interactive Object.
|
|
*
|
|
* If the coordinates are within the hit area they are set into the Interactive Objects Input `localX` and `localY` properties.
|
|
*
|
|
* @method Phaser.Input.InputManager#pointWithinInteractiveObject
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Input.InteractiveObject} object - The Interactive Object to check against.
|
|
* @param {number} x - The translated x coordinate for the hit test.
|
|
* @param {number} y - The translated y coordinate for the hit test.
|
|
*
|
|
* @return {boolean} `true` if the coordinates were inside the Game Objects hit area, otherwise `false`.
|
|
*/
|
|
pointWithinInteractiveObject: function (object, x, y)
|
|
{
|
|
if (!object.hitArea)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Normalize the origin
|
|
x += object.gameObject.displayOriginX;
|
|
y += object.gameObject.displayOriginY;
|
|
|
|
object.localX = x;
|
|
object.localY = y;
|
|
|
|
return object.hitAreaCallback(object.hitArea, x, y, object);
|
|
},
|
|
|
|
/**
|
|
* Transforms the pageX and pageY values of a Pointer into the scaled coordinate space of the Input Manager.
|
|
*
|
|
* @method Phaser.Input.InputManager#transformPointer
|
|
* @since 3.10.0
|
|
*
|
|
* @param {Phaser.Input.Pointer} pointer - The Pointer to transform the values for.
|
|
*
|
|
* @return {number} The translated value.
|
|
*/
|
|
transformPointer: function (pointer, pageX, pageY)
|
|
{
|
|
pointer.x = (pageX - this.bounds.left) * this.scale.x;
|
|
pointer.y = (pageY - this.bounds.top) * this.scale.y;
|
|
},
|
|
|
|
/**
|
|
* Transforms the pageX value into the scaled coordinate space of the Input Manager.
|
|
*
|
|
* @method Phaser.Input.InputManager#transformX
|
|
* @since 3.0.0
|
|
*
|
|
* @param {number} pageX - The DOM pageX value.
|
|
*
|
|
* @return {number} The translated value.
|
|
*/
|
|
transformX: function (pageX)
|
|
{
|
|
return (pageX - this.bounds.left) * this.scale.x;
|
|
},
|
|
|
|
/**
|
|
* Transforms the pageY value into the scaled coordinate space of the Input Manager.
|
|
*
|
|
* @method Phaser.Input.InputManager#transformY
|
|
* @since 3.0.0
|
|
*
|
|
* @param {number} pageY - The DOM pageY value.
|
|
*
|
|
* @return {number} The translated value.
|
|
*/
|
|
transformY: function (pageY)
|
|
{
|
|
return (pageY - this.bounds.top) * this.scale.y;
|
|
},
|
|
|
|
/**
|
|
* Returns the left offset of the Input bounds.
|
|
*
|
|
* @method Phaser.Input.InputManager#getOffsetX
|
|
* @since 3.0.0
|
|
*
|
|
* @return {number} The left bounds value.
|
|
*/
|
|
getOffsetX: function ()
|
|
{
|
|
return this.bounds.left;
|
|
},
|
|
|
|
/**
|
|
* Returns the top offset of the Input bounds.
|
|
*
|
|
* @method Phaser.Input.InputManager#getOffsetY
|
|
* @since 3.0.0
|
|
*
|
|
* @return {number} The top bounds value.
|
|
*/
|
|
getOffsetY: function ()
|
|
{
|
|
return this.bounds.top;
|
|
},
|
|
|
|
/**
|
|
* Returns the horizontal Input Scale value.
|
|
*
|
|
* @method Phaser.Input.InputManager#getScaleX
|
|
* @since 3.0.0
|
|
*
|
|
* @return {number} The horizontal scale factor of the input.
|
|
*/
|
|
getScaleX: function ()
|
|
{
|
|
return this.game.config.width / this.bounds.width;
|
|
},
|
|
|
|
/**
|
|
* Returns the vertical Input Scale value.
|
|
*
|
|
* @method Phaser.Input.InputManager#getScaleY
|
|
* @since 3.0.0
|
|
*
|
|
* @return {number} The vertical scale factor of the input.
|
|
*/
|
|
getScaleY: function ()
|
|
{
|
|
return this.game.config.height / this.bounds.height;
|
|
},
|
|
|
|
/**
|
|
* Destroys the Input Manager and all of its systems.
|
|
*
|
|
* There is no way to recover from doing this.
|
|
*
|
|
* @method Phaser.Input.InputManager#destroy
|
|
* @since 3.0.0
|
|
*/
|
|
destroy: function ()
|
|
{
|
|
this.events.removeAllListeners();
|
|
|
|
if (this.keyboard)
|
|
{
|
|
this.keyboard.destroy();
|
|
}
|
|
|
|
if (this.mouse)
|
|
{
|
|
this.mouse.destroy();
|
|
}
|
|
|
|
if (this.touch)
|
|
{
|
|
this.touch.destroy();
|
|
}
|
|
|
|
if (this.gamepad)
|
|
{
|
|
this.gamepad.destroy();
|
|
}
|
|
|
|
for (var i = 0; i < this.pointers.length; i++)
|
|
{
|
|
this.pointers[i].destroy();
|
|
}
|
|
|
|
this.domCallbacks = {};
|
|
this.pointers = [];
|
|
this.queue = [];
|
|
this._tempHitTest = [];
|
|
this._tempMatrix.destroy();
|
|
this.canvas = null;
|
|
this.game = null;
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = InputManager;
|