phaser/src/gameobjects/GameObject.js

586 lines
19 KiB
JavaScript
Raw Normal View History

2018-02-12 16:01:20 +00:00
/**
* @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');
2018-09-03 12:12:37 +00:00
var ComponentsToJSON = require('./components/ToJSON');
var DataManager = require('../data/DataManager');
var EventEmitter = require('eventemitter3');
2016-12-07 02:28:22 +00:00
2018-01-31 13:54:44 +00:00
/**
2018-02-07 15:27:21 +00:00
* @classdesc
2018-01-31 13:54:44 +00:00
* The base class that all Game Objects extend.
* You don't create GameObjects directly and they cannot be added to the display list.
* Instead, use them as the base for your own custom classes.
*
* @class GameObject
* @memberOf Phaser.GameObjects
2018-03-21 14:09:58 +00:00
* @extends Phaser.Events.EventEmitter
2018-01-31 13:54:44 +00:00
* @constructor
* @since 3.0.0
*
* @param {Phaser.Scene} scene - The Scene to which this Game Object belongs.
* @param {string} type - A textual representation of the type of Game Object, i.e. `sprite`.
*/
var GameObject = new Class({
2016-12-07 02:28:22 +00:00
Extends: EventEmitter,
initialize:
2016-12-07 02:28:22 +00:00
function GameObject (scene, type)
{
EventEmitter.call(this);
2017-09-11 23:28:53 +00:00
/**
* The Scene to which this Game Object belongs.
* Game Objects can only belong to one Scene.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#scene
* @type {Phaser.Scene}
2017-09-13 01:02:49 +00:00
* @protected
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.scene = scene;
2016-12-07 02:28:22 +00:00
2017-09-11 23:28:53 +00:00
/**
* A textual representation of this Game Object, i.e. `sprite`.
* Used internally by Phaser but is available for your own custom classes to populate.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#type
* @type {string}
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.type = type;
2018-04-05 13:56:43 +00:00
/**
* The parent Container of this Game Object, if it has one.
*
* @name Phaser.GameObjects.GameObject#parentContainer
* @type {Phaser.GameObjects.Container}
* @since 3.4.0
*/
this.parentContainer = null;
2017-09-11 23:28:53 +00:00
/**
* The name of this Game Object.
* Empty by default and never populated by Phaser, this is left for developers to use.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#name
* @type {string}
* @default ''
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.name = '';
2017-09-11 23:28:53 +00:00
/**
* The active state of this Game Object.
* A Game Object with an active state of `true` is processed by the Scenes UpdateList, if added to it.
2017-09-13 01:02:49 +00:00
* An active object is one which is having its logic and internal systems updated.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#active
* @type {boolean}
* @default true
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.active = true;
2017-09-11 23:28:53 +00:00
/**
* The Tab Index of the Game Object.
* Reserved for future use by plugins and the Input Manager.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#tabIndex
* @type {integer}
* @default -1
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
2017-05-01 00:27:35 +00:00
this.tabIndex = -1;
2016-12-07 02:28:22 +00:00
2017-09-11 23:28:53 +00:00
/**
* A Data Manager.
* It allows you to store, query and get key/value paired information specific to this Game Object.
* `null` by default. Automatically created if you use `getData` or `setData` or `setDataEnabled`.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#data
* @type {Phaser.Data.DataManager}
* @default null
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.data = null;
2017-09-11 23:28:53 +00:00
/**
2017-09-13 01:02:49 +00:00
* The flags that are compared against `RENDER_MASK` to determine if this Game Object will render or not.
* The bits are 0001 | 0010 | 0100 | 1000 set by the components Visible, Alpha, Transform and Texture respectively.
* If those components are not used by your custom class then you can use this bitmask as you wish.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#renderFlags
* @type {integer}
* @default 15
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.renderFlags = 15;
2017-09-11 23:28:53 +00:00
/**
* A bitmask that controls if this Game Object is drawn by a Camera or not.
2018-06-23 11:26:39 +00:00
* Not usually set directly, instead call `Camera.ignore`, however you can
* set this property directly using the Camera.id property:
*
2018-06-23 11:26:39 +00:00
* @example
* this.cameraFilter |= camera.id
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#cameraFilter
* @type {number}
* @default 0
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
2017-08-15 19:42:04 +00:00
this.cameraFilter = 0;
2017-07-07 17:12:57 +00:00
2017-09-11 23:28:53 +00:00
/**
2017-09-13 01:02:49 +00:00
* If this Game Object is enabled for input then this property will contain an InteractiveObject instance.
* Not usually set directly. Instead call `GameObject.setInteractive()`.
2017-09-11 23:28:53 +00:00
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#input
* @type {?Phaser.Input.InteractiveObject}
* @default null
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.input = null;
2017-09-11 23:28:53 +00:00
/**
* If this Game Object is enabled for physics then this property will contain a reference to a Physics Body.
*
2018-02-01 00:04:45 +00:00
* @name Phaser.GameObjects.GameObject#body
2018-05-12 18:48:15 +00:00
* @type {?(object|Phaser.Physics.Arcade.Body|Phaser.Physics.Impact.Body)}
2018-02-01 00:04:45 +00:00
* @default null
2018-01-31 13:54:44 +00:00
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
this.body = null;
2018-04-13 16:20:46 +00:00
/**
* This Game Object will ignore all calls made to its destroy method if this flag is set to `true`.
* This includes calls that may come from a Group, Container or the Scene itself.
* While it allows you to persist a Game Object across Scenes, please understand you are entirely
* responsible for managing references to and from this Game Object.
*
* @name Phaser.GameObjects.GameObject#ignoreDestroy
* @type {boolean}
* @default false
* @since 3.5.0
2018-04-13 16:20:46 +00:00
*/
this.ignoreDestroy = false;
// Tell the Scene to re-sort the children
scene.sys.queueDepthSort();
scene.sys.events.once('shutdown', this.destroy, this);
},
2016-12-07 02:28:22 +00:00
2017-09-11 23:28:53 +00:00
/**
* Sets the `active` property of this Game Object and returns this Game Object for further chaining.
2017-09-13 01:02:49 +00:00
* A Game Object with its `active` property set to `true` will be updated by the Scenes UpdateList.
2017-09-11 23:28:53 +00:00
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#setActive
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*
* @param {boolean} value - True if this Game Object should be set as active, false if not.
2018-03-19 18:35:08 +00:00
*
* @return {this} This GameObject.
2017-09-11 23:28:53 +00:00
*/
setActive: function (value)
{
this.active = value;
return this;
},
2017-09-11 23:28:53 +00:00
/**
* Sets the `name` property of this Game Object and returns this Game Object for further chaining.
2017-09-13 01:02:49 +00:00
* The `name` property is not populated by Phaser and is presented for your own use.
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#setName
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*
* @param {string} value - The name to be given to this Game Object.
2018-03-19 18:35:08 +00:00
*
* @return {this} This GameObject.
2017-09-11 23:28:53 +00:00
*/
2017-07-27 16:39:46 +00:00
setName: function (value)
{
2017-07-27 16:39:46 +00:00
this.name = value;
return this;
},
2018-01-31 13:54:44 +00:00
/**
* Adds a Data Manager component to this Game Object.
2018-01-31 13:54:44 +00:00
*
* @method Phaser.GameObjects.GameObject#setDataEnabled
* @since 3.0.0
* @see Phaser.Data.DataManager
2018-01-31 13:54:44 +00:00
*
* @return {this} This GameObject.
2018-01-31 13:54:44 +00:00
*/
setDataEnabled: function ()
{
if (!this.data)
{
this.data = new DataManager(this);
}
return this;
},
/**
* Allows you to store a key value pair within this Game Objects Data Manager.
*
* If the Game Object has not been enabled for data (via `setDataEnabled`) then it will be enabled
* before setting the value.
*
* If the key doesn't already exist in the Data Manager then it is created.
*
* ```javascript
* sprite.setData('name', 'Red Gem Stone');
* ```
*
* You can also pass in an object of key value pairs as the first argument:
*
* ```javascript
* sprite.setData({ name: 'Red Gem Stone', level: 2, owner: 'Link', gold: 50 });
* ```
*
* To get a value back again you can call `getData`:
*
* ```javascript
* sprite.getData('gold');
* ```
*
* Or you can access the value directly via the `values` property, where it works like any other variable:
*
* ```javascript
* sprite.data.values.gold += 50;
* ```
*
* When the value is first set, a `setdata` event is emitted from this Game Object.
*
* If the key already exists, a `changedata` event is emitted instead, along an event named after the key.
* For example, if you updated an existing key called `PlayerLives` then it would emit the event `changedata_PlayerLives`.
* These events will be emitted regardless if you use this method to set the value, or the direct `values` setter.
*
* Please note that the data keys are case-sensitive and must be valid JavaScript Object property strings.
* This means the keys `gold` and `Gold` are treated as two unique values within the Data Manager.
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#setData
* @since 3.0.0
*
* @param {(string|object)} key - The key to set the value for. Or an object or key value pairs. If an object the `data` argument is ignored.
* @param {*} data - The value to set for the given key. If an object is provided as the key this argument is ignored.
2018-03-19 18:35:08 +00:00
*
* @return {this} This GameObject.
*/
setData: function (key, value)
{
if (!this.data)
{
this.data = new DataManager(this);
}
this.data.set(key, value);
return this;
},
/**
* Retrieves the value for the given key in this Game Objects Data Manager, or undefined if it doesn't exist.
*
* You can also access values via the `values` object. For example, if you had a key called `gold` you can do either:
*
* ```javascript
* sprite.getData('gold');
* ```
*
* Or access the value directly:
*
* ```javascript
* sprite.data.values.gold;
* ```
*
* You can also pass in an array of keys, in which case an array of values will be returned:
*
* ```javascript
* sprite.getData([ 'gold', 'armor', 'health' ]);
* ```
*
* This approach is useful for destructuring arrays in ES6.
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#getData
* @since 3.0.0
*
* @param {(string|string[])} key - The key of the value to retrieve, or an array of keys.
2018-03-19 18:35:08 +00:00
*
* @return {*} The value belonging to the given key, or an array of values, the order of which will match the input array.
*/
getData: function (key)
{
if (!this.data)
{
this.data = new DataManager(this);
}
return this.data.get(key);
},
2017-09-11 23:28:53 +00:00
/**
* Pass this Game Object to the Input Manager to enable it for Input.
*
* Input works by using hit areas, these are nearly always geometric shapes, such as rectangles or circles, that act as the hit area
* for the Game Object. However, you can provide your own hit area shape and callback, should you wish to handle some more advanced
* input detection.
*
* If no arguments are provided it will try and create a rectangle hit area based on the texture frame the Game Object is using. If
* this isn't a texture-bound object, such as a Graphics or BitmapText object, this will fail, and you'll need to provide a specific
* shape for it to use.
*
* You can also provide an Input Configuration Object as the only argument to this method.
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#setInteractive
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*
* @param {(Phaser.Input.InputConfiguration|any)} [shape] - Either an input configuration object, or a geometric shape that defines the hit area for the Game Object. If not specified a Rectangle will be used.
2018-04-27 11:42:37 +00:00
* @param {HitAreaCallback} [callback] - A callback to be invoked when the Game Object is interacted with. If you provide a shape you must also provide a callback.
* @param {boolean} [dropZone=false] - Should this Game Object be treated as a drop zone target?
2018-03-19 18:35:08 +00:00
*
* @return {this} This GameObject.
2017-09-11 23:28:53 +00:00
*/
setInteractive: function (shape, callback, dropZone)
{
this.scene.sys.input.enable(this, shape, callback, dropZone);
return this;
},
/**
* If this Game Object has previously been enabled for input, this will disable it.
2018-05-12 18:48:15 +00:00
*
* An object that is disabled for input stops processing or being considered for
* input events, but can be turned back on again at any time by simply calling
* `setInteractive()` with no arguments provided.
2018-05-12 18:48:15 +00:00
*
* If want to completely remove interaction from this Game Object then use `removeInteractive` instead.
*
* @method Phaser.GameObjects.GameObject#disableInteractive
* @since 3.7.0
*
* @return {this} This GameObject.
*/
disableInteractive: function ()
{
if (this.input)
{
2018-06-21 12:07:18 +00:00
this.input.enabled = false;
}
return this;
},
/**
2018-08-21 22:48:03 +00:00
* If this Game Object has previously been enabled for input, this will queue it
* for removal, causing it to no longer be interactive. The removal happens on
* the next game step, it is not immediate.
*
* The Interactive Object that was assigned to this Game Object will be destroyed,
* removed from the Input Manager and cleared from this Game Object.
*
* If you wish to re-enable this Game Object at a later date you will need to
2018-07-20 16:49:49 +00:00
* re-create its InteractiveObject by calling `setInteractive` again.
*
* If you wish to only temporarily stop an object from receiving input then use
* `disableInteractive` instead, as that toggles the interactive state, where-as
* this erases it completely.
2018-08-21 22:48:03 +00:00
*
* If you wish to resize a hit area, don't remove and then set it as being
* interactive. Instead, access the hitarea object directly and resize the shape
* being used. I.e.: `sprite.input.hitArea.setSize(width, height)` (assuming the
* shape is a Rectangle, which it is by default.)
*
* @method Phaser.GameObjects.GameObject#removeInteractive
* @since 3.7.0
*
* @return {this} This GameObject.
*/
removeInteractive: function ()
{
this.scene.sys.input.clear(this);
this.input = undefined;
return this;
},
2018-01-31 13:54:44 +00:00
/**
* To be overridden by custom GameObjects. Allows base objects to be used in a Pool.
*
* @method Phaser.GameObjects.GameObject#update
* @since 3.0.0
*
* @param {...*} [args] - args
2018-01-31 13:54:44 +00:00
*/
2017-07-27 16:39:46 +00:00
update: function ()
{
},
2017-09-11 23:28:53 +00:00
/**
* Returns a JSON representation of the Game Object.
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#toJSON
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*
2018-03-19 18:35:08 +00:00
* @return {JSONGameObject} A JSON representation of the Game Object.
2017-09-11 23:28:53 +00:00
*/
toJSON: function ()
{
2018-09-03 12:12:37 +00:00
return ComponentsToJSON(this);
},
2017-09-11 23:28:53 +00:00
/**
* Compares the renderMask with the renderFlags to see if this Game Object will render or not.
* Also checks the Game Object against the given Cameras exclusion list.
2017-09-11 23:28:53 +00:00
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#willRender
* @since 3.0.0
*
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to check against this Game Object.
2017-09-11 23:28:53 +00:00
*
* @return {boolean} True if the Game Object should be rendered, otherwise false.
2017-09-11 23:28:53 +00:00
*/
willRender: function (camera)
2017-07-27 13:22:05 +00:00
{
return !(GameObject.RENDER_MASK !== this.renderFlags || (this.cameraFilter > 0 && (this.cameraFilter & camera.id)));
2017-07-27 13:22:05 +00:00
},
2018-04-12 01:11:17 +00:00
/**
* Returns an array containing the display list index of either this Game Object, or if it has one,
* its parent Container. It then iterates up through all of the parent containers until it hits the
* root of the display list (which is index 0 in the returned array).
2018-05-12 18:48:15 +00:00
*
2018-04-12 01:11:17 +00:00
* Used internally by the InputPlugin but also useful if you wish to find out the display depth of
* this Game Object and all of its ancestors.
*
* @method Phaser.GameObjects.GameObject#getIndexList
* @since 3.4.0
*
* @return {integer[]} An array of display list position indexes.
*/
getIndexList: function ()
{
2018-04-12 01:18:01 +00:00
// eslint-disable-next-line consistent-this
2018-04-12 01:11:17 +00:00
var child = this;
var parent = this.parentContainer;
var indexes = [];
2018-05-12 18:48:15 +00:00
2018-04-12 01:11:17 +00:00
while (parent)
{
// indexes.unshift([parent.getIndex(child), parent.name]);
indexes.unshift(parent.getIndex(child));
child = parent;
if (!parent.parentContainer)
{
break;
}
else
{
parent = parent.parentContainer;
}
}
// indexes.unshift([this.scene.sys.displayList.getIndex(child), 'root']);
indexes.unshift(this.scene.sys.displayList.getIndex(child));
return indexes;
},
2017-09-11 23:28:53 +00:00
/**
2017-09-13 01:02:49 +00:00
* Destroys this Game Object removing it from the Display List and Update List and
* severing all ties to parent resources.
2018-03-19 18:35:08 +00:00
*
2017-09-13 01:02:49 +00:00
* Also removes itself from the Input Manager and Physics Manager if previously enabled.
2018-03-19 18:35:08 +00:00
*
2017-09-13 01:02:49 +00:00
* Use this to remove a Game Object from your game if you don't ever plan to use it again.
* As long as no reference to it exists within your own code it should become free for
* garbage collection by the browser.
2018-03-19 18:35:08 +00:00
*
2017-09-13 01:02:49 +00:00
* If you just want to temporarily disable an object then look at using the
* Game Object Pool instead of destroying it, as destroyed objects cannot be resurrected.
2017-09-11 23:28:53 +00:00
*
2018-01-31 13:54:44 +00:00
* @method Phaser.GameObjects.GameObject#destroy
* @since 3.0.0
2017-09-11 23:28:53 +00:00
*/
2016-12-07 02:28:22 +00:00
destroy: function ()
{
2018-03-05 22:25:55 +00:00
// This Game Object had already been destroyed
2018-04-13 16:20:46 +00:00
if (!this.scene || this.ignoreDestroy)
2018-03-05 22:25:55 +00:00
{
return;
}
if (this.preDestroy)
{
this.preDestroy.call(this);
}
this.emit('destroy', this);
2018-01-17 00:29:23 +00:00
var sys = this.scene.sys;
sys.displayList.remove(this);
sys.updateList.remove(this);
if (this.input)
{
2018-01-17 00:29:23 +00:00
sys.input.clear(this);
this.input = undefined;
}
if (this.data)
{
this.data.destroy();
this.data = undefined;
}
if (this.body)
{
this.body.destroy();
this.body = undefined;
}
// Tell the Scene to re-sort the children
2018-01-17 00:29:23 +00:00
sys.queueDepthSort();
this.active = false;
this.visible = false;
this.scene = undefined;
2018-04-10 14:21:04 +00:00
this.parentContainer = undefined;
this.removeAllListeners();
2016-12-07 02:28:22 +00:00
}
});
/**
* The bitmask that `GameObject.renderFlags` is compared against to determine if the Game Object will render or not.
*
2017-09-13 01:02:49 +00:00
* @constant {integer} RENDER_MASK
2018-01-31 16:53:48 +00:00
* @memberOf Phaser.GameObjects.GameObject
2017-09-13 01:02:49 +00:00
* @default
*/
GameObject.RENDER_MASK = 15;
2016-12-07 02:28:22 +00:00
module.exports = GameObject;