Refactored Input Manager as it was getting too large for a single file class. Split up into Scene and Global Managers, broke all functions off into single files and started work on the new multi-pointer flow.

This commit is contained in:
photonstorm 2017-07-20 17:10:12 +01:00
parent c848d18000
commit c6a2edd855
46 changed files with 650 additions and 512 deletions

View file

@ -14,7 +14,7 @@ var AnimationManager = require('../animation/manager/AnimationManager');
var CreateRenderer = require('./CreateRenderer'); var CreateRenderer = require('./CreateRenderer');
var Data = require('../plugins/Data'); var Data = require('../plugins/Data');
var GlobalCache = require('../cache/GlobalCache'); var GlobalCache = require('../cache/GlobalCache');
var GlobalInputManager = require('../input/GlobalInputManager'); var GlobalInputManager = require('../input/global/GlobalInputManager');
var GlobalSceneManager = require('../scene/GlobalSceneManager'); var GlobalSceneManager = require('../scene/GlobalSceneManager');
var TextureManager = require('../textures/TextureManager'); var TextureManager = require('../textures/TextureManager');
var TimeStep = require('./TimeStep'); var TimeStep = require('./TimeStep');

View file

@ -1,4 +1,4 @@
var CHECKSUM = { var CHECKSUM = {
build: '668ff4c0-6d49-11e7-a9ff-d120a05f4baf' build: 'a59b1ca0-6d65-11e7-9e25-f9ff7de73fa8'
}; };
module.exports = CHECKSUM; module.exports = CHECKSUM;

View file

@ -20,6 +20,9 @@ var InteractiveObject = function (gameObject, hitArea, hitAreaCallback)
isDown: false, isDown: false,
isDragged: false, isDragged: false,
pointersOver: [],
pointersDown: [],
callbackContext: gameObject, callbackContext: gameObject,
onDown: NOOP, onDown: NOOP,

View file

@ -1,8 +0,0 @@
var INPUT_CONST = {
ALL: 0,
TOP: 1
};
module.exports = INPUT_CONST;

View file

@ -1,16 +1,16 @@
// Phaser.Input.GlobalInputManager // Phaser.Input.GlobalInputManager
var Class = require('../utils/Class'); var Class = require('../../utils/Class');
var EventDispatcher = require('../events/EventDispatcher'); var EventDispatcher = require('../../events/EventDispatcher');
var GetTransformedPoint = require('./components/GetTransformedPoint'); var GetTransformedPoint = require('./components/GetTransformedPoint');
var Keyboard = require('./keyboard/KeyboardManager'); var Keyboard = require('../keyboard/KeyboardManager');
var Mouse = require('./mouse/MouseManager'); var Mouse = require('../mouse/MouseManager');
var MouseEvent = require('./mouse/events/'); var MouseEvent = require('../mouse/events/');
var Pointer = require('./Pointer'); var Pointer = require('../Pointer');
var PointScreenToWorldHitTest = require('./components/PointScreenToWorldHitTest'); var PointScreenToWorldHitTest = require('./components/PointScreenToWorldHitTest');
var HitTest = require('./components/HitTest'); var HitTest = require('./components/HitTest');
var PointWithinGameObject = require('./components/PointWithinGameObject'); var PointWithinGameObject = require('./components/PointWithinGameObject');
var TransformMatrix = require('../gameobjects/components/TransformMatrix'); var TransformMatrix = require('../../gameobjects/components/TransformMatrix');
var GlobalInputManager = new Class({ var GlobalInputManager = new Class({

View file

@ -2,10 +2,6 @@
module.exports = { module.exports = {
// CONSTs (makes them visible under Phaser.Input)
RETURN_ALL: 0,
RETURN_TOP: 1,
Keyboard: require('./keyboard'), Keyboard: require('./keyboard'),
Mouse: require('./mouse') Mouse: require('./mouse')

View file

@ -0,0 +1,113 @@
var Class = require('../../utils/Class');
// Drag Events
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
var SceneInputManager = new Class({
initialize:
function SceneInputManager (scene, game)
{
// The Scene that owns this plugin
this.scene = scene;
// GlobalInputManager
this.manager = game.input;
// A reference to this.scene.sys.displayList (set in boot)
this.displayList;
// A reference to the this.scene.sys.cameras.cameras array (set in boot)
this.cameras;
// Should use Scene event dispatcher?
this.events = this.manager.events;
// this.keyboard = this.manager.keyboard;
// this.mouse = this.manager.mouse;
// Only fire *callbacks* on the top-most Game Object in the display list (emulating DOM behavior)
// and ignoring any GOs below it, or call them all?
this.topOnly = true;
// How often should the pointer input be checked?
// Time given in ms
// Pointer will *always* be checked if it has been moved by the user.
// This controls how often it will be polled if it hasn't been moved.
// Set to 0 to poll constantly. Set to -1 to only poll on user movement.
this.pollRate = -1;
this._pollTimer = 0;
this._size = 0;
// All list of all Interactive Objects
this._list = [];
// Objects waiting to be inserted or removed from the active list
this._pendingInsertion = [];
this._pendingRemoval = [];
// A list of Interactive Objects which are *currently* below a pointer (any pointer) during the previous frame
this._over = [];
// Only Game Objects which have been flagged as draggable are added to this array
this._draggable = [];
this._validTypes = [ 'onDown', 'onUp', 'onOver', 'onOut' ];
},
boot: require('./components/Boot'),
begin: require('./components/Begin'),
update: require('./components/Update'),
hitTestPointer: require('./components/HitTestPointer'),
setpollRate: require('./components/SetPollRate'),
setpollAlways: require('./components/SetPollAlways'),
setpollOnMove: require('./components/SetPollOnMove'),
setHitArea: require('./components/SetHitArea'),
setHitAreaCircle: require('./components/SetHitAreaCircle'),
setHitAreaEllipse: require('./components/SetHitAreaEllipse'),
setHitAreaFromTexture: require('./components/SetHitAreaFromTexture'),
setHitAreaRectangle: require('./components/SetHitAreaRectangle'),
setHitAreaTriangle: require('./components/SetHitAreaTriangle'),
setCallback: require('./components/SetCallback'),
setCallbacks: require('./components/SetCallbacks'),
setOnDownCallback: require('./components/SetOnDownCallback'),
setOnOutCallback: require('./components/SetOnOutCallback'),
setOnOverCallback: require('./components/SetOnOverCallback'),
setOnUpCallback: require('./components/SetOnUpCallback'),
queueForInsertion: require('./components/QueueForInsertion'),
queueForRemoval: require('./components/QueueForRemoval'),
// Scene that owns this is shutting down
shutdown: function ()
{
this._list = [];
this._over = [];
this._draggable = [];
this._pendingRemoval = [];
this._pendingInsertion = [];
},
// Game level nuke
destroy: function ()
{
this.shutdown();
this.scene = undefined;
this.cameras = undefined;
this.manager = undefined;
this.events = undefined;
this.keyboard = undefined;
this.mouse = undefined;
}
});
module.exports = SceneInputManager;

View file

@ -0,0 +1,48 @@
var Begin = function ()
{
var toRemove = this._pendingRemoval.length;
var toInsert = this._pendingInsertion.length;
if (toRemove === 0 && toInsert === 0)
{
// Quick bail
return;
}
var i;
var gameObject;
// Delete old gameObjects
for (i = 0; i < toRemove; i++)
{
gameObject = this._pendingRemoval[i];
var index = this._list.indexOf(gameObject);
if (index > -1)
{
this._list.splice(index, 1);
gameObject.input = null;
}
}
// Move pending to active (can swap for concat splice if we don't need anything extra here)
for (i = 0; i < toInsert; i++)
{
gameObject = this._pendingInsertion[i];
// Swap for Input Enabled Object
this._list.push(gameObject);
}
this._size = this._list.length;
// Clear the lists
this._pendingRemoval.length = 0;
this._pendingInsertion.length = 0;
};
module.exports = Begin;

View file

@ -0,0 +1,8 @@
var Boot = function ()
{
this.cameras = this.scene.sys.cameras.cameras;
this.displayList = this.scene.sys.displayList;
};
module.exports = Boot;

View file

@ -0,0 +1,6 @@
var Disable = function (gameObject)
{
gameObject.input.enabled = false;
};
module.exports = Disable;

View file

@ -0,0 +1,17 @@
var Enable = function (gameObject, shape, callback)
{
if (gameObject.input)
{
// If it is already has an InteractiveObject then just enable it and return
gameObject.input.enabled = true;
}
else
{
// Create an InteractiveObject and enable it
this.setHitArea(gameObject, shape, callback);
}
return this;
};
module.exports = Enable;

View file

@ -0,0 +1,20 @@
var HitTestPointer = function (pointer)
{
var output = [];
// Get a list of all objects that can be seen by all the cameras in the scene and store in 'tested' array.
// All objects in this array are input enabled, as checked by the hitTest function, so we don't need to check later on as well.
for (var i = 0; i < this.cameras.length; i++)
{
var camera = this.cameras[i];
if (camera.inputEnabled)
{
output = output.concat(this.manager.hitTest(this._list, pointer.x, pointer.y, camera));
}
}
return output;
};
module.exports = HitTestPointer;

View file

@ -0,0 +1,12 @@
// Queues a Game Object for insertion into this Input Manager on the next update.
var QueueForInsertion = function (child)
{
if (this._pendingInsertion.indexOf(child) === -1 && this._list.indexOf(child) === -1)
{
this._pendingInsertion.push(child);
}
return this;
};
module.exports = QueueForInsertion;

View file

@ -0,0 +1,9 @@
// Queues a Game Object for removal from this Input Manager on the next update.
var QueueForRemoval = function (child)
{
this._pendingRemoval.push(child);
return this;
};
module.exports = QueueForRemoval;

View file

@ -0,0 +1,31 @@
var SetCallback = function (gameObjects, type, callback, context)
{
if (this._validTypes.indexOf(type) === -1)
{
return this;
}
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
if (gameObject.input)
{
gameObject.input[type] = callback;
if (context)
{
gameObject.input.callbackContext = context;
}
}
}
return this;
};
module.exports = SetCallback;

View file

@ -0,0 +1,26 @@
var SetCallbacks = function (gameObjects, onDown, onUp, onOver, onOut, context)
{
if (onDown)
{
this.setOnDownCallback(gameObjects, onDown, context);
}
if (onUp)
{
this.setOnDownCallback(gameObjects, onUp, context);
}
if (onOver)
{
this.setOnDownCallback(gameObjects, onOver, context);
}
if (onOut)
{
this.setOnDownCallback(gameObjects, onOut, context);
}
return this;
};
module.exports = SetCallbacks;

View file

@ -0,0 +1,40 @@
var SetDraggable = function (gameObjects, value)
{
if (value === undefined) { value = true; }
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
var index = this._draggable.indexOf(gameObject);
if (value)
{
// Make draggable
gameObject.input.draggable = true;
if (index === -1)
{
this._draggable.push(gameObject);
}
}
else
{
// Disable drag
gameObject.input.draggable = false;
if (index === -1)
{
this._draggable.splice(index, 1);
}
}
}
return true;
};
module.exports = SetDraggable;

View file

@ -0,0 +1,27 @@
var InteractiveObject = require('../../InteractiveObject');
var SetHitArea = function (gameObjects, shape, callback)
{
if (shape === undefined)
{
return this.setHitAreaFromTexture(gameObjects);
}
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
gameObject.input = InteractiveObject(gameObject, shape, callback);
this.queueForInsertion(gameObject);
}
return this;
};
module.exports = SetHitArea;

View file

@ -0,0 +1,13 @@
var Circle = require('../../../geom/circle/Circle');
var CircleContains = require('../../../geom/circle/Contains');
var SetHitAreaCircle = function (gameObjects, x, y, radius, callback)
{
if (callback === undefined) { callback = CircleContains; }
var shape = new Circle(x, y, radius);
return this.setHitArea(gameObjects, shape, callback);
};
module.exports = SetHitAreaCircle;

View file

@ -0,0 +1,13 @@
var Ellipse = require('../../../geom/ellipse/Ellipse');
var EllipseContains = require('../../../geom/ellipse/Contains');
var SetHitAreaEllipse = function (gameObjects, x, y, width, height, callback)
{
if (callback === undefined) { callback = EllipseContains; }
var shape = new Ellipse(x, y, width, height);
return this.setHitArea(gameObjects, shape, callback);
};
module.exports = SetHitAreaEllipse;

View file

@ -0,0 +1,29 @@
var InteractiveObject = require('../../InteractiveObject');
var Rectangle = require('../../../geom/rectangle/Rectangle');
var RectangleContains = require('../../../geom/rectangle/Contains');
var SetHitAreaFromTexture = function (gameObjects, callback)
{
if (callback === undefined) { callback = RectangleContains; }
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
if (gameObject.frame)
{
gameObject.input = InteractiveObject(gameObject, new Rectangle(0, 0, gameObject.frame.width, gameObject.frame.height), callback);
this.queueForInsertion(gameObject);
}
}
return this;
};
module.exports = SetHitAreaFromTexture;

View file

@ -0,0 +1,13 @@
var Rectangle = require('../../../geom/rectangle/Rectangle');
var RectangleContains = require('../../../geom/rectangle/Contains');
var SetHitAreaRectangle = function (gameObjects, x, y, width, height, callback)
{
if (callback === undefined) { callback = RectangleContains; }
var shape = new Rectangle(x, y, width, height);
return this.setHitArea(gameObjects, shape, callback);
};
module.exports = SetHitAreaRectangle;

View file

@ -0,0 +1,13 @@
var Triangle = require('../../../geom/triangle/Triangle');
var TriangleContains = require('../../../geom/triangle/Contains');
var SetHitAreaTriangle = function (gameObjects, x1, y1, x2, y2, x3, y3, callback)
{
if (callback === undefined) { callback = TriangleContains; }
var shape = new Triangle(x1, y1, x2, y2, x3, y3);
return this.setHitArea(gameObjects, shape, callback);
};
module.exports = SetHitAreaTriangle;

View file

@ -0,0 +1,6 @@
var SetOnDownCallback = function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onDown', callback, context);
};
module.exports = SetOnDownCallback;

View file

@ -0,0 +1,6 @@
var SetOnOutCallback = function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onOut', callback, context);
};
module.exports = SetOnOutCallback;

View file

@ -0,0 +1,6 @@
var SetOnOverCallback = function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onOver', callback, context);
};
module.exports = SetOnOverCallback;

View file

@ -0,0 +1,6 @@
var SetOnUpCallback = function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onUp', callback, context);
};
module.exports = SetOnUpCallback;

View file

@ -0,0 +1,9 @@
var SetPollAlways = function ()
{
this.pollRate = 0;
this._pollTimer = 0;
return this;
};
module.exports = SetPollAlways;

View file

@ -0,0 +1,9 @@
var SetPollOnMove = function ()
{
this.pollRate = -1;
this._pollTimer = 0;
return this;
};
module.exports = SetPollOnMove;

View file

@ -0,0 +1,9 @@
var SetPollRate = function (value)
{
this.pollRate = value;
this._pollTimer = 0;
return this;
};
module.exports = SetPollRate;

View file

@ -0,0 +1,43 @@
var Update = function (time, delta)
{
if (this._size === 0)
{
return;
}
var pointer = this.manager.activePointer;
var runUpdate = (pointer.dirty || this.pollRate === 0);
if (this.pollRate > -1)
{
this._pollTimer -= delta;
if (this._pollTimer < 0)
{
runUpdate = true;
// Discard timer diff
this._pollTimer = this.pollRate;
}
}
if (runUpdate)
{
var results = this.hitTestPointer(pointer);
this.processOverOutEvents(pointer, results);
if (pointer.justUp || pointer.justDown)
{
this.processUpDownEvents(pointer, results);
}
if (pointer.justMoved)
{
this.processMovementEvents(pointer, results);
}
}
};
module.exports = Update;

View file

@ -17,10 +17,10 @@ var PointerDownEvent = new Class({
this.y = pointer.y; this.y = pointer.y;
// An array of all the game objects the pointer event occurred on // An array of all the game objects the pointer event occurred on
this.gameObjects = gameObjects; this.list = gameObjects;
// A reference to the top-most game object in the list (based on display list order) // A reference to the top-most game object in the list (based on display list order)
this.top = topObject; this.gameObject = topObject;
} }
}); });

View file

@ -17,10 +17,10 @@ var PointerOutEvent = new Class({
this.y = pointer.y; this.y = pointer.y;
// An array of all the game objects the pointer event occurred on // An array of all the game objects the pointer event occurred on
this.gameObjects = gameObjects; this.list = gameObjects;
// A reference to the top-most game object in the list (based on display list order) // A reference to the top-most game object in the list (based on display list order)
this.top = topObject; this.gameObject = topObject;
} }
}); });

View file

@ -17,10 +17,10 @@ var PointerOverEvent = new Class({
this.y = pointer.y; this.y = pointer.y;
// An array of all the game objects the pointer event occurred on // An array of all the game objects the pointer event occurred on
this.gameObjects = gameObjects; this.list = gameObjects;
// A reference to the top-most game object in the list (based on display list order) // A reference to the top-most game object in the list (based on display list order)
this.top = topObject; this.gameObject = topObject;
} }
}); });

View file

@ -17,10 +17,10 @@ var PointerUpEvent = new Class({
this.y = pointer.y; this.y = pointer.y;
// An array of all the game objects the pointer event occurred on // An array of all the game objects the pointer event occurred on
this.gameObjects = gameObjects; this.list = gameObjects;
// A reference to the top-most game object in the list (based on display list order) // A reference to the top-most game object in the list (based on display list order)
this.top = topObject; this.gameObject = topObject;
} }
}); });

View file

@ -1,191 +1,50 @@
var Circle = require('../geom/circle/Circle');
var CircleContains = require('../geom/circle/Contains');
var Class = require('../utils/Class'); var Class = require('../utils/Class');
var CONST = require('../input/const'); var SceneInputManager = require('../input/local/SceneInputManager');
var Ellipse = require('../geom/ellipse/Ellipse');
var EllipseContains = require('../geom/ellipse/Contains');
var InputEvent = require('../input/events');
var InteractiveObject = require('../input/InteractiveObject');
var Rectangle = require('../geom/rectangle/Rectangle');
var RectangleContains = require('../geom/rectangle/Contains');
var Triangle = require('../geom/triangle/Triangle');
var TriangleContains = require('../geom/triangle/Contains');
var InputManager = new Class({ var InputManager = new Class({
Extends: SceneInputManager,
initialize: initialize:
function InputManager (scene, game) function InputManager (scene, game)
{ {
// The Scene that owns this plugin SceneInputManager.call(this, scene, game);
this.scene = scene; }
// A reference to this.scene.sys.displayList (set in boot) /*
this.displayList; processOverOutEvents: function (pointer, results)
// A reference to the this.scene.sys.cameras.cameras array (set in boot)
this.cameras;
// GlobalInputManager
this.manager = game.input;
// Should use Scene event dispatcher?
this.events = this.manager.events;
this.keyboard = this.manager.keyboard;
this.mouse = this.manager.mouse;
// Only fire *callbacks* on the top-most Game Object in the display list (emulating DOM behavior)
// and ignoring any GOs below it, or call them all? (TODO: Set via Input config)
this.processOptions = {
up: CONST.TOP,
down: CONST.TOP,
over: CONST.TOP,
out: CONST.TOP
};
// How often should the pointer input be checked?
// Time given in ms
// Pointer will *always* be checked if it has been moved by the user.
// This controls how often it will be polled if it hasn't been moved.
// Set to 0 to poll constantly. Set to -1 to only poll on user movement.
this.pollRate = -1;
this._pollTimer = 0;
this._size = 0;
// All list of all Game Objects that have been input enabled
this._list = [];
// Only those which are currently below a pointer (any pointer)
this._over = [];
// Only Game Objects which have been flagged as draggable are added to this array
this._draggable = [];
// Objects waiting to be inserted or removed from the active list
this._pendingInsertion = [];
this._pendingRemoval = [];
this._validTypes = [ 'onDown', 'onUp', 'onOver', 'onOut' ];
},
boot: function ()
{ {
this.cameras = this.scene.sys.cameras.cameras;
this.displayList = this.scene.sys.displayList;
},
begin: function ()
{
var toRemove = this._pendingRemoval.length;
var toInsert = this._pendingInsertion.length;
if (toRemove === 0 && toInsert === 0)
{
// Quick bail
return;
}
var i;
var gameObject; var gameObject;
// Delete old gameObjects // Go through the results
for (i = 0; i < toRemove; i++) for (var i = 0; i < results.length; i++)
{ {
gameObject = this._pendingRemoval[i]; gameObject = results[i];
var index = this._list.indexOf(gameObject); // Was this GO previously in an 'over' state?
if (index > -1)
// Loop through the tested array and work out which game objects were 'over' previously and which are 'just over' (brand new)
for (i = 0; i < results.length; i++)
{
gameObject = results[i];
if (previouslyOver.indexOf(gameObject) === -1)
{ {
this._list.splice(index, 1); justOver.push(gameObject);
}
gameObject.input = null; else
{
stillOver.push(gameObject);
} }
} }
// Move pending to active (can swap for concat splice if we don't need anything extra here)
for (i = 0; i < toInsert; i++)
{
gameObject = this._pendingInsertion[i];
// Swap for Input Enabled Object
this._list.push(gameObject);
}
this._size = this._list.length;
// Clear the lists
this._pendingRemoval.length = 0;
this._pendingInsertion.length = 0;
},
setPollAlways: function ()
{
this.pollRate = 0;
this._pollTimer = 0;
return this;
},
setPollOnMove: function ()
{
this.pollRate = -1;
this._pollTimer = 0;
return this;
},
setPoll: function (value)
{
this.pollRate = value;
this._pollTimer = 0;
return this;
},
update: function (time, delta)
{
if (this._size === 0)
{
return;
}
var pointer = this.manager.activePointer;
var runUpdate = (pointer.dirty || this.pollRate === 0);
if (this.pollRate > -1)
{
this._pollTimer -= delta;
if (this._pollTimer < 0)
{
runUpdate = true;
// Discard timer diff
this._pollTimer = this.pollRate;
}
}
if (runUpdate)
{
this.hitTestPointer(pointer);
this.processUpDownEvents(pointer);
if (pointer.justMoved)
{
this.processMovementEvents(pointer);
}
}
}, },
*/
/*
hitTestPointer: function (pointer) hitTestPointer: function (pointer)
{ {
var i; var i;
@ -193,7 +52,7 @@ var InputManager = new Class({
var justOut = []; var justOut = [];
var justOver = []; var justOver = [];
var stillOver = []; var stillOver = [];
var currentlyOver = this._over; var previouslyOver = this._over;
var gameObject; var gameObject;
// Get a list of all objects that can be seen by all the cameras in the scene and store in 'tested' array. // Get a list of all objects that can be seen by all the cameras in the scene and store in 'tested' array.
@ -208,25 +67,27 @@ var InputManager = new Class({
} }
} }
// Loop through the tested array and work out which game objects were 'over' previously and which are 'just over' (brand new) // Loop through the tested array and work out which game objects were 'over' previously and which are 'just over' (brand new)
for (i = 0; i < tested.length; i++) for (i = 0; i < tested.length; i++)
{ {
gameObject = tested[i]; gameObject = tested[i];
if (currentlyOver.indexOf(gameObject) !== -1) if (previouslyOver.indexOf(gameObject) === -1)
{
stillOver.push(gameObject);
}
else
{ {
justOver.push(gameObject); justOver.push(gameObject);
} }
else
{
stillOver.push(gameObject);
}
} }
// Loop through the list of 'currently over' objects (from the previous frame) and any missing are now 'just out' // Loop through the list of 'previously over' objects (from the last update) and any missing from it are now 'just out'
for (i = 0; i < currentlyOver.length; i++) for (i = 0; i < previouslyOver.length; i++)
{ {
gameObject = currentlyOver[i]; gameObject = previouslyOver[i];
if (tested.indexOf(gameObject) === -1) if (tested.indexOf(gameObject) === -1)
{ {
@ -238,39 +99,45 @@ var InputManager = new Class({
// Fire a global onOut event that contains all objects that have moved to 'out' status this update // Fire a global onOut event that contains all objects that have moved to 'out' status this update
gameObject = this.getTopInteractiveObject(justOut); if (justOut.length > 0)
this.events.dispatch(new InputEvent.OUT(pointer, gameObject, justOut));
// Call onOut for everything in the justOut array
for (i = 0; i < justOut.length; i++)
{ {
gameObject = justOut[i]; this.sortInteractiveObjects(justOut);
this.gameObjectOnOut(pointer, gameObject); // Call onOut for everything in the justOut array
for (i = 0; i < justOut.length; i++)
if (this.processOptions.out === CONST.TOP)
{ {
break; gameObject = justOut[i];
this.events.dispatch(new InputEvent.OUT(pointer, gameObject, justOut));
this.gameObjectOnOut(pointer, gameObject);
if (this.topOnly)
{
break;
}
} }
} }
// Fire a global onOut event that contains all objects that have moved to 'over' status this update // Fire a global onOut event that contains all objects that have moved to 'over' status this update
gameObject = this.getTopInteractiveObject(justOver); if (justOver.length > 0)
this.events.dispatch(new InputEvent.OVER(pointer, gameObject, justOver));
// Call onOver for everything in the justOver array
for (i = 0; i < justOver.length; i++)
{ {
gameObject = justOver[i]; this.sortInteractiveObjects(justOver);
this.gameObjectOnOver(pointer, gameObject); // Call onOver for everything in the justOver array
for (i = 0; i < justOver.length; i++)
if (this.processOptions.over === CONST.TOP)
{ {
break; gameObject = justOver[i];
this.events.dispatch(new InputEvent.OVER(pointer, gameObject, justOver));
this.gameObjectOnOver(pointer, gameObject);
if (this.topOnly)
{
break;
}
} }
} }
@ -278,14 +145,17 @@ var InputManager = new Class({
this._over = stillOver.concat(justOver); this._over = stillOver.concat(justOver);
// Then sort it into display list order // Then sort it into display list order
this.sortInteractiveObjects(this._over); this._over = this.sortInteractiveObjects(this._over);
}, },
*/
/*
// Given an array of Game Objects, sort the array and return it, // Given an array of Game Objects, sort the array and return it,
// so that the objects are in index order with the lowest at the bottom. // so that the objects are in index order with the lowest at the bottom.
sortInteractiveObjects: function (interactiveObjects) sortInteractiveObjects: function (interactiveObjects)
{ {
this.displayList.depthSort(); this.scene.sys.depthSort();
return interactiveObjects.sort(this.sortIndexHandler.bind(this)); return interactiveObjects.sort(this.sortIndexHandler.bind(this));
}, },
@ -323,44 +193,59 @@ var InputManager = new Class({
// Has it been pressed down or released in this update? // Has it been pressed down or released in this update?
processUpDownEvents: function (pointer) processUpDownEvents: function (pointer)
{ {
// _over is now in display list order, top to bottom // _over is now in display list order, top to bottom, already sorted previously
var i; var i;
var gameObject = this.getTopInteractiveObject(this._over); var gameObject;
var len = this._over.length;
if (pointer.justDown) if (pointer.justDown)
{ {
this.events.dispatch(new InputEvent.DOWN(pointer, gameObject, this._over)); if (len === 0)
if (this.processOptions.down === CONST.TOP)
{ {
this.gameObjectOnDown(pointer, gameObject); // Dispatch global DOWN event, even though nothing was clicked on
this.events.dispatch(new InputEvent.DOWN(pointer));
} }
else else
{ {
for (i = 0; i < this._over.length; i++) for (i = 0; i < len; i++)
{ {
gameObject = this._over[i]; gameObject = this._over[i];
this.events.dispatch(new InputEvent.DOWN(pointer, gameObject, this._over));
this.gameObjectOnDown(pointer, gameObject); this.gameObjectOnDown(pointer, gameObject);
if (this.topOnly)
{
break;
}
} }
} }
} }
else if (pointer.justUp) else if (pointer.justUp)
{ {
this.events.dispatch(new InputEvent.UP(pointer, gameObject, this._over)); if (len === 0)
if (this.processOptions.up === CONST.TOP)
{ {
this.gameObjectOnUp(pointer, gameObject); // Dispatch global UP event, even though nothing was under the pointer when released
this.events.dispatch(new InputEvent.UP(pointer));
} }
else else
{ {
for (i = 0; i < this._over.length; i++) for (i = 0; i < len; i++)
{ {
gameObject = this._over[i]; gameObject = this._over[i];
this.events.dispatch(new InputEvent.UP(pointer, gameObject, this._over));
this.gameObjectOnUp(pointer, gameObject); this.gameObjectOnUp(pointer, gameObject);
if (this.topOnly)
{
break;
}
} }
} }
} }
@ -456,8 +341,6 @@ var InputManager = new Class({
// Don't dispatch if we're dragging the gameObject, as the pointer often gets away from it // Don't dispatch if we're dragging the gameObject, as the pointer often gets away from it
if (!input.isDragged) if (!input.isDragged)
{ {
// this.events.dispatch(new InputEvent.OUT(pointer, gameObject));
input.onOut(gameObject, pointer); input.onOut(gameObject, pointer);
} }
}, },
@ -471,279 +354,11 @@ var InputManager = new Class({
// Don't dispatch if we're dragging the gameObject, as the pointer often gets away from it // Don't dispatch if we're dragging the gameObject, as the pointer often gets away from it
if (!input.isDragged) if (!input.isDragged)
{ {
// this.events.dispatch(new InputEvent.OVER(pointer, gameObject));
input.onOver(gameObject, pointer, input.localX, input.localY); input.onOver(gameObject, pointer, input.localX, input.localY);
} }
}, },
enable: function (gameObject, shape, callback) */
{
if (gameObject.input)
{
// If it is already has an InteractiveObject then just enable it and return
gameObject.input.enabled = true;
}
else
{
// Create an InteractiveObject and enable it
this.setHitArea(gameObject, shape, callback);
}
return this;
},
disable: function (gameObject)
{
gameObject.input.enabled = false;
},
// Queues a Game Object for insertion into this Input Manager on the next update.
queueForInsertion: function (child)
{
if (this._pendingInsertion.indexOf(child) === -1 && this._list.indexOf(child) === -1)
{
this._pendingInsertion.push(child);
}
return this;
},
// Queues a Game Object for removal from this Input Manager on the next update.
queueForRemoval: function (child)
{
this._pendingRemoval.push(child);
return this;
},
// Set Hit Areas
setHitArea: function (gameObjects, shape, callback)
{
if (shape === undefined)
{
return this.setHitAreaFromTexture(gameObjects);
}
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
gameObject.input = InteractiveObject(gameObject, shape, callback);
this.queueForInsertion(gameObject);
}
return this;
},
setHitAreaFromTexture: function (gameObjects, callback)
{
if (callback === undefined) { callback = RectangleContains; }
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
if (gameObject.frame)
{
gameObject.input = InteractiveObject(gameObject, new Rectangle(0, 0, gameObject.frame.width, gameObject.frame.height), callback);
this.queueForInsertion(gameObject);
}
}
return this;
},
setHitAreaRectangle: function (gameObjects, x, y, width, height, callback)
{
if (callback === undefined) { callback = RectangleContains; }
var shape = new Rectangle(x, y, width, height);
return this.setHitArea(gameObjects, shape, callback);
},
setHitAreaCircle: function (gameObjects, x, y, radius, callback)
{
if (callback === undefined) { callback = CircleContains; }
var shape = new Circle(x, y, radius);
return this.setHitArea(gameObjects, shape, callback);
},
setHitAreaEllipse: function (gameObjects, x, y, width, height, callback)
{
if (callback === undefined) { callback = EllipseContains; }
var shape = new Ellipse(x, y, width, height);
return this.setHitArea(gameObjects, shape, callback);
},
setHitAreaTriangle: function (gameObjects, x1, y1, x2, y2, x3, y3, callback)
{
if (callback === undefined) { callback = TriangleContains; }
var shape = new Triangle(x1, y1, x2, y2, x3, y3);
return this.setHitArea(gameObjects, shape, callback);
},
// type = onDown, onUp, onOver, onOut
setOnDownCallback: function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onDown', callback, context);
},
setOnUpCallback: function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onUp', callback, context);
},
setOnOverCallback: function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onOver', callback, context);
},
setOnOutCallback: function (gameObjects, callback, context)
{
return this.setCallback(gameObjects, 'onOut', callback, context);
},
setCallbacks: function (gameObjects, onDown, onUp, onOver, onOut, context)
{
if (onDown)
{
this.setOnDownCallback(gameObjects, onDown, context);
}
if (onUp)
{
this.setOnDownCallback(gameObjects, onUp, context);
}
if (onOver)
{
this.setOnDownCallback(gameObjects, onOver, context);
}
if (onOut)
{
this.setOnDownCallback(gameObjects, onOut, context);
}
return this;
},
setCallback: function (gameObjects, type, callback, context)
{
if (this._validTypes.indexOf(type) === -1)
{
return this;
}
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
if (gameObject.input)
{
gameObject.input[type] = callback;
if (context)
{
gameObject.input.callbackContext = context;
}
}
}
return this;
},
// Drag Events
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
setDraggable: function (gameObjects, value)
{
if (value === undefined) { value = true; }
if (!Array.isArray(gameObjects))
{
gameObjects = [ gameObjects ];
}
for (var i = 0; i < gameObjects.length; i++)
{
var gameObject = gameObjects[i];
var index = this._draggable.indexOf(gameObject);
if (value)
{
// Make draggable
gameObject.input.draggable = true;
if (index === -1)
{
this._draggable.push(gameObject);
}
}
else
{
// Disable drag
gameObject.input.draggable = false;
if (index === -1)
{
this._draggable.splice(index, 1);
}
}
}
return true;
},
// Scene that owns this is shutting down
shutdown: function ()
{
this._list = [];
this._over = [];
this._draggable = [];
this._pendingRemoval = [];
this._pendingInsertion = [];
},
// Game level nuke
destroy: function ()
{
this.shutdown();
this.scene = undefined;
this.cameras = undefined;
this.manager = undefined;
this.events = undefined;
this.keyboard = undefined;
this.mouse = undefined;
}
}); });