mirror of
https://github.com/photonstorm/phaser
synced 2025-02-16 22:18:29 +00:00
Added Path Manager Plugin.
This commit is contained in:
parent
4548d70369
commit
a01f59d82d
7 changed files with 2915 additions and 4 deletions
|
@ -112,6 +112,7 @@ EOL;
|
|||
|
||||
<script src="$path/src/geom/Circle.js"></script>
|
||||
<script src="$path/src/geom/Ellipse.js"></script>
|
||||
<script src="$path/src/geom/Hermite.js"></script>
|
||||
<script src="$path/src/geom/Line.js"></script>
|
||||
<script src="$path/src/geom/Matrix.js"></script>
|
||||
<script src="$path/src/geom/Point.js"></script>
|
||||
|
@ -203,6 +204,12 @@ EOL;
|
|||
<script src="$path/src/plugins/weapon/WeaponPlugin.js"></script>
|
||||
<script src="$path/src/plugins/weapon/Bullet.js"></script>
|
||||
|
||||
<script src="$path/src/plugins/path/PathManagerPlugin.js"></script>
|
||||
<script src="$path/src/plugins/path/Path.js"></script>
|
||||
<script src="$path/src/plugins/path/PathFollower.js"></script>
|
||||
<script src="$path/src/plugins/path/PathPoint.js"></script>
|
||||
<script src="$path/src/plugins/path/EventTarget.js"></script>
|
||||
|
||||
EOL;
|
||||
|
||||
if ($modules['rope'])
|
||||
|
@ -296,10 +303,10 @@ EOL;
|
|||
}
|
||||
|
||||
echo <<<EOL
|
||||
<script src="$path/src/system/Device.js"></script>
|
||||
<script src="$path/src/system/DOM.js"></script>
|
||||
<script src="$path/src/system/Canvas.js"></script>
|
||||
<script src="$path/src/system/RequestAnimationFrame.js"></script>
|
||||
<script src="$path/src/utils/Device.js"></script>
|
||||
<script src="$path/src/utils/DOM.js"></script>
|
||||
<script src="$path/src/utils/Canvas.js"></script>
|
||||
<script src="$path/src/utils/RequestAnimationFrame.js"></script>
|
||||
|
||||
<script src="$path/src/math/Math.js"></script>
|
||||
<script src="$path/src/math/RandomDataGenerator.js"></script>
|
||||
|
|
612
src/geom/Hermite.js
Normal file
612
src/geom/Hermite.js
Normal file
|
@ -0,0 +1,612 @@
|
|||
/**
|
||||
* @author Richard Davey <rich@photonstorm.com>
|
||||
* @author Pete Baron <pete@photonstorm.com>
|
||||
* @copyright 2016 Photon Storm Ltd.
|
||||
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A data representation of a Hermite Curve (see http://en.wikipedia.org/wiki/Cubic_Hermite_spline)
|
||||
*
|
||||
* A Hermite curve has a start and end point and tangent vectors for both of them.
|
||||
* The curve will always pass through the two control points and the shape of it is controlled
|
||||
* by the length and direction of the tangent vectors. At the control points the curve will
|
||||
* be facing exactly in the vector direction.
|
||||
*
|
||||
* As these curves change speed (speed = distance between points separated by an equal change in
|
||||
* 't' value - see Hermite.getPoint) this class attempts to reduce the variation by pre-calculating
|
||||
* the `accuracy` number of points on the curve. The straight-line distances to these points are stored
|
||||
* in the private 'points' array, and this information is used by Hermite.findT() to convert a pixel
|
||||
* distance along the curve into a 'time' value.
|
||||
*
|
||||
* Higher `accuracy` values will result in more even movement, but require more memory for the points
|
||||
* list. 5 works, but 10 seems to be an ideal value for the length of curves found in most games on
|
||||
* a desktop screen. If you use very long curves (more than 400 pixels) you may need to increase
|
||||
* this value further.
|
||||
*
|
||||
* @class Phaser.Hermite
|
||||
* @constructor
|
||||
* @param {number} p1x - The x coordinate of the start of the curve.
|
||||
* @param {number} p1y - The y coordinate of the start of the curve.
|
||||
* @param {number} p2x - The x coordinate of the end of the curve.
|
||||
* @param {number} p2y - The y coordinate of the end of the curve.
|
||||
* @param {number} v1x - The x component of the tangent vector for the start of the curve.
|
||||
* @param {number} v1y - The y component of the tangent vector for the start of the curve.
|
||||
* @param {number} v2x - The x component of the tangent vector for the end of the curve.
|
||||
* @param {number} v2y - The y component of the tangent vector for the end of the curve.
|
||||
* @param {number} [accuracy=10] The amount of points to pre-calculate on the curve.
|
||||
*/
|
||||
Phaser.Hermite = function (p1x, p1y, p2x, p2y, v1x, v1y, v2x, v2y, accuracy) {
|
||||
|
||||
if (accuracy === undefined) { accuracy = 10; }
|
||||
|
||||
/**
|
||||
* @property {number} _accuracy - The amount of points to pre-calculate on the curve.
|
||||
* @private
|
||||
*/
|
||||
this._accuracy = accuracy;
|
||||
|
||||
/**
|
||||
* @property {number} _p1x - The x coordinate of the start of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._p1x = p1x;
|
||||
|
||||
/**
|
||||
* @property {number} _p1y - The y coordinate of the start of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._p1y = p1y;
|
||||
|
||||
/**
|
||||
* @property {number} _p2x - The x coordinate of the end of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._p2x = p2x;
|
||||
|
||||
/**
|
||||
* @property {number} _p2y - The y coordinate of the end of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._p2y = p2y;
|
||||
|
||||
/**
|
||||
* @property {number} _v1x - The x component of the tangent vector for the start of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._v1x = v1x;
|
||||
|
||||
/**
|
||||
* @property {number} _v1y - The y component of the tangent vector for the start of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._v1y = v1y;
|
||||
|
||||
/**
|
||||
* @property {number} _v2x - The x component of the tangent vector for the end of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._v2x = v2x;
|
||||
|
||||
/**
|
||||
* @property {number} _v2y - The y component of the tangent vector for the end of the curve.
|
||||
* @private
|
||||
*/
|
||||
this._v2y = v2y;
|
||||
|
||||
/**
|
||||
* @property {array} _points - A local array of cached points.
|
||||
* @private
|
||||
*/
|
||||
this._points = [];
|
||||
|
||||
/**
|
||||
* @property {Phaser.Point} _temp1 - A local cached Point object.
|
||||
* @private
|
||||
*/
|
||||
this._temp1 = new Phaser.Point;
|
||||
|
||||
/**
|
||||
* @property {Phaser.Point} _temp2 - A local cached Point object.
|
||||
* @private
|
||||
*/
|
||||
this._temp2 = new Phaser.Point;
|
||||
|
||||
this.recalculate();
|
||||
|
||||
};
|
||||
|
||||
Phaser.Hermite.prototype.constructor = Phaser.Hermite;
|
||||
|
||||
Phaser.Hermite.prototype = {
|
||||
|
||||
/**
|
||||
* Performs the curve calculations.
|
||||
*
|
||||
* This is called automatically if you change any of the curves public properties, such as `Hermite.p1x` or `Hermite.v2y`.
|
||||
*
|
||||
* If you adjust any of the internal private values, then call this to update the points.
|
||||
*
|
||||
* @method Phaser.Hermite#recalculate
|
||||
* @return {Phaser.Hermite} This object.
|
||||
*/
|
||||
recalculate: function () {
|
||||
|
||||
this._ax = (2 * this._p1x - 2 * this._p2x + this._v1x + this._v2x);
|
||||
this._ay = (2 * this._p1y - 2 * this._p2y + this._v1y + this._v2y);
|
||||
this._bx = (-3 * this._p1x + 3 * this._p2x - 2 * this._v1x - this._v2x);
|
||||
this._by = (-3 * this._p1y + 3 * this._p2y - 2 * this._v1y - this._v2y);
|
||||
|
||||
this.length = this.calculateEvenPoints();
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate a number of points along the curve, based on `Hermite.accuracy`, and stores them in the private `_points` array.
|
||||
*
|
||||
* @method Phaser.Hermite#calculateEvenPoints
|
||||
* @return {number} The total length of the curve approximated as straight line distances between the points.
|
||||
*/
|
||||
calculateEvenPoints: function () {
|
||||
|
||||
var totalLength = 0;
|
||||
|
||||
this._temp1.setTo(0, 0); // pnt
|
||||
this._temp2.setTo(this._p1x, this._p1y); // lastPnt
|
||||
|
||||
this._points[0] = 0;
|
||||
|
||||
for (var i = 1; i <= this._accuracy; i++)
|
||||
{
|
||||
this.getPoint(i / this._accuracy, this._temp1);
|
||||
totalLength += this._temp1.distance(this._temp2);
|
||||
this._points[i] = totalLength;
|
||||
this._temp2.copyFrom(this._temp1);
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a distance along this curve into a `time` value which will be between 0 and 1.
|
||||
*
|
||||
* For example if this curve has a length of 100 pixels then `findT(50)` would return `0.5`.
|
||||
*
|
||||
* @method Phaser.Hermite#findT
|
||||
* @param {integer} distance - The distance into the curve in pixels. Should be a positive integer.
|
||||
* @return {number} The time (`t`) value, a float between 0 and 1.
|
||||
*/
|
||||
findT: function (distance) {
|
||||
|
||||
if (distance <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the _points which bracket the distance value
|
||||
var ti = Math.floor(distance / this.length * this._accuracy);
|
||||
|
||||
while (ti > 0 && this._points[ti] > distance)
|
||||
{
|
||||
ti--;
|
||||
}
|
||||
|
||||
while (ti < this._accuracy && this._points[ti] < distance)
|
||||
{
|
||||
ti++;
|
||||
}
|
||||
|
||||
// Linear interpolation to get a more accurate fix
|
||||
var dt = this._points[ti] - this._points[ti - 1];
|
||||
var d = distance - this._points[ti - 1];
|
||||
|
||||
return ((ti - 1) / this._accuracy) + d / (dt * this._accuracy);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the X component of a point on the curve based on the `t` (time) value, which must be between 0 and 1.
|
||||
*
|
||||
* @method Phaser.Hermite#getX
|
||||
* @param {number} [t=0] - The time value along the curve from which to extract a point. This is a value between 0 and 1, where 0 represents the start of the curve and 1 the end.
|
||||
* @return {number} The X component of a point on the curve based on the `t` (time) value.
|
||||
*/
|
||||
getX: function (t) {
|
||||
|
||||
if (t === undefined)
|
||||
{
|
||||
t = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t < 0)
|
||||
{
|
||||
t = 0;
|
||||
}
|
||||
|
||||
if (t > 1)
|
||||
{
|
||||
t = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var t2 = t * t;
|
||||
var t3 = t * t2;
|
||||
|
||||
return (t3 * this._ax + t2 * this._bx + t * this._v1x + this._p1x);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Y component of a point on the curve based on the `t` (time) value, which must be between 0 and 1.
|
||||
*
|
||||
* @method Phaser.Hermite#getY
|
||||
* @param {number} [t=0] - The time value along the curve from which to extract a point. This is a value between 0 and 1, where 0 represents the start of the curve and 1 the end.
|
||||
* @return {number} The Y component of a point on the curve based on the `t` (time) value.
|
||||
*/
|
||||
getY: function (t) {
|
||||
|
||||
if (t === undefined)
|
||||
{
|
||||
t = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t < 0)
|
||||
{
|
||||
t = 0;
|
||||
}
|
||||
|
||||
if (t > 1)
|
||||
{
|
||||
t = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var t2 = t * t;
|
||||
var t3 = t * t2;
|
||||
|
||||
return (t3 * this._ay + t2 * this._by + t * this._v1y + this._p1y);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a point on the curve using the `t` (time) value, which must be between 0 and 1.
|
||||
*
|
||||
* @method Phaser.Hermite#getPoint
|
||||
* @param {number} [t=0] - The time value along the curve from which to extract a point. This is a value between 0 and 1, where 0 represents the start of the curve and 1 the end.
|
||||
* @param {Phaser.Point|Object} [point] - An optional Phaser.Point, or Object containing public `x` and `y` properties. If given the resulting values will be stored in the Objects `x` and `y` properties. If omitted a new Phaser.Point object is created.
|
||||
* @return {Phaser.Point} An Object with the x, y coordinate of the curve at the specified `t` value set in its `x` and `y` properties.
|
||||
*/
|
||||
getPoint: function (t, point) {
|
||||
|
||||
if (t === undefined) { t = 0; }
|
||||
if (point === undefined) { point = new Phaser.Point(); }
|
||||
|
||||
if (t < 0)
|
||||
{
|
||||
t = 0;
|
||||
}
|
||||
|
||||
if (t > 1)
|
||||
{
|
||||
t = 1;
|
||||
}
|
||||
|
||||
var t2 = t * t;
|
||||
var t3 = t * t2;
|
||||
|
||||
point.x = t3 * this._ax + t2 * this._bx + t * this._v1x + this._p1x;
|
||||
point.y = t3 * this._ay + t2 * this._by + t * this._v1y + this._p1y;
|
||||
|
||||
return point;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a point on the curve using the distance, in pixels, along the curve.
|
||||
*
|
||||
* @method Phaser.Hermite#getPointWithDistance
|
||||
* @param {integer} [distance=0] - The distance along the curve to get the point from, given in pixels.
|
||||
* @param {Phaser.Point|Object} [point] - An optional Phaser.Point, or Object containing public `x` and `y` properties. If given the resulting values will be stored in the Objects `x` and `y` properties. If omitted a new Phaser.Point object is created.
|
||||
* @return {Phaser.Point} The point on the line at the specified 'distance' along the curve.
|
||||
*/
|
||||
getPointWithDistance: function (distance, point) {
|
||||
|
||||
if (distance === undefined) { distance = 0; }
|
||||
if (point === undefined) { point = new Phaser.Point(); }
|
||||
|
||||
if (distance <= 0)
|
||||
{
|
||||
point.x = this._p1x;
|
||||
point.y = this._p1y;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.getPoint(this.findT(distance), point);
|
||||
}
|
||||
|
||||
return point;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate and return the angle, in radians, of the curves tangent based on time.
|
||||
*
|
||||
* @method Phaser.Hermite#getAngle
|
||||
* @param {number} [t=0] - The `t` (time) value at which to find the angle. Must be between 0 and 1.
|
||||
* @return {number} The angle of the line at the specified `t` time value along the curve. The value is in radians.
|
||||
*/
|
||||
getAngle: function (t) {
|
||||
|
||||
if (t === undefined) { t = 0; }
|
||||
|
||||
this.getPoint(t - .01, this._temp1);
|
||||
this.getPoint(t + .01, this._temp2);
|
||||
|
||||
return Math.atan2(this._temp2.y - this._temp1.y, this._temp2.x - this._temp1.x);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate and return the angle, in radians, of the curves tangent at the given pixel distance along the curves length.
|
||||
*
|
||||
* @method Phaser.Hermite#getAngleWithDistance
|
||||
* @param {number} [distance=0] - The distance along the curve to get the angle from, in pixels.
|
||||
* @return {number} The angle of the line at the specified distance along the curve. The value is in radians.
|
||||
*/
|
||||
getAngleWithDistance: function (distance) {
|
||||
|
||||
if (distance === undefined) { distance = 0; }
|
||||
|
||||
if (distance <= 0)
|
||||
{
|
||||
return Math.atan2(this._v1y, this._v1x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.getAngle(this.findT(distance));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the angle of the curves entry point.
|
||||
*
|
||||
* @method Phaser.Hermite#getEntryTangent
|
||||
* @param {Phaser.Point|Object} point - The Phaser.Point object, or an Object with public `x` and `y` properties, in which the tangent vector values will be stored.
|
||||
* @return {Phaser.Point} A Point object containing the tangent vector of this Hermite curve.
|
||||
*/
|
||||
getEntryTangent: function (point) {
|
||||
|
||||
point.x = this._v1x;
|
||||
point.y = this._v1y;
|
||||
|
||||
return point;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Object.defineProperties(Phaser.Hermite.prototype, {
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#accuracy
|
||||
* @property {number} accuracy - The amount of points to pre-calculate on the curve.
|
||||
*/
|
||||
accuracy: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._accuracy;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._accuracy)
|
||||
{
|
||||
this._accuracy = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#p1x
|
||||
* @property {number} p1x - The x coordinate of the start of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
p1x: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._p1x;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._p1x)
|
||||
{
|
||||
this._p1x = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#p1y
|
||||
* @property {number} p1y - The y coordinate of the start of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
p1y: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._p1y;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._p1y)
|
||||
{
|
||||
this._p1y = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#p2x
|
||||
* @property {number} p2x - The x coordinate of the end of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
p2x: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._p2x;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._p2x)
|
||||
{
|
||||
this._p2x = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#p2y
|
||||
* @property {number} p2y - The y coordinate of the end of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
p2y: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._p2y;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._p2y)
|
||||
{
|
||||
this._p2y = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#v1x
|
||||
* @property {number} v1x - The x component of the tangent vector for the start of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
v1x: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._v1x;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._v1x)
|
||||
{
|
||||
this._v1x = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#v1y
|
||||
* @property {number} v1y - The y component of the tangent vector for the start of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
v1y: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._v1y;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._v1y)
|
||||
{
|
||||
this._v1y = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#v2x
|
||||
* @property {number} v2x - The x component of the tangent vector for the end of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
v2x: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._v2x;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._v2x)
|
||||
{
|
||||
this._v2x = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @name Phaser.Hermite#v2y
|
||||
* @property {number} v2y - The y component of the tangent vector for the end of the curve. Setting this value will recalculate the curve.
|
||||
*/
|
||||
v2y: {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._v2y;
|
||||
|
||||
},
|
||||
|
||||
set: function (value) {
|
||||
|
||||
if (value !== this._v2y)
|
||||
{
|
||||
this._v2y = value;
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
284
src/plugins/path/EventTarget.js
Normal file
284
src/plugins/path/EventTarget.js
Normal file
|
@ -0,0 +1,284 @@
|
|||
/**
|
||||
* @author Mat Groves http://matgroves.com/ @Doormat23
|
||||
* @author Chad Engler https://github.com/englercj @Rolnaaba
|
||||
*/
|
||||
|
||||
/**
|
||||
* Originally based on https://github.com/mrdoob/eventtarget.js/ from mr Doob.
|
||||
* Currently takes inspiration from the nodejs EventEmitter, EventEmitter3, and smokesignals
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mixins event emitter functionality to a class
|
||||
*
|
||||
* @class EventTarget
|
||||
* @example
|
||||
* function MyEmitter() {}
|
||||
*
|
||||
* Phaser.EventTarget.mixin(MyEmitter.prototype);
|
||||
*
|
||||
* var em = new MyEmitter();
|
||||
* em.emit('eventName', 'some data', 'some more data', {}, null, ...);
|
||||
*/
|
||||
Phaser.EventTarget = {
|
||||
/**
|
||||
* Backward compat from when this used to be a function
|
||||
*/
|
||||
call: function callCompat(obj) {
|
||||
if(obj) {
|
||||
obj = obj.prototype || obj;
|
||||
Phaser.EventTarget.mixin(obj);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Mixes in the properties of the EventTarget prototype onto another object
|
||||
*
|
||||
* @method mixin
|
||||
* @param object {Object} The obj to mix into
|
||||
*/
|
||||
mixin: function mixin(obj) {
|
||||
/**
|
||||
* Return a list of assigned event listeners.
|
||||
*
|
||||
* @method listeners
|
||||
* @param eventName {String} The events that should be listed.
|
||||
* @return {Array} An array of listener functions
|
||||
*/
|
||||
obj.listeners = function listeners(eventName) {
|
||||
this._listeners = this._listeners || {};
|
||||
|
||||
return this._listeners[eventName] ? this._listeners[eventName].slice() : [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit an event to all registered event listeners.
|
||||
*
|
||||
* @method emit
|
||||
* @alias dispatchEvent
|
||||
* @param eventName {String} The name of the event.
|
||||
* @return {Boolean} Indication if we've emitted an event.
|
||||
*/
|
||||
obj.emit = obj.dispatchEvent = function emit(eventName, data) {
|
||||
this._listeners = this._listeners || {};
|
||||
|
||||
//backwards compat with old method ".emit({ type: 'something' })"
|
||||
if(typeof eventName === 'object') {
|
||||
data = eventName;
|
||||
eventName = eventName.type;
|
||||
}
|
||||
|
||||
//ensure we are using a real pixi event
|
||||
if(!data || data.__isEventObject !== true) {
|
||||
data = new Phaser.Event(this, eventName, data);
|
||||
}
|
||||
|
||||
//iterate the listeners
|
||||
if(this._listeners && this._listeners[eventName]) {
|
||||
var listeners = this._listeners[eventName].slice(0),
|
||||
length = listeners.length,
|
||||
fn = listeners[0],
|
||||
i;
|
||||
|
||||
for(i = 0; i < length; fn = listeners[++i]) {
|
||||
//call the event listener
|
||||
fn.call(this, data);
|
||||
|
||||
//if "stopImmediatePropagation" is called, stop calling sibling events
|
||||
if(data.stoppedImmediate) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
//if "stopPropagation" is called then don't bubble the event
|
||||
if(data.stopped) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
//bubble this event up the scene graph
|
||||
if(this.parent && this.parent.emit) {
|
||||
this.parent.emit.call(this.parent, eventName, data);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a new EventListener for the given event.
|
||||
*
|
||||
* @method on
|
||||
* @alias addEventListener
|
||||
* @param eventName {String} Name of the event.
|
||||
* @param callback {Functon} fn Callback function.
|
||||
*/
|
||||
obj.on = obj.addEventListener = function on(eventName, fn) {
|
||||
this._listeners = this._listeners || {};
|
||||
|
||||
(this._listeners[eventName] = this._listeners[eventName] || [])
|
||||
.push(fn);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an EventListener that's only called once.
|
||||
*
|
||||
* @method once
|
||||
* @param eventName {String} Name of the event.
|
||||
* @param callback {Function} Callback function.
|
||||
*/
|
||||
obj.once = function once(eventName, fn) {
|
||||
this._listeners = this._listeners || {};
|
||||
|
||||
var self = this;
|
||||
function onceHandlerWrapper() {
|
||||
fn.apply(self.off(eventName, onceHandlerWrapper), arguments);
|
||||
}
|
||||
onceHandlerWrapper._originalHandler = fn;
|
||||
|
||||
return this.on(eventName, onceHandlerWrapper);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove event listeners.
|
||||
*
|
||||
* @method off
|
||||
* @alias removeEventListener
|
||||
* @param eventName {String} The event we want to remove.
|
||||
* @param callback {Function} The listener that we need to find.
|
||||
*/
|
||||
obj.off = obj.removeEventListener = function off(eventName, fn) {
|
||||
this._listeners = this._listeners || {};
|
||||
|
||||
if(!this._listeners[eventName])
|
||||
return this;
|
||||
|
||||
var list = this._listeners[eventName],
|
||||
i = fn ? list.length : 0;
|
||||
|
||||
while(i-- > 0) {
|
||||
if(list[i] === fn || list[i]._originalHandler === fn) {
|
||||
list.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(list.length === 0) {
|
||||
delete this._listeners[eventName];
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all listeners or only the listeners for the specified event.
|
||||
*
|
||||
* @method removeAllListeners
|
||||
* @param eventName {String} The event you want to remove all listeners for.
|
||||
*/
|
||||
obj.removeAllListeners = function removeAllListeners(eventName) {
|
||||
this._listeners = this._listeners || {};
|
||||
|
||||
if(!this._listeners[eventName])
|
||||
return this;
|
||||
|
||||
delete this._listeners[eventName];
|
||||
|
||||
return this;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an homogenous object for tracking events so users can know what to expect.
|
||||
*
|
||||
* @class Event
|
||||
* @extends Object
|
||||
* @constructor
|
||||
* @param target {Object} The target object that the event is called on
|
||||
* @param name {String} The string name of the event that was triggered
|
||||
* @param data {Object} Arbitrary event data to pass along
|
||||
*/
|
||||
Phaser.Event = function(target, name, data) {
|
||||
//for duck typing in the ".on()" function
|
||||
this.__isEventObject = true;
|
||||
|
||||
/**
|
||||
* Tracks the state of bubbling propagation. Do not
|
||||
* set this directly, instead use `event.stopPropagation()`
|
||||
*
|
||||
* @property stopped
|
||||
* @type Boolean
|
||||
* @private
|
||||
* @readOnly
|
||||
*/
|
||||
this.stopped = false;
|
||||
|
||||
/**
|
||||
* Tracks the state of sibling listener propagation. Do not
|
||||
* set this directly, instead use `event.stopImmediatePropagation()`
|
||||
*
|
||||
* @property stoppedImmediate
|
||||
* @type Boolean
|
||||
* @private
|
||||
* @readOnly
|
||||
*/
|
||||
this.stoppedImmediate = false;
|
||||
|
||||
/**
|
||||
* The original target the event triggered on.
|
||||
*
|
||||
* @property target
|
||||
* @type Object
|
||||
* @readOnly
|
||||
*/
|
||||
this.target = target;
|
||||
|
||||
/**
|
||||
* The string name of the event that this represents.
|
||||
*
|
||||
* @property type
|
||||
* @type String
|
||||
* @readOnly
|
||||
*/
|
||||
this.type = name;
|
||||
|
||||
/**
|
||||
* The data that was passed in with this event.
|
||||
*
|
||||
* @property data
|
||||
* @type Object
|
||||
* @readOnly
|
||||
*/
|
||||
this.data = data;
|
||||
|
||||
//backwards compat with older version of events
|
||||
this.content = data;
|
||||
|
||||
/**
|
||||
* The timestamp when the event occurred.
|
||||
*
|
||||
* @property timeStamp
|
||||
* @type Number
|
||||
* @readOnly
|
||||
*/
|
||||
this.timeStamp = Date.now();
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops the propagation of events up the scene graph (prevents bubbling).
|
||||
*
|
||||
* @method stopPropagation
|
||||
*/
|
||||
Phaser.Event.prototype.stopPropagation = function stopPropagation() {
|
||||
this.stopped = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops the propagation of events to sibling listeners (no longer calls any listeners).
|
||||
*
|
||||
* @method stopImmediatePropagation
|
||||
*/
|
||||
Phaser.Event.prototype.stopImmediatePropagation = function stopImmediatePropagation() {
|
||||
this.stoppedImmediate = true;
|
||||
};
|
862
src/plugins/path/Path.js
Normal file
862
src/plugins/path/Path.js
Normal file
|
@ -0,0 +1,862 @@
|
|||
/**
|
||||
* @author Richard Davey <rich@photonstorm.com>
|
||||
* @author Pete Baron <pete@photonstorm.com>
|
||||
* @copyright 2016 Photon Storm Ltd.
|
||||
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Phaser.Path contains all the functions need to create and manipulate a single Path object.
|
||||
* A Path is a list of PathPoint objects connected by Hermite curves.
|
||||
*
|
||||
* @class Phaser.Path
|
||||
* @constructor
|
||||
* @param {Phaser.Game} game - A reference to the Phaser.Game instance.
|
||||
* @param {number} [type=Phaser.Path.CoordinateSystems.WORLD] - The coordinate system used by the Path.
|
||||
* @param {boolean} [loops=false] - Should this Path loop or not when a PathFollower reaches the end of it?
|
||||
*/
|
||||
Phaser.Path = function (game, type, loops) {
|
||||
|
||||
if (type === undefined) { type = Phaser.Path.CoordinateSystems.WORLD; }
|
||||
if (loops === undefined) { loops = false; }
|
||||
|
||||
/**
|
||||
* @property {Phaser.Game} game - A reference to the currently running game.
|
||||
*/
|
||||
this.game = game;
|
||||
|
||||
/**
|
||||
* @property {number} coordinateSystem - The coordinate system used by the Path.
|
||||
*/
|
||||
this.coordinateSystem = type;
|
||||
|
||||
/**
|
||||
* @property {boolean} loops - Should this Path loop or not when a PathFollower reaches the end of it?
|
||||
*/
|
||||
this.loops = loops;
|
||||
|
||||
/**
|
||||
* @property {string} cacheKey - The key of the JSON file in the cache used to define this path.
|
||||
*/
|
||||
this.cacheKey = '';
|
||||
|
||||
/**
|
||||
* @property {string} key - The key of the object within the JSON data. Used if there are multiple paths per JSON file.
|
||||
*/
|
||||
this.key = '';
|
||||
|
||||
/*
|
||||
* @property {Phaser.PathPoint} name - The name of this path.
|
||||
*/
|
||||
this.name = '';
|
||||
|
||||
/*
|
||||
* @property {Phaser.PathPoint} type - The Phaser.Path.PathTypes of this path.
|
||||
*/
|
||||
this.type = Phaser.Path.PathTypes.PATH;
|
||||
|
||||
/*
|
||||
* @property {Array} branches - A list of branches this path has.
|
||||
*/
|
||||
this.branches = [];
|
||||
|
||||
/**
|
||||
* @property {array} _points - A private cache of the Points on this Path.
|
||||
* @private
|
||||
*/
|
||||
this._points = [];
|
||||
|
||||
/**
|
||||
* @property {Phaser.Point} _offset - Default offset for PathFollowers on this path instance.
|
||||
* @private
|
||||
*/
|
||||
this._offset = new Phaser.Point();
|
||||
|
||||
/*
|
||||
* @property {Phaser.PathPoint} _p1 - Used for internal calculations.
|
||||
* @private
|
||||
*/
|
||||
this._p1 = new Phaser.PathPoint();
|
||||
|
||||
/*
|
||||
* @property {Phaser.PathPoint} _p2 - Used for internal calculations.
|
||||
* @private
|
||||
*/
|
||||
this._p2 = new Phaser.PathPoint();
|
||||
|
||||
/*
|
||||
* @property {Phaser.PathPoint} origin - the origin of this path. Used mostly for BRANCH paths.
|
||||
* @private
|
||||
*/
|
||||
this._origin = new Phaser.Point();
|
||||
|
||||
};
|
||||
|
||||
Phaser.Path.prototype.constructor = Phaser.Path;
|
||||
|
||||
Phaser.Path.PathTypes = {};
|
||||
Phaser.Path.BranchTypes = {};
|
||||
Phaser.Path.CoordinateSystems = {};
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.Path.PathTypes.PATH = 0;
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.Path.PathTypes.BRANCH = 1;
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.Path.BranchTypes.ATTACHED = 0;
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.Path.BranchTypes.JOINED = 1;
|
||||
|
||||
/**
|
||||
* Points are relative to the World origin.
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.Path.CoordinateSystems.WORLD = 1;
|
||||
|
||||
/**
|
||||
* Points are relative to the screen origin.
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.Path.CoordinateSystems.SCREEN = 2;
|
||||
|
||||
/**
|
||||
* Points are relative to the first point.
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.Path.CoordinateSystems.OFFSET = 3;
|
||||
|
||||
Phaser.Path.prototype = {
|
||||
|
||||
/**
|
||||
* Initialize a Path based on the given coordinate system.
|
||||
*
|
||||
* @method Phaser.Path#create
|
||||
* @param {number|string} coordinateSystem - The Phaser.Path.CoordinateSystems type to use.
|
||||
* @param {boolean} [loops=false] - Should this Path loop or not when a PathFollower reaches the end of it?
|
||||
* @return {Phaser.Path} This Path object.
|
||||
*/
|
||||
create: function (coordinateSystem, loops) {
|
||||
|
||||
if (loops === undefined) { loops = false; }
|
||||
|
||||
switch (coordinateSystem)
|
||||
{
|
||||
default:
|
||||
this.coordinateSystem = Phaser.Path.CoordinateSystems.WORLD;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 'SCREEN_COORDINATES':
|
||||
this.coordinateSystem = Phaser.Path.CoordinateSystems.SCREEN;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 'OFFSET_COORDINATES':
|
||||
this.coordinateSystem = Phaser.Path.CoordinateSystems.OFFSET;
|
||||
break;
|
||||
}
|
||||
|
||||
this.loops = loops;
|
||||
|
||||
this._points = [];
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Clone this Path object. It clones the origin and points data.
|
||||
*
|
||||
* @method Phaser.Path#clone
|
||||
* @return {Phaser.Path} The cloned Path.
|
||||
*/
|
||||
clone: function () {
|
||||
|
||||
var clone = new Phaser.Path(this.coordinateSystem, this.loops);
|
||||
|
||||
this.origin.clone(clone.origin);
|
||||
|
||||
this.points.forEach(function(p) {
|
||||
clone._points.push(p.clone());
|
||||
});
|
||||
|
||||
return clone;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new PathPoint object, relative to the path origin, and adds it to this path.
|
||||
*
|
||||
* @method Phaser.Path#addPathPoint
|
||||
* @param {number} [x=0] - The x position of the PathPoint.
|
||||
* @param {number} [y=0] - The y position of the PathPoint.
|
||||
* @param {number} [vx=0] - The vx tangent vector value of the PathPoint.
|
||||
* @param {number} [vy=0] - The vy tangent vector value of the PathPoint.
|
||||
* @param {number} [speed=1] - The speed value of the PathPoint.
|
||||
* @param {number} [data={}] - The data object
|
||||
* @param {number} [index=null] - The index of the new path point. If not given, will add point to end of point list.
|
||||
* @return {Phaser.PathPoint} The PathPoint object that was created.
|
||||
*/
|
||||
addPathPoint: function (x, y, vx, vy, speed, data, index) {
|
||||
|
||||
if (x === undefined) { x = 0; }
|
||||
if (y === undefined) { y = 0; }
|
||||
if (vx === undefined) { vx = 0; }
|
||||
if (vy === undefined) { vy = 0; }
|
||||
|
||||
var pp = new Phaser.PathPoint(x - this.origin.x, y - this.origin.y, vx, vy, speed, data);
|
||||
|
||||
if (index !== null && index !== undefined)
|
||||
{
|
||||
this._points.splice(index, 0, pp);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._points.push(pp);
|
||||
}
|
||||
|
||||
return pp;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a PathPoint from this paths point list.
|
||||
*
|
||||
* @method Phaser.Path#removePathPoint
|
||||
* @param {number} [index] - The index of the PathPoint to remove.
|
||||
* @return {Phaser.PathPoint} The removed PathPoint object.
|
||||
*/
|
||||
removePathPoint: function (index) {
|
||||
|
||||
var p = this.getPathPointReference(index);
|
||||
|
||||
if (p)
|
||||
{
|
||||
this._points.splice(index, 1);
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a PathPoint objects position and tangent vector.
|
||||
*
|
||||
* @method Phaser.Path#setPathPoint
|
||||
* @param {number} index - The index of the PathPoint in this paths point list.
|
||||
* @param {number} x - The x coordinate of the PathPoint.
|
||||
* @param {number} y - The y coordinate of the PathPoint.
|
||||
* @param {number} [vx] - The x coordinate of the tangent vector to create the curve from.
|
||||
* @param {number} [vy] - The y coordinate of the tangent vector to create the curve from.
|
||||
* @return {Phaser.PathPoint} A reference to the PathPoint object that was updated.
|
||||
*/
|
||||
setPathPoint: function (index, x, y, vx, vy) {
|
||||
|
||||
var p = this.getPathPointReference(index);
|
||||
|
||||
if (p)
|
||||
{
|
||||
p.setTo(x, y, vx, vy);
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Translate all points in a path by the given point.
|
||||
*
|
||||
* @method Phaser.Path#translatePoints
|
||||
* @param {Phaser.Point|object} point - A Phaser.Point, or a Point-like Object with public `x` and `y` properties, that will be used to modify all points in this paths point list.
|
||||
* @return {Phaser.Path} This Path object.
|
||||
*/
|
||||
translatePoints: function (point) {
|
||||
|
||||
this._points.forEach(function(pnt) {
|
||||
pnt.x += point.x;
|
||||
pnt.y += point.y;
|
||||
});
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the Path level offset which will affect all of this paths PathFollowers.
|
||||
*
|
||||
* @method Phaser.Path#setOffset
|
||||
* @param {number} x - The x offset.
|
||||
* @param {number} y - The y offset.
|
||||
* @return {Phaser.Path} This Path object.
|
||||
*/
|
||||
setOffset: function (x, y) {
|
||||
|
||||
this._offset.x = x;
|
||||
this._offset.y = y;
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a point on the the current Path curve.
|
||||
*
|
||||
* @method Phaser.Path#getPointOnThisCurve
|
||||
* @param {Phaser.Hermite} curve - A Phaser.Hermite curve object.
|
||||
* @param {number} [t=0 .. 1.0] - The distance on the curve to get the point from. Where 0 is the start of the curve, and 1 is the end.
|
||||
* @return {Phaser.Point} A point containing the x and y values at the specified distance (t) value in the curve.
|
||||
*/
|
||||
getPointOnThisCurve: function (curve, t) {
|
||||
|
||||
if (!curve)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var pnt = curve.getPoint(t);
|
||||
|
||||
pnt.x += this._offset.x;
|
||||
pnt.y += this._offset.y;
|
||||
|
||||
return pnt;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the points on the curve representing the end points of the line segments that make up the curve.
|
||||
*
|
||||
* @method Phaser.Path#getControlPointsOnThisCurve
|
||||
* @param {Phaser.Hermite} curve - A Phaser.Hermite curve.
|
||||
* @return {[Phaser.Point]} An array of points representing the end points of 10 line segments that make up the curve
|
||||
*/
|
||||
getControlPointsOnThisCurve: function (curve) {
|
||||
|
||||
var pnts = Phaser.ArrayUtils.numberArrayStep(0, 1.1, 0.1).map(function(num) {
|
||||
return this.getPointOnThisCurve(curve, num);
|
||||
}, this);
|
||||
|
||||
return pnts;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a PathPoint from this path. Automatically handles path looping.
|
||||
*
|
||||
* The values from the PathPoint are copied into the given PathPoint object, which must
|
||||
* be a reference to a pre-existing PathPoint, as it's not returned by this method.
|
||||
*
|
||||
* @method Phaser.Path#getPathPoint
|
||||
* @param {number} index - The index of the point in this path to get.
|
||||
* @param {Phaser.PathPoint} point - A PathPoint object into which the found point object is cloned.
|
||||
* @return {boolean} false if the index is past the end of the path and it doesn't loop, otherwise true.
|
||||
*/
|
||||
getPathPoint: function (index, point) {
|
||||
|
||||
var i = this.loops ? index % this._points.length : index;
|
||||
|
||||
// If index is in the points list range
|
||||
if (this._points.length > i)
|
||||
{
|
||||
point.copy(this._points[i]);
|
||||
|
||||
switch (this.coordinateSystem)
|
||||
{
|
||||
case Phaser.Path.CoordinateSystems.SCREEN:
|
||||
|
||||
point.x -= this.game.camera.x;
|
||||
point.y -= this.game.camera.y;
|
||||
break;
|
||||
|
||||
case Phaser.Path.CoordinateSystems.OFFSET:
|
||||
|
||||
point.x += this.origin.x;
|
||||
point.y += this.origin.y;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The path doesn't loop and the index is out of range, so fail
|
||||
return false;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a reference to a PathPoint from this Path, handle path looping.
|
||||
*
|
||||
* NOTE: because this is a PathPoint reference, it does not take into account the coordinateSystem selected, it will be WORLD, or OFFSET unmodified
|
||||
*
|
||||
* @method Phaser.Path#getPathPointReference
|
||||
* @param {number} index - The index of the point in this path to get.
|
||||
* @return {Phaser.PathPoint} A reference to the PathPoint object in this Path, or null if index is out of range.
|
||||
*/
|
||||
getPathPointReference: function (index) {
|
||||
|
||||
var i = this.loops ? index % this._points.length : index;
|
||||
|
||||
// If index is in the points list range
|
||||
if (this._points.length > i)
|
||||
{
|
||||
return this._points[i];
|
||||
}
|
||||
|
||||
// The path doesn't loop and the index is out of range, fail
|
||||
return null;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the curve from the given point index to the next.
|
||||
*
|
||||
* If the curve has been created previously, use that definition again, otherwise calculate it now.
|
||||
*
|
||||
* @method Phaser.Path#getCurve
|
||||
* @param {number} [index=0] - The index of the point in this path to get the curve from.
|
||||
* @return {Phaser.Hermite} A new Hermite object representing the curve starting at the 'index' path point.
|
||||
*/
|
||||
getCurve: function (index) {
|
||||
|
||||
if (index === undefined) { index = 0; }
|
||||
|
||||
// Beginning of the curve
|
||||
if (!this.getPathPoint(index, this._p1))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Has this curve been calculated already?
|
||||
if (this._p1.curve)
|
||||
{
|
||||
return this._p1.curve;
|
||||
}
|
||||
|
||||
// End of the curve
|
||||
if (!this.getPathPoint(index + 1, this._p2))
|
||||
{
|
||||
if (!this._p1.branchPath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// We joined another Path
|
||||
var newPath = this._p1.branchPath;
|
||||
var joinIndex = this._p1.branchPointIndex;
|
||||
|
||||
if (!newPath.getPathPoint(joinIndex + 1, this._p2))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Create and return the new Hermite object
|
||||
this._p1.curve = new Phaser.Hermite(this._p1.x, this._p1.y, this._p2.x, this._p2.y, this._p1.vx, this._p1.vy, this._p2.vx, this._p2.vy);
|
||||
|
||||
this.curvePointIndex = index;
|
||||
|
||||
return this._p1.curve;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the first matching PathPoint in this path.
|
||||
* It works by taking the given PathPoint object, and then iterating through all points
|
||||
* in this Path until it finds one with the same values, then returns the index to it.
|
||||
*
|
||||
* @method Phaser.Path#pointIndex
|
||||
* @param {Phaser.PathPoint} pathPoint - The PathPoint object that will have its values compared to all the points in this Path.
|
||||
* @return {number} The index of the PathPoint in this Path if an equal match is found, or -1 if no match is found.
|
||||
*/
|
||||
pointIndex: function (pathPoint) {
|
||||
|
||||
var l = this._points.length;
|
||||
|
||||
for (var i = 0; i < l; i++)
|
||||
{
|
||||
if (this.coordinateSystem === Phaser.Path.CoordinateSystems.OFFSET && i !== 0)
|
||||
{
|
||||
if (pathPoint.equals(this._points[i], this._points[0].x, this._points[0].y))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pathPoint.equals(this._points[i]))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Is the given PathPoint index the end of this path?
|
||||
*
|
||||
* @method Phaser.Path#atEnd
|
||||
* @param {number} index - The index of the PathPoint to test.
|
||||
* @return {boolean} true if index is the last point in this path.
|
||||
*/
|
||||
atEnd: function (index) {
|
||||
|
||||
// If the path loops, the end of the path is the end of the last curve
|
||||
if (this.loops)
|
||||
{
|
||||
return (index === this._points.length);
|
||||
}
|
||||
|
||||
// If the path doesn't loop, the end of the path is the last point on it
|
||||
return (index === this._points.length - 1);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The total number of PathPoints in this Path.
|
||||
*
|
||||
* @method Phaser.Path#numPoints
|
||||
* return {number} The total number of PathPoints in this Path.
|
||||
*/
|
||||
numPoints: function () {
|
||||
|
||||
return this._points.length;
|
||||
|
||||
},
|
||||
|
||||
/*
|
||||
* DATA PROCESSING
|
||||
*/
|
||||
|
||||
/**
|
||||
* Process the data associated with a point on this Path.
|
||||
* Used by Phaser.PathFollower objects as they pass each control point.
|
||||
*
|
||||
* @method Phaser.Path#processData
|
||||
* @param {Phaser.PathFollower} follower - The PathFollower that is processing the data.
|
||||
* @param {number} pathPointIndex - The index of the path point to process.
|
||||
* @param {boolean} reversing - Whether or not the follower is traversing the path in reverse.
|
||||
* @return {Phaser.PathPoint} The PathPoint that has been processed.
|
||||
*/
|
||||
processData: function (follower, pathPointIndex, reversing) {
|
||||
|
||||
if (this.getPathPoint(pathPointIndex, this._p1))
|
||||
{
|
||||
// If there is a branch that can be taken from this point,
|
||||
// trigger an event to decide whether to take it or stay on the current path.
|
||||
// Branches are forwards facing so they are ignored when the follower is reversing.
|
||||
if (this._p1.branchPath && !reversing)
|
||||
{
|
||||
follower.dispatchEvent({
|
||||
type: Phaser.PathFollower.EVENT_BRANCH_CHOICE,
|
||||
target: follower,
|
||||
data: this._p1.clone()
|
||||
});
|
||||
}
|
||||
|
||||
// If there is information in the data member of this point
|
||||
if (this._p1.data && this._p1.data.type)
|
||||
{
|
||||
switch (this._p1.data.type)
|
||||
{
|
||||
case Phaser.PathPoint.DATA_PAUSE:
|
||||
|
||||
follower.pause(this._p1.data.value);
|
||||
break;
|
||||
|
||||
case Phaser.PathPoint.DATA_COUNTER:
|
||||
|
||||
// first time past, set the count
|
||||
if (follower.branchCount === 0)
|
||||
{
|
||||
follower.branchCount = this._p1.data.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// After that decrease the count
|
||||
follower.branchCount--;
|
||||
|
||||
if (follower.branchCount <= 0)
|
||||
{
|
||||
follower.branchCount = 0;
|
||||
|
||||
// Trigger event when counter expires
|
||||
follower.dispatchEvent({
|
||||
type: Phaser.PathFollower.EVENT_COUNT_FINISH,
|
||||
target: follower,
|
||||
data: this._p1.clone()
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger event when passing any point on the path
|
||||
follower.dispatchEvent({
|
||||
type: Phaser.PathFollower.EVENT_REACHED_POINT,
|
||||
target: follower,
|
||||
data: this._p1.clone()
|
||||
});
|
||||
}
|
||||
|
||||
return this._p1;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* If your Path has 3 points or more, this will walk through it and auto-smooth them out.
|
||||
* Note: It ignores branches.
|
||||
*
|
||||
* @method Phaser.Path#smooth
|
||||
* @return {Phaser.Path} This Path object.
|
||||
*/
|
||||
smooth: function () {
|
||||
|
||||
if (this._points.length === 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var i;
|
||||
var thisPoint;
|
||||
var p1;
|
||||
var p2;
|
||||
var dx;
|
||||
var dy;
|
||||
|
||||
for (i = 1; i < this._points.length - 1; i++)
|
||||
{
|
||||
thisPoint = this.getPathPointReference(i);
|
||||
|
||||
p1 = this.getPathPointReference(i - 1);
|
||||
p2 = this.getPathPointReference(i + 1);
|
||||
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
|
||||
thisPoint.setTangent(dx, dy);
|
||||
}
|
||||
|
||||
if (this.loops)
|
||||
{
|
||||
i = this._points.length - 1;
|
||||
|
||||
thisPoint = this.getPathPointReference(i);
|
||||
|
||||
p1 = this.getPathPointReference(i - 1);
|
||||
p2 = this.getPathPointReference(0);
|
||||
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
|
||||
thisPoint.setTangent(dx, dy);
|
||||
|
||||
i = 0;
|
||||
|
||||
thisPoint = this.getPathPointReference(i);
|
||||
|
||||
p1 = this.getPathPointReference(this._points.length - 1);
|
||||
p2 = this.getPathPointReference(1);
|
||||
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
|
||||
thisPoint.setTangent(dx, dy);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw the path on given canvas context. Used for debugging.
|
||||
*
|
||||
* @method Phaser.Path#debug
|
||||
* @param {CanvasContext2D} ctx - The canvas context to draw the path on.
|
||||
* @param {boolean} [active=false] - Whether or not to highlight the active segments of this Path or not.
|
||||
* @return {Phaser.Path} This Path object.
|
||||
*/
|
||||
debug: function (ctx, active) {
|
||||
|
||||
var lineColor = '#333333';
|
||||
|
||||
if (active)
|
||||
{
|
||||
lineColor = '#ffff00';
|
||||
}
|
||||
|
||||
if (this._points.length === 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
this._p1.setTo(0, 0);
|
||||
|
||||
// Draw the lines
|
||||
|
||||
var lastPoint = this._points.length;
|
||||
|
||||
if (!this.loops)
|
||||
{
|
||||
lastPoint--;
|
||||
}
|
||||
|
||||
var p = new Phaser.PathPoint();
|
||||
|
||||
for (var i = 0; i < lastPoint; i++)
|
||||
{
|
||||
var curve = this.getCurve(i);
|
||||
|
||||
this.getPathPoint(i, p);
|
||||
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = 'rgb(100, 0, 250)';
|
||||
|
||||
ctx.save();
|
||||
|
||||
// Control Points
|
||||
var controlPoints = this.getControlPointsOnThisCurve(curve);
|
||||
|
||||
// Draw lines
|
||||
ctx.beginPath();
|
||||
|
||||
controlPoints.forEach(function(pnt, index) {
|
||||
|
||||
if (!!pnt)
|
||||
{
|
||||
if (index === 0)
|
||||
{
|
||||
ctx.moveTo(pnt.x, pnt.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.lineTo(pnt.x, pnt.y);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
|
||||
if (p.active)
|
||||
{
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.strokeStyle = '#333333';
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
// Copy control points to the point object
|
||||
this.getPathPointReference(i).controlPoints = controlPoints;
|
||||
|
||||
controlPoints.forEach(function(pnt) {
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.arc(pnt.x, pnt.y, 3, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
|
||||
ctx.closePath();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes this Path into a JSON object and returns it.
|
||||
*
|
||||
* @methods Phaser.Path#toJSON
|
||||
* @return {Object} A JSON object representing this Path.
|
||||
*/
|
||||
toJSON: function () {
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
coordinateSystem: this.coordinateSystem,
|
||||
loops: this.loops,
|
||||
speed: 1,
|
||||
pointList: this._points.map(function(p) {
|
||||
return p.toJSON();
|
||||
}),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @property {Array} - The list of PathPoints that make up this path.
|
||||
* @readonly
|
||||
*/
|
||||
Object.defineProperty(Phaser.Path.prototype, 'points', {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._points;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* @property {number} - The number of points in this path.
|
||||
* @readonly
|
||||
*/
|
||||
Object.defineProperty(Phaser.Path.prototype, 'length', {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this._points.length;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* @property {Phaser.Point} - The origin of the path.
|
||||
*/
|
||||
Object.defineProperty(Phaser.Path.prototype, 'origin', {
|
||||
|
||||
get: function() {
|
||||
|
||||
return this._origin;
|
||||
|
||||
},
|
||||
|
||||
set: function (val) {
|
||||
|
||||
this._origin.setTo(val.x, val.y);
|
||||
|
||||
}
|
||||
|
||||
});
|
536
src/plugins/path/PathFollower.js
Normal file
536
src/plugins/path/PathFollower.js
Normal file
|
@ -0,0 +1,536 @@
|
|||
/**
|
||||
* @author Richard Davey <rich@photonstorm.com>
|
||||
* @author Pete Baron <pete@photonstorm.com>
|
||||
* @copyright 2016 Photon Storm Ltd.
|
||||
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A PathFollower is a virtual entity that follows the Path.
|
||||
* It is usually linked to a game object such as a Sprite and it will control either the
|
||||
* position of that object, or its velocity if it is a physics object.
|
||||
*
|
||||
* Callbacks will be triggered when certain events happen as the follower moves. These
|
||||
* may be used to aid in the creation of complex behaviours for the game objects.
|
||||
*
|
||||
* @class Phaser.PathFollower
|
||||
* @constructor
|
||||
* @param {Phaser.Path} path - The Path object which this follower is created on.
|
||||
* @param {Phaser.Sprite|object} follower - The game object which this follower controls. Requires public properties: `x`, `y` for position and `rotation` for angle control (if specified).
|
||||
* @param {number} [speed=1] - The current speed of this follower in pixels per frame. This value is multiplied with the Path segment speed to give the final value used.
|
||||
* @param {number} [angleOffset=null] - If `null` then the PathFollower won't rotate. Otherwise it will face in the paths direction plus this offset which is given in radians.
|
||||
* @param {function} [callbackAtEnd] - A callback to be invoked when the follower reaches the end of a path.
|
||||
* @param {number} [physicsAdjustTime=0] - If non-zero then the follower expects to control a physics object using "arcade.moveToObject" to control velocity.
|
||||
*/
|
||||
Phaser.PathFollower = function (path, follower, speed, rotationOffset, angularOffset, callbackAtEnd, physicsAdjustTime) {
|
||||
|
||||
if (speed === undefined) { speed = 1; }
|
||||
if (rotationOffset === undefined) { rotationOffset = 0; }
|
||||
if (angularOffset === undefined) { angularOffset = { angle: 0, distance: 0 }; }
|
||||
if (physicsAdjustTime === undefined) { physicsAdjustTime = 0; }
|
||||
|
||||
Phaser.EventTarget.call(this);
|
||||
|
||||
this.path = path;
|
||||
|
||||
this.follower = follower;
|
||||
|
||||
this._turnOffset = rotationOffset;
|
||||
|
||||
this.callbackAtEnd = callbackAtEnd;
|
||||
|
||||
this.physicsAdjustTime = physicsAdjustTime;
|
||||
|
||||
// offset is an x,y offset from the Path unique for this PathFollower, it is added to the Path's own offset to give a final location
|
||||
this.offset = new Phaser.Point(0, 0);
|
||||
|
||||
if (typeof speed === 'object')
|
||||
{
|
||||
this.speed = Phaser.Utils.extend(true, Object.create(Phaser.PathFollower.Defaults.speed), speed);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.speed = Object.create(Phaser.PathFollower.Defaults.speed);
|
||||
|
||||
this.speed.min = speed;
|
||||
this.speed.max = speed;
|
||||
}
|
||||
|
||||
// _angularOffset is an angular offset from the Path's tangent direction, using angle (radians) and distance (pixels)
|
||||
this._angularOffset = { angle: 0, distance: 0 };
|
||||
|
||||
this.setAngularOffset(angularOffset.angle, angularOffset.distance);
|
||||
|
||||
// branchCount is used when the follower passes a counted PathPoint (see the Mummy Path example)
|
||||
// it is set whenever this follower passes a counted PathPoint and the count is zero
|
||||
// it decrements each time the follower passes a counted PathPoint and the count is non-zero
|
||||
// when the count reaches zero it triggers EVENT_COUNT_FINISH
|
||||
// NOTE: if there are multiple counted PathPoints this will not work as expected as there is only one count variable per follower!
|
||||
this.branchCount = 0;
|
||||
|
||||
this.branchPredicate = null;
|
||||
|
||||
// distance along the current Path segment
|
||||
this._currentDistance = 0;
|
||||
|
||||
// PathPoint index of the start of the current Path segment
|
||||
this._currentPoint = 0;
|
||||
|
||||
// Hermite curve for the current Path segment
|
||||
this._currentCurve = this.path.getCurve(this._currentPoint);
|
||||
|
||||
// initialise the _pathSpeed by taking the speed of the first point on this Path
|
||||
var pp = new Phaser.PathPoint();
|
||||
|
||||
if (this.path.getPathPoint(0, pp))
|
||||
{
|
||||
this._pathSpeed = pp.speed;
|
||||
}
|
||||
|
||||
// set up a virtualParticle if this is controlling a Physics body instead of a simple graphic object
|
||||
if (this.physicsAdjustTime !== 0)
|
||||
{
|
||||
this.virtualParticle = new Phaser.Point(pp.x, pp.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.virtualParticle = null;
|
||||
}
|
||||
|
||||
// default maximum gap permitted between a physics based follower and its virtual particle, in pixels
|
||||
this.maximumGap = 1000;
|
||||
|
||||
// process the data for the first point on this Path
|
||||
this.path.processData(this, this._currentPoint, false);
|
||||
|
||||
// initialise the pause time to zero for this follower
|
||||
this._pauseTime = 0;
|
||||
|
||||
this._accelerationTime = 0;
|
||||
|
||||
this.yoyo = false;
|
||||
|
||||
if (!follower.events)
|
||||
{
|
||||
follower.events = {};
|
||||
}
|
||||
|
||||
follower.events.onPathPointReached = new Phaser.Signal(); // "follower has reached a PathPoint on the path"
|
||||
follower.events.onPathBranchReached = new Phaser.Signal(); // "follower has reached a branch and must choose a direction" (stay on this path or changePath to the branch)
|
||||
|
||||
/* TODO: */
|
||||
follower.events.onCountFinished = new Phaser.Signal(); // "follower passed a counted point the specified number of times" */
|
||||
follower.events.onPathStart = new Phaser.Signal(); // NOTE: not "follower started a path" but "follower moved backwards to the start of the path"
|
||||
follower.events.onPathYoyo = new Phaser.Signal(); // "follower moved to the end of the path" but NOT if the path is looped, that generates EVENT_PATH_LOOPED instead
|
||||
follower.events.onPathEnd = new Phaser.Signal(); // "follower moved to the end of the path" but NOT if the path is looped, that generates EVENT_PATH_LOOPED instead
|
||||
follower.events.onPathLoop = new Phaser.Signal(); // "follower reached the end of a looped path and has started at the beginning again"
|
||||
|
||||
follower.followerPathName = this.path.name;
|
||||
|
||||
Object.defineProperty(this.speed, 'avg', {
|
||||
get: function() {
|
||||
return (this.min + this.max) / 2;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// events for PathFollower
|
||||
Phaser.PathFollower.EVENT_REACHED_POINT = "event_reached_point"; // "follower has reached a PathPoint on the path"
|
||||
Phaser.PathFollower.EVENT_BRANCH_CHOICE = "event_branch_choice"; // "follower has reached a branch and must choose a direction" (stay on this path or changePath to the branch)
|
||||
Phaser.PathFollower.EVENT_COUNT_FINISH = "event_count_finish"; // "follower passed a counted point the specified number of times"
|
||||
Phaser.PathFollower.EVENT_PATH_START = "event_path_start"; // NOTE: "a path started" but "follower moved backwards to the start of the path"
|
||||
Phaser.PathFollower.EVENT_PATH_END = "event_path_end"; // "follower moved to the end of the path" but NOT if the path is looped, that generates EVENT_PATH_LOOPED instead
|
||||
Phaser.PathFollower.EVENT_PATH_LOOPED = "event_path_looped"; // "follower reached the end of a looped path and has started at the beginning again"
|
||||
|
||||
// reduce dynamic object allocations by using this temporary Point wherever possible
|
||||
Phaser.PathFollower.tempPoint = new Phaser.Point();
|
||||
|
||||
Phaser.PathFollower.Defaults = {
|
||||
speed: {
|
||||
min: 1,
|
||||
max: 1,
|
||||
theta: null,
|
||||
lambda: null,
|
||||
_target: null,
|
||||
_elapsed: 0,
|
||||
_current: null,
|
||||
_previous: null
|
||||
}
|
||||
};
|
||||
|
||||
// remove all event listeners when this PathFollower is destroyed
|
||||
Phaser.PathFollower.prototype.destroy = function () {
|
||||
|
||||
this.follower.events.onPathPointReached.removeAll();
|
||||
this.follower.events.onPathBranchReached.removeAll();
|
||||
this.follower.events.onCountFinished.removeAll();
|
||||
this.follower.events.onPathStart.removeAll();
|
||||
this.follower.events.onPathEnd.removeAll();
|
||||
this.follower.events.onPathLoop.removeAll();
|
||||
|
||||
};
|
||||
|
||||
// update this PathFollower and move the attached graphic or physics object
|
||||
// @return: false if this PathFollower should be removed from the Path's list of followers
|
||||
Phaser.PathFollower.prototype.update = function () {
|
||||
|
||||
// exit immediately if _pauseTime is non-zero and it's not that time yet
|
||||
if (this._pauseTime != 0)
|
||||
{
|
||||
if (game.time.now < this._pauseTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
this._pauseTime = 0;
|
||||
|
||||
if (this.follower.animations !== undefined)
|
||||
{
|
||||
// Phaser.AnimationManager doesn't check for a currentAnim before trying to set it's paused value, so I have to do it here
|
||||
if (this.follower.animations.currentAnim)
|
||||
{
|
||||
this.follower.animations.paused = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the follower is a physics object following a virtual particle
|
||||
var waitForFollower = false;
|
||||
|
||||
if (this.physicsAdjustTime && this.virtualParticle)
|
||||
{
|
||||
// if the distance is too great, make the virtual particle wait for the follower to catch up
|
||||
if (game.physics.arcade.distanceBetween(this.follower, this.virtualParticle) >= this.maximumGap)
|
||||
{
|
||||
waitForFollower = true;
|
||||
}
|
||||
}
|
||||
|
||||
// advance along the path unless we're waiting for the follower to catch up
|
||||
if (!waitForFollower)
|
||||
{
|
||||
this._currentDistance += this._calculateDistance();
|
||||
}
|
||||
|
||||
// are we moving forwards or backwards?
|
||||
var direction = (this.speed.avg * this._pathSpeed) >= 0 ? 1 : -1;
|
||||
|
||||
// while we're past either end of the current curve
|
||||
while ((direction == 1 && this._currentDistance >= this._currentCurve.length) || (direction == -1 && this._currentDistance < 0))
|
||||
{
|
||||
var memCurveLength = this._currentCurve.length;
|
||||
|
||||
// backwards...
|
||||
if (direction == -1)
|
||||
{
|
||||
var branchTaken = false;
|
||||
|
||||
// passed a point going backwards, process the data for it
|
||||
var point = this.path.processData(this, this._currentPoint, true);
|
||||
this.follower.events.onPathPointReached.dispatch(this.follower, point);
|
||||
this.takeBranchIfAvailable();
|
||||
this._currentPoint--;
|
||||
|
||||
// reached the start of the path moving backwards
|
||||
if (this._currentPoint < 0)
|
||||
{
|
||||
if (this.path.loops)
|
||||
{
|
||||
this.follower.events.onPathLoop.dispatch(point);
|
||||
this._currentPoint = this.path.numPoints() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!this.yoyo)
|
||||
{
|
||||
this.follower.events.onPathEnd.dispatch();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.follower.events.onPathYoyo.dispatch();
|
||||
var speed = {min: this.speed.min, max: this.speed.max};
|
||||
this.speed.min = -speed.max;
|
||||
this.speed.max = -speed.min;
|
||||
this._currentPoint = 0;
|
||||
this._currentCurve = this.path.getCurve(this._currentPoint);
|
||||
this._currentDistance = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!branchTaken)
|
||||
{
|
||||
// get the curve for this new point
|
||||
this._currentCurve = this.path.getCurve(this._currentPoint);
|
||||
|
||||
// there isn't one, take a branch if there's one attached here
|
||||
if (!this._currentCurve)
|
||||
{
|
||||
return this.takeBranchIfAvailable();
|
||||
}
|
||||
|
||||
// move backwards to the end of the previous curve in the path
|
||||
this._currentDistance += this._currentCurve.length;
|
||||
}
|
||||
}
|
||||
else // forwards...
|
||||
{
|
||||
this._currentPoint++;
|
||||
|
||||
// reached the end of the path moving forwards
|
||||
if (this.path.atEnd(this._currentPoint))
|
||||
{
|
||||
if (this.path.loops)
|
||||
{
|
||||
// the path loops
|
||||
this.follower.events.onPathLoop.dispatch();
|
||||
this._currentPoint = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the path doesn't loop
|
||||
if (!this.takeBranchIfAvailable())
|
||||
{
|
||||
if (!this.yoyo)
|
||||
{
|
||||
this.follower.events.onPathEnd.dispatch();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.follower.events.onPathYoyo.dispatch();
|
||||
var speed = {min: this.speed.min, max: this.speed.max};
|
||||
this.speed.min = -speed.max;
|
||||
this.speed.max = -speed.min;
|
||||
this._currentPoint = this.path.length - 2;
|
||||
this._currentCurve = this.path.getCurve(this._currentPoint);
|
||||
this._currentDistance = this._currentCurve.length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// passed a point going forwards, process the data for the next one
|
||||
point = this.path.processData(this, this._currentPoint, false);
|
||||
|
||||
this.follower.events.onPathPointReached.dispatch(this.follower, point);
|
||||
|
||||
this.takeBranchIfAvailable();
|
||||
|
||||
// move forwards to the start of the next curve in the path
|
||||
this._currentDistance -= memCurveLength;
|
||||
|
||||
// get the curve for this new point
|
||||
this._currentCurve = this.path.getCurve(this._currentPoint);
|
||||
|
||||
// there isn't one, take a branch if there's one attached here
|
||||
if (!this._currentCurve)
|
||||
{
|
||||
return this.takeBranchIfAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
// update the path speed while we have a reference to the PathPoint handy
|
||||
this._pathSpeed = point.speed;
|
||||
}
|
||||
|
||||
return this.setPosition();
|
||||
|
||||
};
|
||||
|
||||
Phaser.PathFollower.prototype._calculateDistance = function () {
|
||||
|
||||
if (this.speed.min === this.speed.max)
|
||||
{
|
||||
return game.time.elapsed * this.speed.avg * this._pathSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.speed._elapsed += game.time.elapsed;
|
||||
this.speed._current = this.speed.current || this.speed.avg;
|
||||
|
||||
if (this.speed._elapsed >= this.speed.theta)
|
||||
{
|
||||
this.speed._current = this.speed._target;
|
||||
this.speed._target = null;
|
||||
this.speed._elapsed = 0;
|
||||
}
|
||||
|
||||
if (!this.speed._target )
|
||||
{
|
||||
var min = Phaser.Math.clamp(this.speed._current - (this.speed._current * this.speed.lambda), this.speed.min, this.speed.max);
|
||||
var max = Phaser.Math.clamp(this.speed._current + (this.speed._current * this.speed.lambda), this.speed.min, this.speed.max);
|
||||
|
||||
this.speed._target = game.rnd.realInRange(min, max);
|
||||
}
|
||||
|
||||
var step = Phaser.Math.smoothstep(this.speed._elapsed,0,this.speed.theta);
|
||||
|
||||
return Phaser.Math.linear(this.speed._current, this.speed._target, step) * this._pathSpeed;;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// move the attached graphic or physics object to match this PathFollower
|
||||
// @return: false if this PathFollower should be removed from the Path's list of followers
|
||||
Phaser.PathFollower.prototype.setPosition = function () {
|
||||
|
||||
// if the follower object has been destroyed, kill this too
|
||||
if (!this.follower)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this._currentCurve.getPointWithDistance(this._currentDistance, Phaser.PathFollower.tempPoint);
|
||||
|
||||
var ox = this.offset.x;
|
||||
var oy = this.offset.y;
|
||||
|
||||
if (this._angularOffset.distance != 0)
|
||||
{
|
||||
var angle = (this.follower.rotation + this._angularOffset.angle);
|
||||
ox += Math.cos(angle) * this._angularOffset.distance;
|
||||
oy += Math.sin(angle) * this._angularOffset.distance;
|
||||
}
|
||||
|
||||
if (this.physicsAdjustTime)
|
||||
{
|
||||
// move the virtual particle along the path
|
||||
this.virtualParticle.x = Phaser.PathFollower.tempPoint.x + ox;
|
||||
this.virtualParticle.y = Phaser.PathFollower.tempPoint.y + oy;
|
||||
|
||||
// move the physics body towards the virtual particle
|
||||
if (this.follower.body)
|
||||
{
|
||||
game.physics.arcade.moveToObject(this.follower, this.virtualParticle, 100, this.physicsAdjustTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the follower along the path by directly adjusting it's x,y coordinates
|
||||
this.follower.x = Phaser.PathFollower.tempPoint.x + ox;
|
||||
this.follower.y = Phaser.PathFollower.tempPoint.y + oy;
|
||||
}
|
||||
|
||||
// if this follower should turn to follow the path, and it has a rotation member
|
||||
if (this._turnOffset !== undefined && this.follower.rotation !== undefined)
|
||||
{
|
||||
// turn to follow the path with a fixed offset of _turnOffset
|
||||
this.follower.rotation = this._currentCurve.getAngleWithDistance(this._currentDistance) + this._turnOffset;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
// if we've reached the end of a path or a branch, take any branch that is available rather than die
|
||||
// @return: true if successful, false if no branch is available
|
||||
Phaser.PathFollower.prototype.takeBranchIfAvailable = function () {
|
||||
|
||||
var p = new Phaser.PathPoint();
|
||||
|
||||
if (this.path.getPathPoint(this._currentPoint, p))
|
||||
{
|
||||
// kill this follower if there isn't a branch for us to take
|
||||
if (!p.branchPath || !this.branchPredicate || !this.branchPredicate(p, this.path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// changePath calls back to redo this function, exit after calling it
|
||||
|
||||
this.changePath(p.branchPath, p.branchPointIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
// follow a different path
|
||||
Phaser.PathFollower.prototype.changePath = function (branchPath, branchPointIndex) {
|
||||
|
||||
// change to the new path
|
||||
this.path = branchPath;
|
||||
|
||||
// get the speed of the new path
|
||||
this._pathSpeed = this.path.getPathPointReference(0).speed;
|
||||
|
||||
// set my position on the new path
|
||||
this._currentPoint = branchPointIndex;
|
||||
|
||||
// update the curve if we've moved past a Path point
|
||||
this._currentCurve = this.path.getCurve(this._currentPoint);
|
||||
|
||||
// we've finished the path
|
||||
if (!this._currentCurve)
|
||||
{
|
||||
return this.takeBranchIfAvailable();
|
||||
}
|
||||
|
||||
// move me to the correct position on the new curve
|
||||
this.setPosition();
|
||||
|
||||
};
|
||||
|
||||
// change this follower's x,y offset values
|
||||
Phaser.PathFollower.prototype.setOffset = function (x, y) {
|
||||
|
||||
// remove any prior offset from the follower's position
|
||||
this.follower.x -= this.offset.x;
|
||||
this.follower.y -= this.offset.y;
|
||||
|
||||
// set the new offset for this PathFollower
|
||||
this.offset.x = x;
|
||||
this.offset.y = y;
|
||||
|
||||
// add the offset into the follower's position straight away
|
||||
this.follower.x += this.offset.x;
|
||||
this.follower.y += this.offset.y;
|
||||
|
||||
};
|
||||
|
||||
// set this follower's angular offset values
|
||||
Phaser.PathFollower.prototype.setAngularOffset = function (angle, distance) {
|
||||
|
||||
this._angularOffset.angle = angle;
|
||||
this._angularOffset.distance = distance;
|
||||
|
||||
};
|
||||
|
||||
// cause this follower to pause for 'delay' milliseconds
|
||||
Phaser.PathFollower.prototype.pause = function (delay) {
|
||||
|
||||
this._pauseTime = game.time.now + delay;
|
||||
|
||||
if (this.follower.animations !== undefined)
|
||||
{
|
||||
if (this.follower.animations.currentAnim)
|
||||
{
|
||||
this.follower.animations.paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Object.defineProperty(Phaser.PathFollower.prototype, 'paused', {
|
||||
|
||||
get: function() {
|
||||
return !!this._pauseTime;
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
if(!!val) {
|
||||
this.pause(Number.MAX_VALUE);
|
||||
} else {
|
||||
this._pauseTime = game.time.now - 1;
|
||||
}
|
||||
},
|
||||
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
|
||||
});
|
352
src/plugins/path/PathManagerPlugin.js
Normal file
352
src/plugins/path/PathManagerPlugin.js
Normal file
|
@ -0,0 +1,352 @@
|
|||
/**
|
||||
* @author Richard Davey <rich@photonstorm.com>
|
||||
* @author Pete Baron <pete@photonstorm.com>
|
||||
* @copyright 2016 Photon Storm Ltd.
|
||||
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
||||
*/
|
||||
|
||||
/**
|
||||
* PathManager controls a list of Paths and a list of PathFollowers.
|
||||
* It is the central control for the majority of the Pathing API.
|
||||
*
|
||||
* @method PathManager
|
||||
* @param {Phaser.Game} game - A reference to the Phaser Game instance.
|
||||
* @param {[type]} parent - ?
|
||||
*/
|
||||
Phaser.Plugin.PathManager = function(game, parent) {
|
||||
|
||||
Phaser.Plugin.call(this, game, parent);
|
||||
|
||||
/**
|
||||
* @property {array} _list - list of paths
|
||||
* @private
|
||||
*/
|
||||
this._list = [];
|
||||
|
||||
/**
|
||||
* @property {array} _followers - list of path followers
|
||||
* @private
|
||||
*/
|
||||
this._followers = [];
|
||||
|
||||
this._branchRegistry = {};
|
||||
|
||||
};
|
||||
|
||||
Phaser.Plugin.PathManager.prototype = Object.create(Phaser.Plugin.prototype);
|
||||
Phaser.Plugin.PathManager.prototype.constructor = Phaser.Plugin.PathManager;
|
||||
|
||||
/**
|
||||
* create a new Path from JSON data
|
||||
*
|
||||
* JSON data format:
|
||||
* required: "coordinateSystem":, "smoothness":, "loops":, "speed":, "pointList":[ {"x":, "y":}, ... ]
|
||||
* optional: "branchFrom": { "path":, "point": }, "joinTo": { "path":, "point": }
|
||||
*/
|
||||
Phaser.Plugin.PathManager.prototype.createPathsFromJSON = function(jsonKey) {
|
||||
|
||||
var parse = this.game.cache.getJSON(jsonKey);
|
||||
var path;
|
||||
var createdPaths = [];
|
||||
var branchList = [];
|
||||
|
||||
parse.paths.forEach(function(config) {
|
||||
path = new Phaser.Path(config.coordinateSystem, config.loops);
|
||||
path.name = config.name;
|
||||
this.addPoints(path, config.pointList, config.speed);
|
||||
this._list.push(path);
|
||||
createdPaths.push(path);
|
||||
config.pointList.reduce(function(list, pnt, index) {
|
||||
if (pnt.branchType === Phaser.Path.BranchTypes.ATTACHED) {
|
||||
list.push({
|
||||
path: path.name,
|
||||
branchPath: pnt.branchPath,
|
||||
pointIndex: index,
|
||||
type: pnt.branchType
|
||||
});
|
||||
} else if (pnt.branchType === Phaser.Path.BranchTypes.JOINED) {
|
||||
list.push({
|
||||
path: pnt.branchPath,
|
||||
branchPath: path.name,
|
||||
pointIndex: pnt.branchPointIndex,
|
||||
type: pnt.branchType
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}, branchList);
|
||||
}, this);
|
||||
branchList.forEach(function(branch) {
|
||||
var mainPath = this.findPathByName(branch.path);
|
||||
var branchPath = this.findPathByName(branch.branchPath);
|
||||
var mainPathPointIndex = branch.pointIndex;
|
||||
if (branch.type === Phaser.Path.BranchTypes.ATTACHED) {
|
||||
this.attachBranch(branchPath, mainPath, mainPathPointIndex);
|
||||
} else if (branch.type === Phaser.Path.BranchTypes.JOINED) {
|
||||
this.joinBranch(branchPath, mainPath, mainPathPointIndex, false);
|
||||
}
|
||||
}, this);
|
||||
|
||||
return createdPaths;
|
||||
};
|
||||
|
||||
Phaser.Plugin.PathManager.prototype.addPath = function(path) {
|
||||
|
||||
// if path has points then addPoints, otherwise don't
|
||||
// this.addPoints(path, parse.pointList, parse.speed);
|
||||
|
||||
this._list.push(path);
|
||||
|
||||
return path;
|
||||
|
||||
};
|
||||
|
||||
// create a branching path and attach the start to an existing path
|
||||
// when a PathFollower encounters the attachment point, it will be able to switch onto this new branch
|
||||
//
|
||||
// @param: count {value, optional}, make this branch counted (it won't be taken until a follower has passed it enough times)
|
||||
Phaser.Plugin.PathManager.prototype.attachBranch = function(branchPath, mainPath, mainPathPointIndex, count) {
|
||||
|
||||
if (typeof mainPath === 'string') {
|
||||
mainPath = this.findPathByName(mainPath);
|
||||
}
|
||||
|
||||
var branchFromPoint = new Phaser.PathPoint();
|
||||
|
||||
if (mainPath.getPathPoint(mainPathPointIndex, branchFromPoint)) {
|
||||
// move the first point of the branchPath to the branchFromPoint location
|
||||
branchPath.origin = branchFromPoint;
|
||||
var branchToPoint = branchPath.getPathPointReference(0);
|
||||
|
||||
// attach the branch (use point reference so the changes go into the path)
|
||||
var branchFromPointRef = mainPath.getPathPointReference(mainPathPointIndex);
|
||||
|
||||
this._branchAttach(branchFromPointRef, branchPath, 0);
|
||||
branchFromPointRef.branchType = Phaser.Path.BranchTypes.ATTACHED;
|
||||
|
||||
// attach the branch's first point back to where it branched off from (for path reversal)
|
||||
branchToPoint.branchPath = mainPath;
|
||||
|
||||
branchToPoint.branchPointIndex = mainPathPointIndex;
|
||||
|
||||
// make sure this branch knows that it's using offset coordinates based on the first path point location
|
||||
branchPath.coordinateSystem = Phaser.Path.CoordinateSystems.OFFSET;
|
||||
branchPath.type = Phaser.Path.PathTypes.BRANCH;
|
||||
|
||||
// set up counted branches data
|
||||
if (count !== undefined) {
|
||||
branchFromPointRef.data = {
|
||||
type: Phaser.PathPoint.DATA_COUNTER,
|
||||
value: count
|
||||
};
|
||||
}
|
||||
|
||||
if (this._branchRegistry[branchPath.name]) {
|
||||
this._branchRegistry[branchPath.name].push(branchFromPointRef);
|
||||
} else {
|
||||
this._branchRegistry[branchPath.name] = [branchFromPointRef];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// attach the end of a path to an existing path
|
||||
// when a PathFollower encounters the attachment point, it will automatically switch onto the attached path
|
||||
Phaser.Plugin.PathManager.prototype.joinBranch = function(branchPath, mainPath, mainPathPointIndex, addPoint) {
|
||||
if (typeof addPoint === 'undefined') {
|
||||
addPoint = true;
|
||||
}
|
||||
if (typeof mainPath === 'string') {
|
||||
mainPath = this.findPathByName(mainPath);
|
||||
}
|
||||
|
||||
var mainPathJoinPoint, branchLastPoint;
|
||||
|
||||
mainPathJoinPoint = new Phaser.PathPoint();
|
||||
mainPath.getPathPoint(mainPathPointIndex, mainPathJoinPoint);
|
||||
|
||||
if (mainPathJoinPoint) {
|
||||
if (addPoint) {
|
||||
|
||||
|
||||
var newBranchPoint = new Phaser.PathPoint();
|
||||
if (branchPath.getPathPoint(0, newBranchPoint)) {
|
||||
// make sure the newly added last path point is relative to the previously added first path point for the branch path by subtracting it out
|
||||
branchLastPoint = branchPath.addPathPoint(mainPathJoinPoint.x - newBranchPoint.x, mainPathJoinPoint.y - newBranchPoint.y, mainPathJoinPoint.vx, mainPathJoinPoint.vy, 1.0);
|
||||
this._branchAttach(branchLastPoint, mainPath, mainPathPointIndex);
|
||||
}
|
||||
} else {
|
||||
branchLastPoint = branchPath.getPathPointReference(branchPath.length - 1);
|
||||
this._branchAttach(branchLastPoint, mainPath, mainPathPointIndex);
|
||||
}
|
||||
branchLastPoint.branchType = Phaser.Path.BranchTypes.JOINED;
|
||||
}
|
||||
if (this._branchRegistry[branchPath.name]) {
|
||||
this._branchRegistry[branchPath.name].push(branchLastPoint);
|
||||
} else {
|
||||
this._branchRegistry[branchPath.name] = [branchLastPoint];
|
||||
}
|
||||
};
|
||||
|
||||
// internal function, set the branching parameters of a PathPoint
|
||||
Phaser.Plugin.PathManager.prototype._branchAttach = function(attachPoint, branchingPath, branchToPointIndex) {
|
||||
attachPoint.branchPath = branchingPath;
|
||||
attachPoint.branchPointIndex = branchToPointIndex;
|
||||
};
|
||||
|
||||
Phaser.Plugin.PathManager.prototype._branchDetach = function(attachedPoint) {
|
||||
attachedPoint.branchPath = null;
|
||||
attachedPoint.branchPointIndex = null;
|
||||
};
|
||||
|
||||
|
||||
Phaser.Plugin.PathManager.prototype.removeBranch = function(branch) {
|
||||
if (typeof branch === 'string') {
|
||||
branch = this.findPathByName(branch);
|
||||
}
|
||||
this._branchRegistry[branch.name].forEach(function(point) {
|
||||
this._branchDetach(point);
|
||||
}, this);
|
||||
this._branchRegistry[branch.name] = null;
|
||||
this.removePath(this.pathIndex(branch));
|
||||
|
||||
};
|
||||
|
||||
// @return: the Path object which is at 'index' in the list
|
||||
Phaser.Plugin.PathManager.prototype.getPath = function(index) {
|
||||
return this._list[index];
|
||||
};
|
||||
|
||||
|
||||
|
||||
// add a list of points to a Path
|
||||
Phaser.Plugin.PathManager.prototype.addPoints = function(path, pointList, speed) {
|
||||
if (speed === undefined) speed = 1.0;
|
||||
|
||||
for (var i = 0; i < pointList.length; i++) {
|
||||
path.addPathPoint(pointList[i].x, pointList[i].y, pointList[i].vx, pointList[i].vy, speed, pointList[i].data);
|
||||
}
|
||||
|
||||
return path.numPoints();
|
||||
};
|
||||
|
||||
// @return: the Path object matching 'name' in the list
|
||||
Phaser.Plugin.PathManager.prototype.findPathByName = function(name) {
|
||||
for (var i = 0; i < this._list.length; i++) {
|
||||
if (this._list[i].name == name) {
|
||||
return this._list[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Phaser.Plugin.PathManager.prototype.findPathByPoint = function(point) {
|
||||
var l = this._list.length;
|
||||
|
||||
for (var i = 0; i < l; i++) {
|
||||
if (this._list[i].pointIndex(point) > -1) {
|
||||
return this._list[i];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* FOLLOWERS
|
||||
*
|
||||
* the following functions control PathFollower objects
|
||||
*
|
||||
*/
|
||||
|
||||
// create a new PathFollower and add it to the list
|
||||
// @param: physicsAdjustTime - how quickly does a physics object attempt to get back to the path's virtual particle position (milliseconds), 0 = it's not a physics object
|
||||
// @return: the new PathFollower object
|
||||
Phaser.Plugin.PathManager.prototype.addFollower = function(path, follower, speed, rotationOffset, angularOffset, callbackAtEnd, physicsAdjustTime) {
|
||||
|
||||
var f = new Phaser.PathFollower(path, follower, speed, rotationOffset, angularOffset, callbackAtEnd, physicsAdjustTime);
|
||||
|
||||
this._followers.push(f);
|
||||
|
||||
return f;
|
||||
|
||||
};
|
||||
|
||||
// update all PathFollower objects in the _followers list
|
||||
// this will automatically move them along the Paths
|
||||
// was called updateFollowers
|
||||
Phaser.Plugin.PathManager.prototype.update = function() {
|
||||
|
||||
// move this to a plugin var
|
||||
//var elapsedTime = 1.0;
|
||||
|
||||
for (var i = this._followers.length - 1; i >= 0; --i) {
|
||||
var f = this._followers[i];
|
||||
|
||||
// when a follower's update returns false, kill it
|
||||
if (!f.update(this.game.time.elpased)) {
|
||||
// callback for this follower when it dies
|
||||
if (f.callbackAtEnd) {
|
||||
f.callbackAtEnd(f.follower);
|
||||
}
|
||||
|
||||
// destroy the follower
|
||||
f.destroy();
|
||||
|
||||
// remove the follower from the list
|
||||
this._followers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// remove all PathFollowers on this path without destroying their attached graphic objects
|
||||
// (eg. a long line of enemies use a path to enter, then switch to AI control on arrival maintaining their relative positions)
|
||||
Phaser.Plugin.PathManager.prototype.removeAllFollowers = function(path) {
|
||||
for (var i = this._followers.length - 1; i >= 0; --i) {
|
||||
var f = this._followers[i];
|
||||
|
||||
if (f.path == path) {
|
||||
// callback for this follower when it dies
|
||||
if (f.callbackAtEnd) {
|
||||
f.callbackAtEnd(f.follower);
|
||||
}
|
||||
|
||||
// destroy the follower
|
||||
f.destroy();
|
||||
|
||||
// remove the follower from the list
|
||||
this._followers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Phaser.Plugin.PathManager.prototype.pathIndex = function(path) {
|
||||
return this._list.indexOf(path);
|
||||
};
|
||||
|
||||
Phaser.Plugin.PathManager.prototype.removePath = function(pathIndex) {
|
||||
this.removeAllFollowers(this.getPath(pathIndex));
|
||||
if (pathIndex < this._list.length) {
|
||||
return this._list.splice(pathIndex, 1);
|
||||
} else {
|
||||
throw new Error("ERROR: Cannot remove non-existent path");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* DEBUG DRAWING OF ALL PATHS
|
||||
*/
|
||||
|
||||
// draw all paths
|
||||
Phaser.Plugin.PathManager.prototype.drawPaths = function(graphics) {
|
||||
for (var i = 0; i < this._list.length; i++) {
|
||||
this._list[i].debug(graphics);
|
||||
}
|
||||
};
|
258
src/plugins/path/PathPoint.js
Normal file
258
src/plugins/path/PathPoint.js
Normal file
|
@ -0,0 +1,258 @@
|
|||
/**
|
||||
* @author Richard Davey <rich@photonstorm.com>
|
||||
* @author Pete Baron <pete@photonstorm.com>
|
||||
* @copyright 2016 Photon Storm Ltd.
|
||||
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The PathPoint class contains data and functions for each point on a Path.
|
||||
*
|
||||
* @class Phaser.PathPoint
|
||||
* @constructor
|
||||
* @param {number} x - The x coordinate of the PathPoint.
|
||||
* @param {number} y - The y coordinate of the PathPoint.
|
||||
* @param {number} vx - The x coordinate of the tangent vector to create the curve from.
|
||||
* @param {number} vy - The y coordinate of the tangent vector to create the curve from.
|
||||
* @param {number} [speed=1] - The speed multiplier for PathFollowers on this Path segment.
|
||||
* @param {object} [data] - The data associated with this point, e.g.: { type: PathPoint.DATA_VALUE, value: XXX }
|
||||
* @param {Phaser.Path} [branchPath] - A branched path which is attached to this point.
|
||||
* @param {number} [branchPointIndex] - The index where the branch is attached to on the new path.
|
||||
*/
|
||||
Phaser.PathPoint = function (x, y, vx, vy, speed, data, branchPath, branchPointIndex) {
|
||||
|
||||
if (speed === undefined) { speed = 1; }
|
||||
if (data === undefined) { data = { type: 0, value: 0 }; }
|
||||
if (branchPath === undefined) { branchPath = null; }
|
||||
if (branchPointIndex === undefined) { branchPointIndex = 0; }
|
||||
|
||||
/**
|
||||
* @property {number} x - The x coordinate of the PathPoint.
|
||||
*/
|
||||
this.x = x;
|
||||
|
||||
/**
|
||||
* @property {number} y - The y coordinate of the PathPoint.
|
||||
*/
|
||||
this.y = y;
|
||||
|
||||
/**
|
||||
* @property {number} vx - The x coordinate of the tangent vector to create the curve from.
|
||||
*/
|
||||
this.vx = vx;
|
||||
|
||||
/**
|
||||
* @property {number} vy - The y coordinate of the tangent vector to create the curve from.
|
||||
*/
|
||||
this.vy = vy;
|
||||
|
||||
/**
|
||||
* @property {number} speed - The speed multiplier for PathFollowers on this path segment.
|
||||
*/
|
||||
this.speed = speed;
|
||||
|
||||
/**
|
||||
* @property {object} data - Data associated with this point eg: { type: PathPoint.DATA_VALUE, value: XXX }
|
||||
*/
|
||||
this.data = data;
|
||||
|
||||
/**
|
||||
* @property {Phaser.Path} branchPath - A branched path which is attached at this point.
|
||||
*/
|
||||
this.branchPath = branchPath;
|
||||
|
||||
/**
|
||||
* @property {number} branchPointIndex - The index where the branch is attached to on the new path.
|
||||
*/
|
||||
this.branchPointIndex = branchPointIndex;
|
||||
|
||||
/**
|
||||
* @property {number} branchType - The branch type of the path this point is on. Either 0 (attached) or 1 (joined)
|
||||
*/
|
||||
this.branchType = 0;
|
||||
|
||||
/**
|
||||
* @property {number} curve - Once the Hermite curve is calculated, store it to avoid recalculation later.
|
||||
* @protected
|
||||
*/
|
||||
this.curve = null;
|
||||
|
||||
/**
|
||||
* @property {boolean} active - Is this point a selected (or active) point?
|
||||
* @warn For Path Editor use only
|
||||
*/
|
||||
this.active = false;
|
||||
|
||||
/**
|
||||
* @property {array} controlPoints - A list of Phaser.Point objects representing the control points on the segment.
|
||||
* @warn For Path Editor use only
|
||||
*/
|
||||
this.controlPoints = null;
|
||||
|
||||
};
|
||||
|
||||
Phaser.PathPoint.prototype.constructor = Phaser.PathPoint;
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.PathPoint.DATA_NONE = 0;
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.PathPoint.DATA_PAUSE = 1;
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.PathPoint.DATA_VALUE = 2;
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
Phaser.PathPoint.DATA_COUNTER = 3;
|
||||
|
||||
Phaser.PathPoint.prototype = {
|
||||
|
||||
/**
|
||||
* Sets the x, y and optionally vx and vy properties of this PathPoint.
|
||||
*
|
||||
* @method Phaser.PathPoint#setTo
|
||||
* @param {number} x - The x coordinate of the PathPoint.
|
||||
* @param {number} y - The y coordinate of the PathPoint.
|
||||
* @param {number} [vx] - The x coordinate of the tangent vector to create the curve from.
|
||||
* @param {number} [vy] - The y coordinate of the tangent vector to create the curve from.
|
||||
* @return {Phaser.PathPoint} This object.
|
||||
*/
|
||||
setTo: function (x, y, vx, vy) {
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
if (vx !== undefined)
|
||||
{
|
||||
this.vx = vx;
|
||||
}
|
||||
|
||||
if (vy !== undefined)
|
||||
{
|
||||
this.vy = vy;
|
||||
}
|
||||
|
||||
// Invalidate the pre-calculated curve to force it to recalculate with these new settings
|
||||
this.curve = null;
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the tangent vector properties of this PathPoint.
|
||||
*
|
||||
* @method Phaser.PathPoint#setTangent
|
||||
* @param {number} vx - The x coordinate of the tangent vector to create the curve from.
|
||||
* @param {number} vy - The y coordinate of the tangent vector to create the curve from.
|
||||
* @return {Phaser.PathPoint} This object.
|
||||
*/
|
||||
setTangent: function (vx, vy) {
|
||||
|
||||
this.vx = vx;
|
||||
this.vy = vy;
|
||||
|
||||
// Invalidate the pre-calculated curve to force it to recalculate with these new settings
|
||||
this.curve = null;
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a clone of this PathPoint object.
|
||||
*
|
||||
* @method Phaser.PathPoint#clone
|
||||
* @param {Phaser.PathPoint} [out] - An optional PathPoint object into which this object is cloned. If no object is provided a new PathPoint is created.
|
||||
* @return {Phaser.PathPoint} A clone of this PathPoint.
|
||||
*/
|
||||
clone: function (out) {
|
||||
|
||||
if (out === undefined) { out = new Phaser.PathPoint(); }
|
||||
|
||||
return out.copy(this);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Copies all of the values from the given PathPoint object into this PathPoint.
|
||||
* The source PathPoint is untouched by this operation.
|
||||
*
|
||||
* @method Phaser.PathPoint#copy
|
||||
* @param {Phaser.PathPoint} source - The PathPoint object to copy the values from.
|
||||
* @return {Phaser.PathPoint} This PathPoint object.
|
||||
*/
|
||||
copy: function (source) {
|
||||
|
||||
this.x = source.x;
|
||||
this.y = source.y;
|
||||
this.vx = source.vx;
|
||||
this.vy = source.vy;
|
||||
this.speed = source.speed;
|
||||
this.data = source.data;
|
||||
this.branchPath = source.branchPath;
|
||||
this.branchPointIndex = source.branchPointIndex;
|
||||
this.curve = null;
|
||||
this.active = source.active;
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Compare this PathPoint with another PathPoint object and return `true`
|
||||
* if they have the same `x`, `y` and `speed` properties, after taking the optional
|
||||
* offset values into consideration.
|
||||
*
|
||||
* @method Phaser.PathPoint#equals
|
||||
* @param {Phaser.PathPoint} pathPoint - The PathPoint to compare against this PathPoint.
|
||||
* @param {number} [offsetX=0] - A value to apply to the x coordinate before comparison.
|
||||
* @param {number} [offsetY=0] - A value to apply to the y coordinate before comparison.
|
||||
* @return {boolean} True if the two PathPoint objects match, after the offsets are applied, or false if they don't.
|
||||
*/
|
||||
equals: function (pathPoint, offsetX, offsetY) {
|
||||
|
||||
if (offsetX === undefined) { offsetX = 0; }
|
||||
if (offsetY === undefined) { offsetY = 0; }
|
||||
|
||||
return (this.x === pathPoint.x + offsetX &&
|
||||
this.y === pathPoint.y + offsetY &&
|
||||
this.speed === pathPoint.speed);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes this PathPoint into a JSON object and returns it.
|
||||
*
|
||||
* @method Phaser.PathPoint#toJSON
|
||||
* @return {Object} A JSON object representing this PathPoint.
|
||||
*/
|
||||
toJSON: function () {
|
||||
|
||||
return {
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
vx: this.vx,
|
||||
vy: this.vy,
|
||||
speed: this.speed,
|
||||
data: this.data,
|
||||
branchPath: !!this.branchPath ? this.branchPath.name : null,
|
||||
branchPointIndex: this.branchPointIndex,
|
||||
branchType: this.branchType
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
};
|
Loading…
Add table
Reference in a new issue