diff --git a/Docs/phaser_rotate4.png b/Docs/phaser_rotate4.png
new file mode 100644
index 00000000..fee589a0
Binary files /dev/null and b/Docs/phaser_rotate4.png differ
diff --git a/Phaser/Game.ts b/Phaser/Game.ts
index 81bb3eba..76c1ec1d 100644
--- a/Phaser/Game.ts
+++ b/Phaser/Game.ts
@@ -15,7 +15,9 @@
///
///
///
+///
///
+///
///
///
///
@@ -230,6 +232,12 @@ module Phaser {
*/
public tweens: TweenManager;
+ /**
+ * Reference to the verlet manager.
+ * @type {VerletManager}
+ */
+ public verlet: Phaser.Verlet.VerletManager;
+
/**
* Reference to the world.
* @type {World}
@@ -292,6 +300,7 @@ module Phaser {
this.tweens = new TweenManager(this);
this.input = new Input(this);
this.rnd = new RandomDataGenerator([(Date.now() * Math.random()).toString()]);
+ this.verlet = new Phaser.Verlet.VerletManager(this, width, height);
this.framerate = 60;
this.isBooted = true;
@@ -368,6 +377,7 @@ module Phaser {
this.tweens.update();
this.input.update();
this.stage.update();
+ this.verlet.update();
this._accumulator += this.time.delta;
diff --git a/Phaser/GameMath.ts b/Phaser/GameMath.ts
index 3a20f2d9..8138252b 100644
--- a/Phaser/GameMath.ts
+++ b/Phaser/GameMath.ts
@@ -1022,6 +1022,45 @@ module Phaser {
}
+ /**
+ * Returns the distance from this Point object to the given Point object.
+ * @method distanceFrom
+ * @param {Point} target - The destination Point object.
+ * @param {Boolean} round - Round the distance to the nearest integer (default false)
+ * @return {Number} The distance between this Point object and the destination Point object.
+ **/
+ public static distanceBetween(x1: number, y1: number, x2: number, y2: number): number {
+
+ var dx = x1 - x2;
+ var dy = y1 - y2;
+
+ return Math.sqrt(dx * dx + dy * dy);
+
+ }
+
+ /**
+ * Rotates a point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param angle {number} The angle of the rotation in radians
+ * @param point {Point} The point object to perform the rotation on
+ * @return The modified point object
+ */
+ public rotatePoint(x: number, y: number, angle: number, point) {
+
+ var s: number = Math.sin(angle);
+ var c: number = Math.cos(angle);
+
+ point.x -= x;
+ point.y -= y;
+
+ var newX: number = point.x * c - point.y * s;
+ var newY: number = point.x * s - point.y * c;
+
+ return point.setTo(newX + x, newY + y);
+
+ }
+
}
}
\ No newline at end of file
diff --git a/Phaser/Group.ts b/Phaser/Group.ts
index ce76aca9..f830cbd4 100644
--- a/Phaser/Group.ts
+++ b/Phaser/Group.ts
@@ -21,9 +21,31 @@ module Phaser {
this._maxSize = MaxSize;
this._marker = 0;
this._sortIndex = null;
+ this.cameraBlacklist = [];
}
+ /**
+ * Internal tracker for the maximum capacity of the group.
+ * Default is 0, or no max capacity.
+ */
+ private _maxSize: number;
+
+ /**
+ * Internal helper variable for recycling objects a la Emitter.
+ */
+ private _marker: number;
+
+ /**
+ * Helper for sort.
+ */
+ private _sortIndex: string;
+
+ /**
+ * Helper for sort.
+ */
+ private _sortOrder: number;
+
/**
* Use with sort() to sort in ascending order.
*/
@@ -47,25 +69,62 @@ module Phaser {
public length: number;
/**
- * Internal tracker for the maximum capacity of the group.
- * Default is 0, or no max capacity.
+ * You can set a globalCompositeOperation that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to apply an effect like 'lighten' to a whole group of children as it saves doing it one-by-one.
+ * If this value is set it will call a canvas context save and restore before and after the render pass.
+ * Set to null to disable.
*/
- private _maxSize: number;
+ public globalCompositeOperation: string = null;
/**
- * Internal helper variable for recycling objects a la Emitter.
+ * You can set an alpha value on this Group that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to alpha a whole group of children as it saves doing it one-by-one.
+ * Set to 0 to disable.
*/
- private _marker: number;
+ public alpha: number = 0;
/**
- * Helper for sort.
+ * An Array of Cameras to which this Group, or any of its children, won't render
+ * @type {Array}
*/
- private _sortIndex: string;
+ public cameraBlacklist: number[];
/**
- * Helper for sort.
+ * If you do not wish this object to be visible to a specific camera, pass the camera here.
+ *
+ * @param camera {Camera} The specific camera.
+ */
+ public hideFromCamera(camera: Camera) {
+
+ if (this.cameraBlacklist.indexOf(camera.ID) == -1)
+ {
+ this.cameraBlacklist.push(camera.ID);
+ }
+
+ }
+
+ /**
+ * Make this object only visible to a specific camera.
+ *
+ * @param camera {Camera} The camera you wish it to be visible.
+ */
+ public showToCamera(camera: Camera) {
+
+ if (this.cameraBlacklist.indexOf(camera.ID) !== -1)
+ {
+ this.cameraBlacklist.slice(this.cameraBlacklist.indexOf(camera.ID), 1);
+ }
+
+ }
+
+ /**
+ * This clears the camera black list, making the GameObject visible to all cameras.
*/
- private _sortOrder: number;
+ public clearCameraList() {
+
+ this.cameraBlacklist.length = 0;
+
+ }
/**
* Override this function to handle any deleting or "shutdown" type operations you might need,
@@ -126,11 +185,28 @@ module Phaser {
*/
public render(camera: Camera, cameraOffsetX: number, cameraOffsetY: number, forceRender?: bool = false) {
+ if (this.cameraBlacklist.indexOf(camera.ID) !== -1)
+ {
+ return;
+ }
+
if (this.ignoreGlobalRender && forceRender == false)
{
return;
}
+ if (this.globalCompositeOperation)
+ {
+ this._game.stage.context.save();
+ this._game.stage.context.globalCompositeOperation = this.globalCompositeOperation;
+ }
+
+ if (this.alpha > 0)
+ {
+ var prevAlpha: number = this._game.stage.context.globalAlpha;
+ this._game.stage.context.globalAlpha = this.alpha;
+ }
+
var basic: Basic;
var i: number = 0;
@@ -143,6 +219,16 @@ module Phaser {
basic.render(camera, cameraOffsetX, cameraOffsetY, forceRender);
}
}
+
+ if (this.alpha > 0)
+ {
+ this._game.stage.context.globalAlpha = prevAlpha;
+ }
+
+ if (this.globalCompositeOperation)
+ {
+ this._game.stage.context.restore();
+ }
}
/**
diff --git a/Phaser/Phaser.csproj b/Phaser/Phaser.csproj
index d627d288..4bce4c50 100644
--- a/Phaser/Phaser.csproj
+++ b/Phaser/Phaser.csproj
@@ -105,9 +105,25 @@
Tilemap.ts
+
+
+
+ VerletManager.ts
+
+
+ AngleConstraint.ts
+
Circle.ts
+
+
+
+ Composite.ts
+
+
+ DistanceConstraint.ts
+
IntersectResult.ts
@@ -118,6 +134,14 @@
MicroPoint.ts
+
+
+
+ Particle.ts
+
+
+ PinConstraint.ts
+
Point.ts
@@ -128,6 +152,10 @@
Rectangle.ts
+
+
+ Vector2.ts
+
SoundManager.ts
diff --git a/Phaser/Stage.ts b/Phaser/Stage.ts
index d2e57b8a..24bb9b3e 100644
--- a/Phaser/Stage.ts
+++ b/Phaser/Stage.ts
@@ -47,6 +47,7 @@ module Phaser {
this.canvas.style['ms-touch-action'] = 'none';
this.canvas.style['touch-action'] = 'none';
this.canvas.style.backgroundColor = 'rgb(0,0,0)';
+ this.canvas.oncontextmenu = function(event) { event.preventDefault(); };
this.context = this.canvas.getContext('2d');
diff --git a/Phaser/VerletManager.ts b/Phaser/VerletManager.ts
new file mode 100644
index 00000000..c9a60b69
--- /dev/null
+++ b/Phaser/VerletManager.ts
@@ -0,0 +1,401 @@
+///
+///
+///
+///
+///
+///
+///
+
+/**
+* Phaser - Verlet
+*
+* Based on verlet-js by Sub Protocol released under MIT
+*/
+
+module Phaser.Verlet {
+
+ export class VerletManager {
+
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ constructor(game: Game, width: number, height: number) {
+
+ this._game = game;
+ this.width = width;
+ this.height = height;
+ this.gravity = new Vector2(0, 0.2);
+ this.friction = 0.99;
+ this.groundFriction = 0.8;
+
+ this.canvas = game.stage.canvas;
+ this.context = game.stage.context;
+
+ this._game.input.onDown.add(this.mouseDownHandler, this);
+ this._game.input.onUp.add(this.mouseUpHandler, this);
+
+ }
+
+ private _game: Game;
+
+ public composites = [];
+
+ public width: number;
+ public height: number;
+ public step: number = 32;
+ public gravity: Vector2;
+ public friction: number;
+ public groundFriction: number;
+ public selectionRadius: number = 20;
+ public draggedEntity = null;
+ public highlightColor = '#4f545c';
+
+
+ /**
+ * This class is actually a wrapper of canvas.
+ * @type {HTMLCanvasElement}
+ */
+ public canvas: HTMLCanvasElement;
+
+ /**
+ * Canvas context of this object.
+ * @type {CanvasRenderingContext2D}
+ */
+ public context: CanvasRenderingContext2D;
+
+ /**
+ * Computes time of intersection of a particle with a wall
+ *
+ * @param {Vec2} line wall's root position
+ * @param {Vec2} p particle's position
+ * @param {Vec2} dir walls's direction
+ * @param {Vec2} v particle's velocity
+ */
+ public intersectionTime(wall, p, dir, v) {
+
+ if (dir.x != 0)
+ {
+ var denominator = v.y - dir.y * v.x / dir.x;
+ if (denominator == 0) return undefined; // Movement is parallel to wall
+ var numerator = wall.y + dir.y * (p.x - wall.x) / dir.x - p.y;
+ return numerator / denominator;
+ }
+ else
+ {
+ if (v.x == 0) return undefined; // parallel again
+ var denominator = v.x;
+ var numerator = wall.x - p.x;
+ return numerator / denominator;
+ }
+
+ }
+
+ public intersectionPoint(wall, p, dir, v) {
+ var t = this.intersectionTime(wall, p, dir, v);
+ return new Phaser.Vector2(p.x + v.x * t, p.y + v.y * t);
+ }
+
+ private v = new Phaser.Vector2();
+
+ public bounds(particle: Phaser.Verlet.Particle) {
+
+ this.v.mutableSet(particle.pos);
+ this.v.mutableSub(particle.lastPos);
+
+ if (particle.pos.y > this.height - 1)
+ {
+ particle.pos.mutableSet(
+ this.intersectionPoint(new Phaser.Vector2(0, this.height - 1), particle.lastPos, new Phaser.Vector2(1, 0), this.v));
+ }
+
+ if (particle.pos.x < 0)
+ {
+ particle.pos.mutableSet(
+ this.intersectionPoint(new Phaser.Vector2(0, 0), particle.pos, new Phaser.Vector2(0, 1), this.v));
+ }
+
+ if (particle.pos.x > this.width - 1)
+ {
+ particle.pos.mutableSet(
+ this.intersectionPoint(new Phaser.Vector2(this.width - 1, 0), particle.pos, new Phaser.Vector2(0, 1), this.v));
+ }
+ }
+
+ public OLDbounds(particle: Phaser.Verlet.Particle) {
+
+ if (particle.pos.y > this.height - 1)
+ particle.pos.y = this.height - 1;
+
+ if (particle.pos.x < 0)
+ {
+ var vx = particle.pos.x - particle.lastPos.x;
+ var vy = particle.pos.y - particle.lastPos.y;
+
+ if (vx == 0)
+ {
+ particle.pos.x = 0;
+ }
+ else
+ {
+ var t = -particle.lastPos.x / vx;
+ particle.pos.x = particle.lastPos.x + t * vx;
+ particle.pos.y = particle.lastPos.y + t * vy;
+ }
+ }
+
+ if (particle.pos.x > this.width - 1)
+ particle.pos.x = this.width - 1;
+
+ }
+
+ public createPoint(pos: Vector2) {
+
+ var composite = new Phaser.Verlet.Composite(this._game);
+ composite.particles.push(new Phaser.Verlet.Particle(pos));
+ this.composites.push(composite);
+ return composite;
+
+ }
+
+ public createLineSegments(vertices, stiffness) {
+
+ var i;
+ var composite = new Phaser.Verlet.Composite(this._game);
+
+ for (i in vertices)
+ {
+ composite.particles.push(new Phaser.Verlet.Particle(vertices[i]));
+ if (i > 0)
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[i], composite.particles[i - 1], stiffness));
+ }
+
+ this.composites.push(composite);
+ return composite;
+
+ }
+
+ public createCloth(origin, width, height, segments, pinMod, stiffness) {
+
+ var composite = new Phaser.Verlet.Composite(this._game);
+
+ var xStride = width / segments;
+ var yStride = height / segments;
+
+ var x, y;
+ for (y = 0; y < segments; ++y)
+ {
+ for (x = 0; x < segments; ++x)
+ {
+ var px = origin.x + x * xStride - width / 2 + xStride / 2;
+ var py = origin.y + y * yStride - height / 2 + yStride / 2;
+ composite.particles.push(new Phaser.Verlet.Particle(new Vector2(px, py)));
+
+ if (x > 0)
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[y * segments + x], composite.particles[y * segments + x - 1], stiffness));
+
+ if (y > 0)
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[y * segments + x], composite.particles[(y - 1) * segments + x], stiffness));
+ }
+ }
+
+ for (x = 0; x < segments; ++x)
+ {
+ if (x % pinMod == 0)
+ composite.pin(x);
+ }
+
+ this.composites.push(composite);
+ return composite;
+
+ }
+
+ public createTire(origin, radius, segments, spokeStiffness, treadStiffness) {
+
+ var stride = (2 * Math.PI) / segments;
+ var i;
+
+ var composite = new Phaser.Verlet.Composite(this._game);
+
+ // particles
+ for (i = 0; i < segments; ++i)
+ {
+ var theta = i * stride;
+ composite.particles.push(new Particle(new Vector2(origin.x + Math.cos(theta) * radius, origin.y + Math.sin(theta) * radius)));
+ }
+
+ var center = new Particle(origin);
+ composite.particles.push(center);
+
+ // constraints
+ for (i = 0; i < segments; ++i)
+ {
+ composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[(i + 1) % segments], treadStiffness));
+ composite.constraints.push(new DistanceConstraint(composite.particles[i], center, spokeStiffness))
+ composite.constraints.push(new DistanceConstraint(composite.particles[i], composite.particles[(i + 5) % segments], treadStiffness));
+ }
+
+ this.composites.push(composite);
+
+ return composite;
+ }
+
+ public update() {
+
+ if (this.composites.length == 0)
+ {
+ return;
+ }
+
+ var i, j, c;
+
+ for (c in this.composites)
+ {
+ for (i in this.composites[c].particles)
+ {
+ var particles = this.composites[c].particles;
+
+ // calculate velocity
+ var velocity = particles[i].pos.sub(particles[i].lastPos).scale(this.friction);
+
+ // ground friction
+ if (particles[i].pos.y >= this.height - 1 && velocity.length2() > 0.000001)
+ {
+ var m = velocity.length();
+ velocity.x /= m;
+ velocity.y /= m;
+ velocity.mutableScale(m * this.groundFriction);
+ }
+
+ // save last good state
+ particles[i].lastPos.mutableSet(particles[i].pos);
+
+ // gravity
+ particles[i].pos.mutableAdd(this.gravity);
+
+ // inertia
+ particles[i].pos.mutableAdd(velocity);
+ }
+ }
+
+ // handle dragging of entities
+ if (this.draggedEntity)
+ this.draggedEntity.pos.mutableSet(new Vector2(this._game.input.x, this._game.input.y));
+
+ // relax
+ var stepCoef = 1 / this.step;
+
+ for (c in this.composites)
+ {
+ var constraints = this.composites[c].constraints;
+ for (i = 0; i < this.step; ++i)
+ for (j in constraints)
+ constraints[j].relax(stepCoef);
+ }
+
+ // bounds checking
+ for (c in this.composites)
+ {
+ var particles = this.composites[c].particles;
+ for (i in particles)
+ this.bounds(particles[i]);
+ }
+
+ }
+
+ private mouseDownHandler() {
+
+ var nearest = this.nearestEntity();
+
+ if (nearest)
+ {
+ this.draggedEntity = nearest;
+ }
+ }
+
+ private mouseUpHandler() {
+ this.draggedEntity = null;
+ }
+
+ public nearestEntity() {
+
+ var c, i;
+ var d2Nearest = 0;
+ var entity = null;
+ var constraintsNearest = null;
+
+ // find nearest point
+ for (c in this.composites)
+ {
+ var particles = this.composites[c].particles;
+
+ for (i in particles)
+ {
+ var d2 = particles[i].pos.dist2(new Vector2(this._game.input.x, this._game.input.y));
+
+ if (d2 <= this.selectionRadius * this.selectionRadius && (entity == null || d2 < d2Nearest))
+ {
+ entity = particles[i];
+ constraintsNearest = this.composites[c].constraints;
+ d2Nearest = d2;
+ }
+ }
+ }
+
+ // search for pinned constraints for this entity
+ for (i in constraintsNearest)
+ if (constraintsNearest[i] instanceof PinConstraint && constraintsNearest[i].a == entity)
+ entity = constraintsNearest[i];
+
+ return entity;
+
+ }
+
+ public render() {
+
+ var i, c;
+
+ for (c in this.composites)
+ {
+ // draw constraints
+ if (this.composites[c].drawConstraints)
+ {
+ this.composites[c].drawConstraints(this.context, this.composites[c]);
+ } else
+ {
+ var constraints = this.composites[c].constraints;
+ for (i in constraints)
+ constraints[i].render(this.context);
+ }
+
+ // draw particles
+ if (this.composites[c].drawParticles)
+ {
+ this.composites[c].drawParticles(this.context, this.composites[c]);
+ } else
+ {
+ var particles = this.composites[c].particles;
+ for (i in particles)
+ particles[i].render(this.context);
+ }
+ }
+
+ // highlight nearest / dragged entity
+ var nearest = this.draggedEntity || this.nearestEntity();
+
+ if (nearest)
+ {
+ this.context.beginPath();
+ this.context.arc(nearest.pos.x, nearest.pos.y, 8, 0, 2 * Math.PI);
+ this.context.strokeStyle = this.highlightColor;
+ this.context.stroke();
+ }
+ }
+
+ }
+
+}
diff --git a/Phaser/gameobjects/GameObject.ts b/Phaser/gameobjects/GameObject.ts
index c1d211a8..9ddf6559 100644
--- a/Phaser/gameobjects/GameObject.ts
+++ b/Phaser/gameobjects/GameObject.ts
@@ -41,7 +41,6 @@ module Phaser {
this.scale = new MicroPoint(1, 1);
this.last = new MicroPoint(x, y);
- //this.origin = new MicroPoint(this.frameBounds.halfWidth, this.frameBounds.halfHeight);
this.align = GameObject.ALIGN_TOP_LEFT;
this.mass = 1;
this.elasticity = 0;
diff --git a/Phaser/geom/Point.ts b/Phaser/geom/Point.ts
index e697b369..68b12cd7 100644
--- a/Phaser/geom/Point.ts
+++ b/Phaser/geom/Point.ts
@@ -296,6 +296,32 @@ module Phaser {
}
+ /**
+ * Rotates the point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param {Number} angle The angle in radians (unless asDegrees is true) to return the point from.
+ * @param {Boolean} asDegrees Is the given angle in radians (false) or degrees (true)?
+ * @param {Number} distance An optional distance constraint between the point and the anchor
+ * @return The modified point object
+ */
+ public rotate(cx: number, cy: number, angle: number, asDegrees: bool = false, distance?:number = null) {
+
+ if (asDegrees)
+ {
+ angle = angle * GameMath.DEG_TO_RAD;
+ }
+
+ // Get distance from origin (cx/cy) to this point
+ if (distance === null)
+ {
+ distance = Math.sqrt(((cx - this.x) * (cx - this.x)) + ((cy - this.y) * (cy - this.y)));
+ }
+
+ return this.setTo(cx + distance * Math.cos(angle), cy + distance * Math.sin(angle));
+
+ }
+
/**
* Sets the x and y values of this Point object to the given coordinates.
* @method setTo
diff --git a/Phaser/geom/Quad.ts b/Phaser/geom/Quad.ts
index a920f238..d6cb7aa0 100644
--- a/Phaser/geom/Quad.ts
+++ b/Phaser/geom/Quad.ts
@@ -19,7 +19,7 @@ module Phaser {
* @param {Number} y The y coordinate of the top-left corner of the quad.
* @param {Number} width The width of the quad.
* @param {Number} height The height of the quad.
- * @return {Quad } This object
+ * @return {Quad} This object
**/
constructor(x: number = 0, y: number = 0, width: number = 0, height: number = 0) {
diff --git a/Phaser/geom/Vector2.ts b/Phaser/geom/Vector2.ts
new file mode 100644
index 00000000..7ea3d62e
--- /dev/null
+++ b/Phaser/geom/Vector2.ts
@@ -0,0 +1,151 @@
+///
+
+/**
+* Phaser - Vector2
+*
+* A simple 2-dimensional vector class. Based on the one included with verlet-js by Sub Protocol released under MIT
+*/
+
+module Phaser {
+
+ export class Vector2 {
+
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ constructor(x: number = 0, y: number = 0) {
+
+ this.x = x;
+ this.y = y;
+
+ }
+
+ public x: number;
+ public y: number;
+
+ public setTo(x: number, y: number): Vector2 {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ public add(v: Vector2, output?:Vector2 = new Vector2): Vector2 {
+ return output.setTo(this.x + v.x, this.y + v.y);
+ }
+
+ public sub(v: Vector2, output?:Vector2 = new Vector2): Vector2 {
+ return output.setTo(this.x - v.x, this.y - v.y);
+ }
+
+ public mul(v: Vector2, output?:Vector2 = new Vector2): Vector2 {
+ return output.setTo(this.x * v.x, this.y * v.y);
+ }
+
+ public div(v: Vector2, output?:Vector2 = new Vector2): Vector2 {
+ return output.setTo(this.x / v.x, this.y / v.y);
+ }
+
+ public scale(coef: number, output?:Vector2 = new Vector2): Vector2 {
+ return output.setTo(this.x * coef, this.y * coef);
+ }
+
+ public mutableSet(v: Vector2): Vector2 {
+ this.x = v.x;
+ this.y = v.y;
+ return this;
+ }
+
+ public mutableAdd(v: Vector2): Vector2 {
+ this.x += v.x;
+ this.y += v.y;
+ return this;
+ }
+
+ public mutableSub(v: Vector2): Vector2 {
+ this.x -= v.x;
+ this.y -= v.y;
+ return this;
+ }
+
+ public mutableMul(v: Vector2): Vector2 {
+ this.x *= v.x;
+ this.y *= v.y;
+ return this;
+ }
+
+ public mutableDiv(v: Vector2): Vector2 {
+ this.x /= v.x;
+ this.y /= v.y;
+ return this;
+ }
+
+ public mutableScale(coef: number): Vector2 {
+ this.x *= coef;
+ this.y *= coef;
+ return this;
+ }
+
+ public equals(v: Vector2): bool {
+ return this.x == v.x && this.y == v.y;
+ }
+
+ public epsilonEquals(v: Vector2, epsilon:number): bool {
+ return Math.abs(this.x - v.x) <= epsilon && Math.abs(this.y - v.y) <= epsilon;
+ }
+
+ public length(): number {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ }
+
+ public length2(): number {
+ return this.x * this.x + this.y * this.y;
+ }
+
+ public dist(v: Vector2): number {
+ return Math.sqrt(this.dist2(v));
+ }
+
+ public dist2(v: Vector2): number {
+ return ((v.x - this.x) * (v.x - this.x)) + ((v.y - this.y) * (v.y - this.y));
+ }
+
+ public normal(output?: Vector2 = new Vector2) {
+ var m = Math.sqrt(this.x * this.x + this.y * this.y);
+ return output.setTo(this.x / m, this.y / m);
+ }
+
+ public dot(v: Vector2): number {
+ return this.x * v.x + this.y * v.y;
+ }
+
+ public angle(v: Vector2): number {
+ return Math.atan2(this.x * v.y - this.y * v.x, this.x * v.x + this.y * v.y);
+ }
+
+ public angle2(vLeft: Vector2, vRight: Vector2): number {
+ return vLeft.sub(this).angle(vRight.sub(this));
+ }
+
+ public rotate(origin, theta, output?: Vector2 = new Vector2): Vector2 {
+ var x = this.x - origin.x;
+ var y = this.y - origin.y;
+ return output.setTo(x * Math.cos(theta) - y * Math.sin(theta) + origin.x, x * Math.sin(theta) + y * Math.cos(theta) + origin.y);
+ }
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {string} a string representation of the object.
+ **/
+ public toString(): string {
+ return "[{Vector2 (x=" + this.x + " y=" + this.y + ")}]";
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Phaser/verlet/AngleConstraint.ts b/Phaser/verlet/AngleConstraint.ts
new file mode 100644
index 00000000..3b090b56
--- /dev/null
+++ b/Phaser/verlet/AngleConstraint.ts
@@ -0,0 +1,74 @@
+///
+///
+///
+
+/**
+* Phaser - AngleConstraint
+*
+* constrains 3 particles to an angle
+*/
+
+module Phaser.Verlet {
+
+ export class AngleConstraint {
+
+ /**
+ * Creates a new AngleConstraint object.
+ * @class AngleConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {AngleConstraint} This object
+ **/
+ constructor(a: Phaser.Verlet.Particle, b: Phaser.Verlet.Particle, c: Phaser.Verlet.Particle, stiffness: number) {
+
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.angle = this.b.pos.angle2(this.a.pos, this.c.pos);
+ this.stiffness = stiffness;
+
+ }
+
+ public a: Phaser.Verlet.Particle;
+ public b: Phaser.Verlet.Particle;
+ public c: Phaser.Verlet.Particle;
+ public angle: number;
+ public stiffness: number;
+
+ public relax(stepCoef: number) {
+
+ var angle = this.b.pos.angle2(this.a.pos, this.c.pos);
+ var diff = angle - this.angle;
+
+ if (diff <= -Math.PI)
+ diff += 2 * Math.PI;
+ else if (diff >= Math.PI)
+ diff -= 2 * Math.PI;
+
+ diff *= stepCoef * this.stiffness;
+
+ this.a.pos = this.a.pos.rotate(this.b.pos, diff);
+ this.c.pos = this.c.pos.rotate(this.b.pos, -diff);
+ this.b.pos = this.b.pos.rotate(this.a.pos, diff);
+ this.b.pos = this.b.pos.rotate(this.c.pos, -diff);
+
+ }
+
+ public render(ctx) {
+
+ ctx.beginPath();
+ ctx.moveTo(this.a.pos.x, this.a.pos.y);
+ ctx.lineTo(this.b.pos.x, this.b.pos.y);
+ ctx.lineTo(this.c.pos.x, this.c.pos.y);
+ var tmp = ctx.lineWidth;
+ ctx.lineWidth = 5;
+ ctx.strokeStyle = "rgba(255,255,0,0.2)";
+ ctx.stroke();
+ ctx.lineWidth = tmp;
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Phaser/verlet/Composite.ts b/Phaser/verlet/Composite.ts
new file mode 100644
index 00000000..19055cfa
--- /dev/null
+++ b/Phaser/verlet/Composite.ts
@@ -0,0 +1,78 @@
+///
+///
+///
+///
+
+/**
+* Phaser - Verlet - Composite
+*
+*
+*/
+
+module Phaser.Verlet {
+
+ export class Composite {
+
+ /**
+ * Creates a new Composite object.
+ * @class Composite
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Composite} This object
+ **/
+ constructor(game: Game) {
+
+ this._game = game;
+
+ this.particles = [];
+ this.constraints = [];
+
+ }
+
+ private _game: Game;
+
+ public particles: Phaser.Verlet.Particle[];
+ public constraints;
+ public drawParticles = null;
+ public drawConstraints = null;
+
+ // Map sprites to particles
+
+ public createDistanceConstraint(a: Phaser.Verlet.Particle, b: Phaser.Verlet.Particle, stiffness: number, distance?: number = null): Phaser.Verlet.DistanceConstraint {
+
+ this.constraints.push(new Phaser.Verlet.DistanceConstraint(a, b, stiffness, distance));
+ return this.constraints[this.constraints.length - 1];
+
+ }
+
+ public createAngleConstraint(a: Phaser.Verlet.Particle, b: Phaser.Verlet.Particle, c: Phaser.Verlet.Particle, stiffness: number): Phaser.Verlet.AngleConstraint {
+
+ this.constraints.push(new Phaser.Verlet.AngleConstraint(a, b, c, stiffness));
+ return this.constraints[this.constraints.length - 1];
+
+ }
+
+ public createPinConstraint(a: Phaser.Verlet.Particle, pos: Vector2): Phaser.Verlet.PinConstraint {
+
+ this.constraints.push(new Phaser.Verlet.PinConstraint(a, pos));
+ return this.constraints[this.constraints.length - 1];
+
+ }
+
+ public pin(index, pos?=null) {
+
+ if (pos == null)
+ {
+ pos = this.particles[index].pos;
+ }
+
+ var pc = new Phaser.Verlet.PinConstraint(this.particles[index], pos);
+ this.constraints.push(pc);
+ return pc;
+
+ }
+
+ }
+
+}
diff --git a/Phaser/verlet/DistanceConstraint.ts b/Phaser/verlet/DistanceConstraint.ts
new file mode 100644
index 00000000..f2aa0eec
--- /dev/null
+++ b/Phaser/verlet/DistanceConstraint.ts
@@ -0,0 +1,69 @@
+///
+///
+///
+
+/**
+* Phaser - DistanceConstraint
+*
+* Constrains to initial distance
+*/
+
+module Phaser.Verlet {
+
+ export class DistanceConstraint {
+
+ /**
+ * Creates a new DistanceConstraint object.
+ * @class DistanceConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {DistanceConstraint} This object
+ **/
+ constructor(a: Phaser.Verlet.Particle, b: Phaser.Verlet.Particle, stiffness: number, distance?:number = null) {
+
+ this.a = a;
+ this.b = b;
+
+ if (distance === null)
+ {
+ this.distance = a.pos.sub(b.pos).length();
+ }
+ else
+ {
+ this.distance = distance;
+ }
+
+ this.stiffness = stiffness;
+
+ }
+
+ public a: Phaser.Verlet.Particle;
+ public b: Phaser.Verlet.Particle;
+ public distance: number;
+ public stiffness: number;
+
+ public relax(stepCoef: number) {
+
+ var normal = this.a.pos.sub(this.b.pos);
+
+ var m = normal.length2();
+
+ normal.mutableScale(((this.distance * this.distance - m) / m) * this.stiffness * stepCoef);
+
+ this.a.pos.mutableAdd(normal);
+ this.b.pos.mutableSub(normal);
+
+ }
+
+ public render(ctx) {
+ ctx.beginPath();
+ ctx.moveTo(this.a.pos.x, this.a.pos.y);
+ ctx.lineTo(this.b.pos.x, this.b.pos.y);
+ ctx.strokeStyle = "#d8dde2";
+ ctx.stroke();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Phaser/verlet/Particle.ts b/Phaser/verlet/Particle.ts
new file mode 100644
index 00000000..a4accdf3
--- /dev/null
+++ b/Phaser/verlet/Particle.ts
@@ -0,0 +1,44 @@
+///
+///
+
+/**
+* Phaser - Verlet - Particle
+*
+*
+*/
+
+module Phaser.Verlet {
+
+ export class Particle {
+
+ /**
+ * Creates a new Particle object.
+ * @class Particle
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Particle} This object
+ **/
+ constructor(pos: Vector2) {
+
+ this.pos = (new Vector2()).mutableSet(pos);
+ this.lastPos = (new Vector2()).mutableSet(pos);
+
+
+ }
+
+ public pos: Vector2;
+ public lastPos: Vector2;
+
+ public render(ctx) {
+
+ ctx.beginPath();
+ ctx.arc(this.pos.x, this.pos.y, 2, 0, 2*Math.PI);
+ ctx.fillStyle = "#2dad8f";
+ ctx.fill();
+
+ }
+
+ }
+
+}
diff --git a/Phaser/verlet/PinConstraint.ts b/Phaser/verlet/PinConstraint.ts
new file mode 100644
index 00000000..191df71f
--- /dev/null
+++ b/Phaser/verlet/PinConstraint.ts
@@ -0,0 +1,48 @@
+///
+///
+///
+
+/**
+* Phaser - PinConstraint
+*
+* Constrains to static / fixed point
+*/
+
+module Phaser.Verlet {
+
+ export class PinConstraint {
+
+ /**
+ * Creates a new PinConstraint object.
+ * @class PinConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {PinConstraint} This object
+ **/
+ constructor(a: Phaser.Verlet.Particle, pos: Vector2) {
+
+ this.a = a;
+ this.pos = (new Vector2()).mutableSet(pos);
+
+ }
+
+ public a: Phaser.Verlet.Particle;
+ public pos: Vector2;
+
+ public relax() {
+ this.a.pos.mutableSet(this.pos);
+ }
+
+ public render(ctx) {
+
+ ctx.beginPath();
+ ctx.arc(this.pos.x, this.pos.y, 6, 0, 2*Math.PI);
+ ctx.fillStyle = "rgba(0,153,255,0.1)";
+ ctx.fill();
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 4e4536c8..ba4131d6 100644
--- a/README.md
+++ b/README.md
@@ -86,6 +86,11 @@ V0.9.6
* Updated QuadTree to use the new CollisionMask values and significantly optimised and reduced overall class size
* Updated Collision.seperate to use the new CollisionMask
* Added a callback context parameter to Game.collide, Collision.overlap and the QuadTree class
+* Stage.canvas now calls preventDefault() when the context menu is activated (oncontextmenu)
+* Added Point.rotate to allow you to rotate a point around another point, with optional distance clamping. Also created test cases.
+* Added Group.alpha to apply a globalAlpha before the groups children are rendered. Useful to save on alpha calls.
+* Added Group.globalCompositeOperation to apply a composite operation before all of the groups children are rendered.
+* Added Camera black list support to Group along with Group.showToCamera, Group.hideFromCamera and Group.clearCameraList
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 781e5b73..c6dd907f 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -125,6 +125,34 @@
mask test 2.ts
+
+
+ multi rotate.ts
+
+
+
+
+ rotate point 1.ts
+
+
+ rotate point 2.ts
+
+
+
+ rotate point 3.ts
+
+
+
+
+ rotate point 4.ts
+
+
+ verlet 1.ts
+
+
+
+ verlet sprites.ts
+
display order.ts
diff --git a/Tests/geometry/multi rotate.js b/Tests/geometry/multi rotate.js
new file mode 100644
index 00000000..f00d76c4
--- /dev/null
+++ b/Tests/geometry/multi rotate.js
@@ -0,0 +1,31 @@
+///
+(function () {
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+ var p1;
+ var p2;
+ var p3;
+ var p4;
+ var d = 0;
+ function create() {
+ p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY);
+ p2 = new Phaser.Point(p1.x - 50, p1.y - 50);
+ p3 = new Phaser.Point(p1.x - 100, p1.y - 100);
+ p4 = new Phaser.Point(p1.x - 150, p1.y - 150);
+ }
+ function update() {
+ p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true);
+ p3.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true);
+ p4.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true);
+ d++;
+ }
+ function render() {
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+ myGame.stage.context.fillStyle = 'rgb(0,255,0)';
+ myGame.stage.context.fillRect(p3.x, p3.y, 4, 4);
+ myGame.stage.context.fillStyle = 'rgb(255,0,255)';
+ myGame.stage.context.fillRect(p4.x, p4.y, 4, 4);
+ }
+})();
diff --git a/Tests/geometry/multi rotate.ts b/Tests/geometry/multi rotate.ts
new file mode 100644
index 00000000..075f82ca
--- /dev/null
+++ b/Tests/geometry/multi rotate.ts
@@ -0,0 +1,49 @@
+///
+
+(function () {
+
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+
+ var p1:Phaser.Point;
+ var p2:Phaser.Point;
+ var p3:Phaser.Point;
+ var p4:Phaser.Point;
+
+ var d: number = 0;
+
+ function create() {
+
+ p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY);
+ p2 = new Phaser.Point(p1.x - 50, p1.y - 50);
+ p3 = new Phaser.Point(p1.x - 100, p1.y - 100);
+ p4 = new Phaser.Point(p1.x - 150, p1.y - 150);
+
+ }
+
+ function update() {
+
+ p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true);
+ p3.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true);
+ p4.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true);
+
+ d++;
+
+ }
+
+ function render() {
+
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+
+ myGame.stage.context.fillStyle = 'rgb(0,255,0)';
+ myGame.stage.context.fillRect(p3.x, p3.y, 4, 4);
+
+ myGame.stage.context.fillStyle = 'rgb(255,0,255)';
+ myGame.stage.context.fillRect(p4.x, p4.y, 4, 4);
+
+ }
+
+})();
diff --git a/Tests/geometry/rotate point 1.js b/Tests/geometry/rotate point 1.js
new file mode 100644
index 00000000..229ec21f
--- /dev/null
+++ b/Tests/geometry/rotate point 1.js
@@ -0,0 +1,21 @@
+///
+(function () {
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+ var p1;
+ var p2;
+ var d = 0;
+ function create() {
+ p1 = new Phaser.Point(200, 300);
+ p2 = new Phaser.Point(300, 300);
+ }
+ function update() {
+ p1.rotate(p2.x, p2.y, myGame.math.wrapAngle(d), true);
+ d++;
+ }
+ function render() {
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+ }
+})();
diff --git a/Tests/geometry/rotate point 1.ts b/Tests/geometry/rotate point 1.ts
new file mode 100644
index 00000000..c97610f8
--- /dev/null
+++ b/Tests/geometry/rotate point 1.ts
@@ -0,0 +1,36 @@
+///
+
+(function () {
+
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+
+ var p1:Phaser.Point;
+ var p2:Phaser.Point;
+ var d: number = 0;
+
+ function create() {
+
+ p1 = new Phaser.Point(200, 300);
+ p2 = new Phaser.Point(300, 300);
+
+ }
+
+ function update() {
+
+ p1.rotate(p2.x, p2.y, myGame.math.wrapAngle(d), true);
+
+ d++;
+
+ }
+
+ function render() {
+
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+
+ }
+
+})();
diff --git a/Tests/geometry/rotate point 2.js b/Tests/geometry/rotate point 2.js
new file mode 100644
index 00000000..7e1c1688
--- /dev/null
+++ b/Tests/geometry/rotate point 2.js
@@ -0,0 +1,43 @@
+///
+(function () {
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+ var p1;
+ var p2;
+ var p3;
+ var p4;
+ var d2 = 0;
+ var d3 = 0;
+ var d4 = 0;
+ function create() {
+ p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY);
+ p2 = new Phaser.Point(p1.x - 50, p1.y - 50);
+ p3 = new Phaser.Point(p2.x - 50, p2.y - 50);
+ p4 = new Phaser.Point(p3.x - 50, p3.y - 50);
+ }
+ function update() {
+ p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d2), true, 150);
+ p3.rotate(p2.x, p2.y, myGame.math.wrapAngle(d3), true, 50);
+ p4.rotate(p3.x, p3.y, myGame.math.wrapAngle(d4), true, 100);
+ d2 += 1;
+ d3 += 4;
+ d4 += 6;
+ }
+ function render() {
+ myGame.stage.context.strokeStyle = 'rgb(0,255,255)';
+ myGame.stage.context.beginPath();
+ myGame.stage.context.moveTo(p1.x, p1.y);
+ myGame.stage.context.lineTo(p2.x, p2.y);
+ myGame.stage.context.lineTo(p3.x, p3.y);
+ myGame.stage.context.lineTo(p4.x, p4.y);
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+ myGame.stage.context.fillStyle = 'rgb(0,255,0)';
+ myGame.stage.context.fillRect(p3.x, p3.y, 4, 4);
+ myGame.stage.context.fillStyle = 'rgb(255,0,255)';
+ myGame.stage.context.fillRect(p4.x, p4.y, 4, 4);
+ }
+})();
diff --git a/Tests/geometry/rotate point 2.ts b/Tests/geometry/rotate point 2.ts
new file mode 100644
index 00000000..92ce8034
--- /dev/null
+++ b/Tests/geometry/rotate point 2.ts
@@ -0,0 +1,62 @@
+///
+
+(function () {
+
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+
+ var p1:Phaser.Point;
+ var p2:Phaser.Point;
+ var p3:Phaser.Point;
+ var p4:Phaser.Point;
+
+ var d2: number = 0;
+ var d3: number = 0;
+ var d4: number = 0;
+
+ function create() {
+
+ p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY);
+ p2 = new Phaser.Point(p1.x - 50, p1.y - 50);
+ p3 = new Phaser.Point(p2.x - 50, p2.y - 50);
+ p4 = new Phaser.Point(p3.x - 50, p3.y - 50);
+
+ }
+
+ function update() {
+
+ p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d2), true, 150);
+ p3.rotate(p2.x, p2.y, myGame.math.wrapAngle(d3), true, 50);
+ p4.rotate(p3.x, p3.y, myGame.math.wrapAngle(d4), true, 100);
+
+ d2 += 1;
+ d3 += 4;
+ d4 += 6;
+
+ }
+
+ function render() {
+
+ myGame.stage.context.strokeStyle = 'rgb(0,255,255)';
+ myGame.stage.context.beginPath();
+ myGame.stage.context.moveTo(p1.x, p1.y);
+ myGame.stage.context.lineTo(p2.x, p2.y);
+ myGame.stage.context.lineTo(p3.x, p3.y);
+ myGame.stage.context.lineTo(p4.x, p4.y);
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+
+ myGame.stage.context.fillStyle = 'rgb(0,255,0)';
+ myGame.stage.context.fillRect(p3.x, p3.y, 4, 4);
+
+ myGame.stage.context.fillStyle = 'rgb(255,0,255)';
+ myGame.stage.context.fillRect(p4.x, p4.y, 4, 4);
+
+ }
+
+})();
diff --git a/Tests/geometry/rotate point 3.js b/Tests/geometry/rotate point 3.js
new file mode 100644
index 00000000..13169cb2
--- /dev/null
+++ b/Tests/geometry/rotate point 3.js
@@ -0,0 +1,71 @@
+///
+(function () {
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+ var origin;
+ var p1;
+ var p2;
+ var p3;
+ var p4;
+ var d = 0;
+ function create() {
+ // This creates a box made up of 4 edge-points and rotates it around the origin
+ origin = new Phaser.Point(400, 300);
+ p1 = new Phaser.Point()// top left
+ ;
+ p2 = new Phaser.Point()// top right
+ ;
+ p3 = new Phaser.Point()// bottom right
+ ;
+ p4 = new Phaser.Point()// bottom left
+ ;
+ }
+ function update() {
+ // top left (red)
+ p1.rotate(origin.x, origin.y, myGame.math.wrapAngle(d), true, 200);
+ // top right (yellow)
+ p2.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 90), true, 200);
+ // bottom right (aqua)
+ p3.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 180), true, 200);
+ // bottom left (blue)
+ p4.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 270), true, 200);
+ d++;
+ }
+ function render() {
+ // Render the shape
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgba(0,255,0,0.2)';
+ myGame.stage.context.strokeStyle = 'rgb(0,255,0)';
+ myGame.stage.context.lineWidth = 1;
+ myGame.stage.context.moveTo(p1.x, p1.y);
+ myGame.stage.context.lineTo(p2.x, p2.y);
+ myGame.stage.context.lineTo(p3.x, p3.y);
+ myGame.stage.context.lineTo(p4.x, p4.y);
+ myGame.stage.context.lineTo(p1.x, p1.y);
+ myGame.stage.context.fill();
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+ // Render the points
+ myGame.stage.context.fillStyle = 'rgb(255,255,255)';
+ myGame.stage.context.fillRect(origin.x, origin.y, 4, 4);
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.arc(p1.x, p1.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.arc(p2.x, p2.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(0,255,255)';
+ myGame.stage.context.arc(p3.x, p3.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(0,0,255)';
+ myGame.stage.context.arc(p4.x, p4.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+ }
+})();
diff --git a/Tests/geometry/rotate point 3.ts b/Tests/geometry/rotate point 3.ts
new file mode 100644
index 00000000..ad0c8d76
--- /dev/null
+++ b/Tests/geometry/rotate point 3.ts
@@ -0,0 +1,95 @@
+///
+
+(function () {
+
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+
+ var origin:Phaser.Point;
+
+ var p1:Phaser.Point;
+ var p2:Phaser.Point;
+ var p3:Phaser.Point;
+ var p4:Phaser.Point;
+
+ var d: number = 0;
+
+ function create() {
+
+ // This creates a box made up of 4 edge-points and rotates it around the origin
+
+ origin = new Phaser.Point(400, 300);
+
+ p1 = new Phaser.Point(); // top left
+ p2 = new Phaser.Point(); // top right
+ p3 = new Phaser.Point(); // bottom right
+ p4 = new Phaser.Point(); // bottom left
+
+ }
+
+ function update() {
+
+ // top left (red)
+ p1.rotate(origin.x, origin.y, myGame.math.wrapAngle(d), true, 200);
+
+ // top right (yellow)
+ p2.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 90), true, 200);
+
+ // bottom right (aqua)
+ p3.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 180), true, 200);
+
+ // bottom left (blue)
+ p4.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 270), true, 200);
+
+ d++;
+
+ }
+
+ function render() {
+
+ // Render the shape
+
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgba(0,255,0,0.2)';
+ myGame.stage.context.strokeStyle = 'rgb(0,255,0)';
+ myGame.stage.context.lineWidth = 1;
+ myGame.stage.context.moveTo(p1.x, p1.y);
+ myGame.stage.context.lineTo(p2.x, p2.y);
+ myGame.stage.context.lineTo(p3.x, p3.y);
+ myGame.stage.context.lineTo(p4.x, p4.y);
+ myGame.stage.context.lineTo(p1.x, p1.y);
+ myGame.stage.context.fill();
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+
+ // Render the points
+
+ myGame.stage.context.fillStyle = 'rgb(255,255,255)';
+ myGame.stage.context.fillRect(origin.x, origin.y, 4, 4);
+
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ myGame.stage.context.arc(p1.x, p1.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ myGame.stage.context.arc(p2.x, p2.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(0,255,255)';
+ myGame.stage.context.arc(p3.x, p3.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+
+ myGame.stage.context.beginPath();
+ myGame.stage.context.fillStyle = 'rgb(0,0,255)';
+ myGame.stage.context.arc(p4.x, p4.y, 4, 0, Math.PI * 2);
+ myGame.stage.context.fill();
+ myGame.stage.context.closePath();
+
+ }
+
+})();
diff --git a/Tests/geometry/rotate point 4.js b/Tests/geometry/rotate point 4.js
new file mode 100644
index 00000000..ea9ed35c
--- /dev/null
+++ b/Tests/geometry/rotate point 4.js
@@ -0,0 +1,54 @@
+///
+(function () {
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+ var origin;
+ var origin2;
+ var points = [];
+ var points2 = [];
+ var d = 0;
+ var d2 = 0;
+ var m = 64;
+ function create() {
+ // Let's have some fun :)
+ origin = new Phaser.Point(300, 200);
+ origin2 = new Phaser.Point(600, 350);
+ for(var i = 0; i < m; i++) {
+ points.push(new Phaser.Point());
+ points2.push(new Phaser.Point());
+ }
+ }
+ function update() {
+ for(var i = 0; i < m; i++) {
+ points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360 / m))), true, i * 5);
+ //points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360/m))), true, i * 10);
+ //points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360/m))), true, 200);
+ points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360 / m))), true, 200);
+ }
+ d -= 2;
+ d2 += 2;
+ }
+ function render() {
+ // Render the shape
+ myGame.stage.context.save();
+ //myGame.stage.context.globalCompositeOperation = 'xor';
+ myGame.stage.context.globalCompositeOperation = 'lighter';
+ myGame.stage.context.lineWidth = 20;
+ for(var i = 0; i < m; i++) {
+ myGame.stage.context.beginPath();
+ myGame.stage.context.strokeStyle = 'rgba(255,' + Math.round(i * (255 / m)).toString() + ',0,1)';
+ myGame.stage.context.moveTo(origin.x, origin.y);
+ myGame.stage.context.lineTo(points[i].x, points[i].y);
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+ }
+ for(var i = 0; i < m; i++) {
+ myGame.stage.context.beginPath();
+ myGame.stage.context.strokeStyle = 'rgba(0,' + Math.round(i * (255 / m)).toString() + ',255,1)';
+ myGame.stage.context.moveTo(origin2.x, origin2.y);
+ myGame.stage.context.lineTo(points2[i].x, points2[i].y);
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+ }
+ myGame.stage.context.restore();
+ }
+})();
diff --git a/Tests/geometry/rotate point 4.ts b/Tests/geometry/rotate point 4.ts
new file mode 100644
index 00000000..020c268c
--- /dev/null
+++ b/Tests/geometry/rotate point 4.ts
@@ -0,0 +1,82 @@
+///
+
+(function () {
+
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+
+ var origin:Phaser.Point;
+ var origin2:Phaser.Point;
+
+ var points:Phaser.Point[] = [];
+ var points2:Phaser.Point[] = [];
+
+ var d: number = 0;
+ var d2: number = 0;
+ var m: number = 64;
+
+ function create() {
+
+ // Let's have some fun :)
+
+ origin = new Phaser.Point(300, 200);
+ origin2 = new Phaser.Point(600, 350);
+
+ for (var i = 0; i < m; i++)
+ {
+ points.push(new Phaser.Point());
+ points2.push(new Phaser.Point());
+ }
+
+ }
+
+ function update() {
+
+ for (var i = 0; i < m; i++)
+ {
+ points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360/m))), true, i * 5);
+ //points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360/m))), true, i * 10);
+
+ //points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360/m))), true, 200);
+ points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360/m))), true, 200);
+ }
+
+ d -= 2;
+ d2 += 2;
+
+ }
+
+ function render() {
+
+ // Render the shape
+
+ myGame.stage.context.save();
+ //myGame.stage.context.globalCompositeOperation = 'xor';
+ myGame.stage.context.globalCompositeOperation = 'lighter';
+ myGame.stage.context.lineWidth = 20;
+
+ for (var i = 0; i < m; i++)
+ {
+ myGame.stage.context.beginPath();
+ myGame.stage.context.strokeStyle = 'rgba(255,' + Math.round(i * (255/m)).toString() + ',0,1)';
+ myGame.stage.context.moveTo(origin.x, origin.y);
+ myGame.stage.context.lineTo(points[i].x, points[i].y);
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+ }
+
+
+ for (var i = 0; i < m; i++)
+ {
+ myGame.stage.context.beginPath();
+ myGame.stage.context.strokeStyle = 'rgba(0,' + Math.round(i * (255/m)).toString() + ',255,1)';
+ myGame.stage.context.moveTo(origin2.x, origin2.y);
+ myGame.stage.context.lineTo(points2[i].x, points2[i].y);
+ myGame.stage.context.stroke();
+ myGame.stage.context.closePath();
+ }
+
+ myGame.stage.context.restore();
+
+ }
+
+})();
diff --git a/Tests/geometry/verlet 1.js b/Tests/geometry/verlet 1.js
new file mode 100644
index 00000000..c951b1b7
--- /dev/null
+++ b/Tests/geometry/verlet 1.js
@@ -0,0 +1,30 @@
+///
+(function () {
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+ var segment;
+ function create() {
+ myGame.verlet.friction = 1;
+ segment = myGame.verlet.createLineSegments([
+ new Phaser.Vector2(20, 10),
+ new Phaser.Vector2(40, 10),
+ new Phaser.Vector2(60, 10),
+ new Phaser.Vector2(80, 10),
+ new Phaser.Vector2(100, 10)
+ ], 0.02);
+ segment.pin(0);
+ segment.pin(4);
+ var wheel = myGame.verlet.createTire(new Phaser.Vector2(200, 50), 100, 30, 0.3, 0.9);
+ var tire2 = myGame.verlet.createTire(new Phaser.Vector2(400, 50), 70, 7, 0.1, 0.2);
+ var cube = myGame.verlet.createTire(new Phaser.Vector2(600, 50), 70, 4, 1, 1);
+ var tri = myGame.verlet.createTire(new Phaser.Vector2(700, 50), 100, 3, 1, 1);
+ }
+ function update() {
+ }
+ function render() {
+ myGame.verlet.render();
+ //myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ //myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+ //myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ //myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+ }
+})();
diff --git a/Tests/geometry/verlet 1.ts b/Tests/geometry/verlet 1.ts
new file mode 100644
index 00000000..455ad974
--- /dev/null
+++ b/Tests/geometry/verlet 1.ts
@@ -0,0 +1,39 @@
+///
+
+(function () {
+
+ var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render);
+
+ var segment: Phaser.Verlet.Composite;
+
+ function create() {
+
+ myGame.verlet.friction = 1;
+
+ segment = myGame.verlet.createLineSegments([new Phaser.Vector2(20, 10), new Phaser.Vector2(40, 10), new Phaser.Vector2(60, 10), new Phaser.Vector2(80, 10), new Phaser.Vector2(100, 10)], 0.02);
+ segment.pin(0);
+ segment.pin(4);
+
+ var wheel = myGame.verlet.createTire(new Phaser.Vector2(200,50), 100, 30, 0.3, 0.9);
+ var tire2 = myGame.verlet.createTire(new Phaser.Vector2(400,50), 70, 7, 0.1, 0.2);
+ var cube = myGame.verlet.createTire(new Phaser.Vector2(600,50), 70, 4, 1, 1);
+ var tri = myGame.verlet.createTire(new Phaser.Vector2(700,50), 100, 3, 1, 1);
+
+ }
+
+ function update() {
+ }
+
+ function render() {
+
+ myGame.verlet.render();
+
+ //myGame.stage.context.fillStyle = 'rgb(255,255,0)';
+ //myGame.stage.context.fillRect(p1.x, p1.y, 4, 4);
+
+ //myGame.stage.context.fillStyle = 'rgb(255,0,0)';
+ //myGame.stage.context.fillRect(p2.x, p2.y, 4, 4);
+
+ }
+
+})();
diff --git a/Tests/geometry/verlet sprites.js b/Tests/geometry/verlet sprites.js
new file mode 100644
index 00000000..93dbc2c1
--- /dev/null
+++ b/Tests/geometry/verlet sprites.js
@@ -0,0 +1,49 @@
+///
+(function () {
+ var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render);
+ var cube;
+ var b1;
+ var b2;
+ var b3;
+ var b4;
+ function init() {
+ myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png');
+ myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png');
+ myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png');
+ myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png');
+ myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png');
+ myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png');
+ myGame.loader.load();
+ }
+ function create() {
+ myGame.verlet.friction = 1;
+ myGame.verlet.step = 32;
+ //var wheel = myGame.verlet.createTire(new Phaser.Vector2(200,50), 100, 30, 0.3, 0.9);
+ //var tire2 = myGame.verlet.createTire(new Phaser.Vector2(400,50), 70, 7, 0.1, 0.2);
+ cube = myGame.verlet.createTire(new Phaser.Vector2(300, 50), 100, 4, 1, 1);
+ //var tri = myGame.verlet.createTire(new Phaser.Vector2(700,50), 100, 3, 1, 1);
+ var dc = new Phaser.Verlet.DistanceConstraint(cube.particles[0], cube.particles[1], 1);
+ cube.constraints.push(dc);
+ var dc2 = new Phaser.Verlet.DistanceConstraint(cube.particles[1], cube.particles[2], 1);
+ cube.constraints.push(dc2);
+ var dc3 = new Phaser.Verlet.DistanceConstraint(cube.particles[2], cube.particles[3], 1);
+ cube.constraints.push(dc3);
+ b1 = myGame.createSprite(cube.particles[0].pos.x, cube.particles[0].pos.y, 'ball0');
+ b2 = myGame.createSprite(cube.particles[1].pos.x, cube.particles[1].pos.y, 'ball1');
+ b3 = myGame.createSprite(cube.particles[2].pos.x, cube.particles[2].pos.y, 'ball2');
+ b4 = myGame.createSprite(cube.particles[3].pos.x, cube.particles[3].pos.y, 'ball3');
+ }
+ function update() {
+ b1.x = cube.particles[0].pos.x - 8;
+ b1.y = cube.particles[0].pos.y - 8;
+ b2.x = cube.particles[1].pos.x - 8;
+ b2.y = cube.particles[1].pos.y - 8;
+ b3.x = cube.particles[2].pos.x - 8;
+ b3.y = cube.particles[2].pos.y - 8;
+ b4.x = cube.particles[3].pos.x - 8;
+ b4.y = cube.particles[3].pos.y - 8;
+ }
+ function render() {
+ myGame.verlet.render();
+ }
+})();
diff --git a/Tests/geometry/verlet sprites.ts b/Tests/geometry/verlet sprites.ts
new file mode 100644
index 00000000..30c5dc85
--- /dev/null
+++ b/Tests/geometry/verlet sprites.ts
@@ -0,0 +1,75 @@
+///
+
+(function () {
+
+ var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render);
+
+ var cube: Phaser.Verlet.Composite;
+
+ var b1: Phaser.Sprite;
+ var b2: Phaser.Sprite;
+ var b3: Phaser.Sprite;
+ var b4: Phaser.Sprite;
+
+ function init() {
+
+ myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png');
+ myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png');
+ myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png');
+ myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png');
+ myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png');
+ myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png');
+
+ myGame.loader.load();
+
+ }
+
+ function create() {
+
+ myGame.verlet.friction = 1;
+ myGame.verlet.step = 32;
+
+ //var wheel = myGame.verlet.createTire(new Phaser.Vector2(200,50), 100, 30, 0.3, 0.9);
+ //var tire2 = myGame.verlet.createTire(new Phaser.Vector2(400,50), 70, 7, 0.1, 0.2);
+ cube = myGame.verlet.createTire(new Phaser.Vector2(300, 50), 100, 4, 1, 1);
+ //var tri = myGame.verlet.createTire(new Phaser.Vector2(700,50), 100, 3, 1, 1);
+
+ var dc: Phaser.Verlet.DistanceConstraint = new Phaser.Verlet.DistanceConstraint(cube.particles[0], cube.particles[1], 1);
+ cube.constraints.push(dc);
+
+ var dc2: Phaser.Verlet.DistanceConstraint = new Phaser.Verlet.DistanceConstraint(cube.particles[1], cube.particles[2], 1);
+ cube.constraints.push(dc2);
+
+ var dc3: Phaser.Verlet.DistanceConstraint = new Phaser.Verlet.DistanceConstraint(cube.particles[2], cube.particles[3], 1);
+ cube.constraints.push(dc3);
+
+ b1 = myGame.createSprite(cube.particles[0].pos.x, cube.particles[0].pos.y, 'ball0');
+ b2 = myGame.createSprite(cube.particles[1].pos.x, cube.particles[1].pos.y, 'ball1');
+ b3 = myGame.createSprite(cube.particles[2].pos.x, cube.particles[2].pos.y, 'ball2');
+ b4 = myGame.createSprite(cube.particles[3].pos.x, cube.particles[3].pos.y, 'ball3');
+
+ }
+
+ function update() {
+
+ b1.x = cube.particles[0].pos.x - 8;
+ b1.y = cube.particles[0].pos.y - 8;
+
+ b2.x = cube.particles[1].pos.x - 8;
+ b2.y = cube.particles[1].pos.y - 8;
+
+ b3.x = cube.particles[2].pos.x - 8;
+ b3.y = cube.particles[2].pos.y - 8;
+
+ b4.x = cube.particles[3].pos.x - 8;
+ b4.y = cube.particles[3].pos.y - 8;
+
+ }
+
+ function render() {
+
+ myGame.verlet.render();
+
+ }
+
+})();
diff --git a/Tests/phaser.js b/Tests/phaser.js
index 54aa3d08..1d5e6f54 100644
--- a/Tests/phaser.js
+++ b/Tests/phaser.js
@@ -3286,6 +3286,27 @@ var Phaser;
**/
function (length, angle) {
};
+ Point.prototype.rotate = /**
+ * Rotates the point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param {Number} angle The angle in radians (unless asDegrees is true) to return the point from.
+ * @param {Boolean} asDegrees Is the given angle in radians (false) or degrees (true)?
+ * @param {Number} distance An optional distance constraint between the point and the anchor
+ * @return The modified point object
+ */
+ function (cx, cy, angle, asDegrees, distance) {
+ if (typeof asDegrees === "undefined") { asDegrees = false; }
+ if (typeof distance === "undefined") { distance = null; }
+ if(asDegrees) {
+ angle = angle * Phaser.GameMath.DEG_TO_RAD;
+ }
+ // Get distance from origin (cx/cy) to this point
+ if(distance === null) {
+ distance = Math.sqrt(((cx - this.x) * (cx - this.x)) + ((cy - this.y) * (cy - this.y)));
+ }
+ return this.setTo(cx + distance * Math.cos(angle), cy + distance * Math.sin(angle));
+ };
Point.prototype.setTo = /**
* Sets the x and y values of this Point object to the given coordinates.
* @method setTo
@@ -4081,7 +4102,7 @@ var Phaser;
* @param {Number} y The y coordinate of the top-left corner of the quad.
* @param {Number} width The width of the quad.
* @param {Number} height The height of the quad.
- * @return {Quad } This object
+ * @return {Quad} This object
**/
function Quad(x, y, width, height) {
if (typeof x === "undefined") { x = 0; }
@@ -5290,7 +5311,6 @@ var Phaser;
continue;
}
if(QuadTree._object.collisionMask.checkHullIntersection(checkObject.collisionMask)) {
- console.log('quad hull');
//Execute callback functions if they exist
if((QuadTree._processingCallback == null) || QuadTree._processingCallback(QuadTree._object, checkObject)) {
overlapProcessed = true;
@@ -7312,6 +7332,35 @@ var Phaser;
}
return array;
};
+ GameMath.distanceBetween = /**
+ * Returns the distance from this Point object to the given Point object.
+ * @method distanceFrom
+ * @param {Point} target - The destination Point object.
+ * @param {Boolean} round - Round the distance to the nearest integer (default false)
+ * @return {Number} The distance between this Point object and the destination Point object.
+ **/
+ function distanceBetween(x1, y1, x2, y2) {
+ var dx = x1 - x2;
+ var dy = y1 - y2;
+ return Math.sqrt(dx * dx + dy * dy);
+ };
+ GameMath.prototype.rotatePoint = /**
+ * Rotates a point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param angle {number} The angle of the rotation in radians
+ * @param point {Point} The point object to perform the rotation on
+ * @return The modified point object
+ */
+ function (x, y, angle, point) {
+ var s = Math.sin(angle);
+ var c = Math.cos(angle);
+ point.x -= x;
+ point.y -= y;
+ var newX = point.x * c - point.y * s;
+ var newY = point.x * s - point.y * c;
+ return point.setTo(newX + x, newY + y);
+ };
return GameMath;
})();
Phaser.GameMath = GameMath;
@@ -7330,6 +7379,19 @@ var Phaser;
function Group(game, MaxSize) {
if (typeof MaxSize === "undefined") { MaxSize = 0; }
_super.call(this, game);
+ /**
+ * You can set a globalCompositeOperation that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to apply an effect like 'lighten' to a whole group of children as it saves doing it one-by-one.
+ * If this value is set it will call a canvas context save and restore before and after the render pass.
+ * Set to null to disable.
+ */
+ this.globalCompositeOperation = null;
+ /**
+ * You can set an alpha value on this Group that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to alpha a whole group of children as it saves doing it one-by-one.
+ * Set to 0 to disable.
+ */
+ this.alpha = 0;
this.isGroup = true;
this.members = [];
this.length = 0;
@@ -7384,6 +7446,14 @@ var Phaser;
if(this.ignoreGlobalRender && forceRender == false) {
return;
}
+ if(this.globalCompositeOperation) {
+ this._game.stage.context.save();
+ this._game.stage.context.globalCompositeOperation = this.globalCompositeOperation;
+ }
+ if(this.alpha > 0) {
+ var prevAlpha = this._game.stage.context.globalAlpha;
+ this._game.stage.context.globalAlpha = this.alpha;
+ }
var basic;
var i = 0;
while(i < this.length) {
@@ -7392,6 +7462,12 @@ var Phaser;
basic.render(camera, cameraOffsetX, cameraOffsetY, forceRender);
}
}
+ if(this.alpha > 0) {
+ this._game.stage.context.globalAlpha = prevAlpha;
+ }
+ if(this.globalCompositeOperation) {
+ this._game.stage.context.restore();
+ }
};
Object.defineProperty(Group.prototype, "maxSize", {
get: /**
@@ -9286,6 +9362,9 @@ var Phaser;
this.canvas.style['ms-touch-action'] = 'none';
this.canvas.style['touch-action'] = 'none';
this.canvas.style.backgroundColor = 'rgb(0,0,0)';
+ this.canvas.oncontextmenu = function (event) {
+ event.preventDefault();
+ };
this.context = this.canvas.getContext('2d');
this.offset = this.getOffset(this.canvas);
this.bounds = new Phaser.Quad(this.offset.x, this.offset.y, width, height);
@@ -10278,6 +10357,661 @@ var Phaser;
})();
Phaser.TweenManager = TweenManager;
})(Phaser || (Phaser = {}));
+///
+/**
+* Phaser - Vector2
+*
+* A simple 2-dimensional vector class. Based on the one included with verlet-js by Sub Protocol released under MIT
+*/
+var Phaser;
+(function (Phaser) {
+ var Vector2 = (function () {
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ function Vector2(x, y) {
+ if (typeof x === "undefined") { x = 0; }
+ if (typeof y === "undefined") { y = 0; }
+ this.x = x;
+ this.y = y;
+ }
+ Vector2.prototype.setTo = function (x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ };
+ Vector2.prototype.add = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x + v.x, this.y + v.y);
+ };
+ Vector2.prototype.sub = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x - v.x, this.y - v.y);
+ };
+ Vector2.prototype.mul = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x * v.x, this.y * v.y);
+ };
+ Vector2.prototype.div = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x / v.x, this.y / v.y);
+ };
+ Vector2.prototype.scale = function (coef, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x * coef, this.y * coef);
+ };
+ Vector2.prototype.mutableSet = function (v) {
+ this.x = v.x;
+ this.y = v.y;
+ return this;
+ };
+ Vector2.prototype.mutableAdd = function (v) {
+ this.x += v.x;
+ this.y += v.y;
+ return this;
+ };
+ Vector2.prototype.mutableSub = function (v) {
+ this.x -= v.x;
+ this.y -= v.y;
+ return this;
+ };
+ Vector2.prototype.mutableMul = function (v) {
+ this.x *= v.x;
+ this.y *= v.y;
+ return this;
+ };
+ Vector2.prototype.mutableDiv = function (v) {
+ this.x /= v.x;
+ this.y /= v.y;
+ return this;
+ };
+ Vector2.prototype.mutableScale = function (coef) {
+ this.x *= coef;
+ this.y *= coef;
+ return this;
+ };
+ Vector2.prototype.equals = function (v) {
+ return this.x == v.x && this.y == v.y;
+ };
+ Vector2.prototype.epsilonEquals = function (v, epsilon) {
+ return Math.abs(this.x - v.x) <= epsilon && Math.abs(this.y - v.y) <= epsilon;
+ };
+ Vector2.prototype.length = function () {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ };
+ Vector2.prototype.length2 = function () {
+ return this.x * this.x + this.y * this.y;
+ };
+ Vector2.prototype.dist = function (v) {
+ return Math.sqrt(this.dist2(v));
+ };
+ Vector2.prototype.dist2 = function (v) {
+ return ((v.x - this.x) * (v.x - this.x)) + ((v.y - this.y) * (v.y - this.y));
+ };
+ Vector2.prototype.normal = function (output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ var m = Math.sqrt(this.x * this.x + this.y * this.y);
+ return output.setTo(this.x / m, this.y / m);
+ };
+ Vector2.prototype.dot = function (v) {
+ return this.x * v.x + this.y * v.y;
+ };
+ Vector2.prototype.angle = function (v) {
+ return Math.atan2(this.x * v.y - this.y * v.x, this.x * v.x + this.y * v.y);
+ };
+ Vector2.prototype.angle2 = function (vLeft, vRight) {
+ return vLeft.sub(this).angle(vRight.sub(this));
+ };
+ Vector2.prototype.rotate = function (origin, theta, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ var x = this.x - origin.x;
+ var y = this.y - origin.y;
+ return output.setTo(x * Math.cos(theta) - y * Math.sin(theta) + origin.x, x * Math.sin(theta) + y * Math.cos(theta) + origin.y);
+ };
+ Vector2.prototype.toString = /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {string} a string representation of the object.
+ **/
+ function () {
+ return "[{Vector2 (x=" + this.x + " y=" + this.y + ")}]";
+ };
+ return Vector2;
+ })();
+ Phaser.Vector2 = Vector2;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ /**
+ * Phaser - Verlet - Particle
+ *
+ *
+ */
+ (function (Verlet) {
+ var Particle = (function () {
+ /**
+ * Creates a new Particle object.
+ * @class Particle
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Particle} This object
+ **/
+ function Particle(pos) {
+ this.pos = (new Phaser.Vector2()).mutableSet(pos);
+ this.lastPos = (new Phaser.Vector2()).mutableSet(pos);
+ }
+ Particle.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.arc(this.pos.x, this.pos.y, 2, 0, 2 * Math.PI);
+ ctx.fillStyle = "#2dad8f";
+ ctx.fill();
+ };
+ return Particle;
+ })();
+ Verlet.Particle = Particle;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ /**
+ * Phaser - PinConstraint
+ *
+ * Constrains to static / fixed point
+ */
+ (function (Verlet) {
+ var PinConstraint = (function () {
+ /**
+ * Creates a new PinConstraint object.
+ * @class PinConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {PinConstraint} This object
+ **/
+ function PinConstraint(a, pos) {
+ this.a = a;
+ this.pos = (new Phaser.Vector2()).mutableSet(pos);
+ }
+ PinConstraint.prototype.relax = function () {
+ this.a.pos.mutableSet(this.pos);
+ };
+ PinConstraint.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.arc(this.pos.x, this.pos.y, 6, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,153,255,0.1)";
+ ctx.fill();
+ };
+ return PinConstraint;
+ })();
+ Verlet.PinConstraint = PinConstraint;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ ///
+ /**
+ * Phaser - Verlet - Composite
+ *
+ *
+ */
+ (function (Verlet) {
+ var Composite = (function () {
+ /**
+ * Creates a new Composite object.
+ * @class Composite
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Composite} This object
+ **/
+ function Composite(game) {
+ this.drawParticles = null;
+ this.drawConstraints = null;
+ this._game = game;
+ this.particles = [];
+ this.constraints = [];
+ }
+ Composite.prototype.createDistanceConstraint = // Map sprites to particles
+ function (a, b, stiffness, distance) {
+ if (typeof distance === "undefined") { distance = null; }
+ this.constraints.push(new Phaser.Verlet.DistanceConstraint(a, b, stiffness, distance));
+ return this.constraints[this.constraints.length - 1];
+ };
+ Composite.prototype.createAngleConstraint = function (a, b, c, stiffness) {
+ this.constraints.push(new Phaser.Verlet.AngleConstraint(a, b, c, stiffness));
+ return this.constraints[this.constraints.length - 1];
+ };
+ Composite.prototype.createPinConstraint = function (a, pos) {
+ this.constraints.push(new Phaser.Verlet.PinConstraint(a, pos));
+ return this.constraints[this.constraints.length - 1];
+ };
+ Composite.prototype.pin = function (index, pos) {
+ if (typeof pos === "undefined") { pos = null; }
+ if(pos == null) {
+ pos = this.particles[index].pos;
+ }
+ var pc = new Phaser.Verlet.PinConstraint(this.particles[index], pos);
+ this.constraints.push(pc);
+ return pc;
+ };
+ return Composite;
+ })();
+ Verlet.Composite = Composite;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ /**
+ * Phaser - DistanceConstraint
+ *
+ * Constrains to initial distance
+ */
+ (function (Verlet) {
+ var DistanceConstraint = (function () {
+ /**
+ * Creates a new DistanceConstraint object.
+ * @class DistanceConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {DistanceConstraint} This object
+ **/
+ function DistanceConstraint(a, b, stiffness, distance) {
+ if (typeof distance === "undefined") { distance = null; }
+ this.a = a;
+ this.b = b;
+ if(distance === null) {
+ this.distance = a.pos.sub(b.pos).length();
+ } else {
+ this.distance = distance;
+ }
+ this.stiffness = stiffness;
+ }
+ DistanceConstraint.prototype.relax = function (stepCoef) {
+ var normal = this.a.pos.sub(this.b.pos);
+ var m = normal.length2();
+ normal.mutableScale(((this.distance * this.distance - m) / m) * this.stiffness * stepCoef);
+ this.a.pos.mutableAdd(normal);
+ this.b.pos.mutableSub(normal);
+ };
+ DistanceConstraint.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.moveTo(this.a.pos.x, this.a.pos.y);
+ ctx.lineTo(this.b.pos.x, this.b.pos.y);
+ ctx.strokeStyle = "#d8dde2";
+ ctx.stroke();
+ };
+ return DistanceConstraint;
+ })();
+ Verlet.DistanceConstraint = DistanceConstraint;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ /**
+ * Phaser - AngleConstraint
+ *
+ * constrains 3 particles to an angle
+ */
+ (function (Verlet) {
+ var AngleConstraint = (function () {
+ /**
+ * Creates a new AngleConstraint object.
+ * @class AngleConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {AngleConstraint} This object
+ **/
+ function AngleConstraint(a, b, c, stiffness) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.angle = this.b.pos.angle2(this.a.pos, this.c.pos);
+ this.stiffness = stiffness;
+ }
+ AngleConstraint.prototype.relax = function (stepCoef) {
+ var angle = this.b.pos.angle2(this.a.pos, this.c.pos);
+ var diff = angle - this.angle;
+ if(diff <= -Math.PI) {
+ diff += 2 * Math.PI;
+ } else if(diff >= Math.PI) {
+ diff -= 2 * Math.PI;
+ }
+ diff *= stepCoef * this.stiffness;
+ this.a.pos = this.a.pos.rotate(this.b.pos, diff);
+ this.c.pos = this.c.pos.rotate(this.b.pos, -diff);
+ this.b.pos = this.b.pos.rotate(this.a.pos, diff);
+ this.b.pos = this.b.pos.rotate(this.c.pos, -diff);
+ };
+ AngleConstraint.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.moveTo(this.a.pos.x, this.a.pos.y);
+ ctx.lineTo(this.b.pos.x, this.b.pos.y);
+ ctx.lineTo(this.c.pos.x, this.c.pos.y);
+ var tmp = ctx.lineWidth;
+ ctx.lineWidth = 5;
+ ctx.strokeStyle = "rgba(255,255,0,0.2)";
+ ctx.stroke();
+ ctx.lineWidth = tmp;
+ };
+ return AngleConstraint;
+ })();
+ Verlet.AngleConstraint = AngleConstraint;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /**
+ * Phaser - Verlet
+ *
+ * Based on verlet-js by Sub Protocol released under MIT
+ */
+ (function (Verlet) {
+ var VerletManager = (function () {
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ function VerletManager(game, width, height) {
+ this.composites = [];
+ this.step = 32;
+ this.selectionRadius = 20;
+ this.draggedEntity = null;
+ this.highlightColor = '#4f545c';
+ this.v = new Phaser.Vector2();
+ this._game = game;
+ this.width = width;
+ this.height = height;
+ this.gravity = new Phaser.Vector2(0, 0.2);
+ this.friction = 0.99;
+ this.groundFriction = 0.8;
+ this.canvas = game.stage.canvas;
+ this.context = game.stage.context;
+ this._game.input.onDown.add(this.mouseDownHandler, this);
+ this._game.input.onUp.add(this.mouseUpHandler, this);
+ }
+ VerletManager.prototype.intersectionTime = /**
+ * Computes time of intersection of a particle with a wall
+ *
+ * @param {Vec2} line wall's root position
+ * @param {Vec2} p particle's position
+ * @param {Vec2} dir walls's direction
+ * @param {Vec2} v particle's velocity
+ */
+ function (wall, p, dir, v) {
+ if(dir.x != 0) {
+ var denominator = v.y - dir.y * v.x / dir.x;
+ if(denominator == 0) {
+ return undefined;
+ }// Movement is parallel to wall
+
+ var numerator = wall.y + dir.y * (p.x - wall.x) / dir.x - p.y;
+ return numerator / denominator;
+ } else {
+ if(v.x == 0) {
+ return undefined;
+ }// parallel again
+
+ var denominator = v.x;
+ var numerator = wall.x - p.x;
+ return numerator / denominator;
+ }
+ };
+ VerletManager.prototype.intersectionPoint = function (wall, p, dir, v) {
+ var t = this.intersectionTime(wall, p, dir, v);
+ return new Phaser.Vector2(p.x + v.x * t, p.y + v.y * t);
+ };
+ VerletManager.prototype.bounds = function (particle) {
+ this.v.mutableSet(particle.pos);
+ this.v.mutableSub(particle.lastPos);
+ if(particle.pos.y > this.height - 1) {
+ particle.pos.mutableSet(this.intersectionPoint(new Phaser.Vector2(0, this.height - 1), particle.lastPos, new Phaser.Vector2(1, 0), this.v));
+ }
+ if(particle.pos.x < 0) {
+ particle.pos.mutableSet(this.intersectionPoint(new Phaser.Vector2(0, 0), particle.pos, new Phaser.Vector2(0, 1), this.v));
+ }
+ if(particle.pos.x > this.width - 1) {
+ particle.pos.mutableSet(this.intersectionPoint(new Phaser.Vector2(this.width - 1, 0), particle.pos, new Phaser.Vector2(0, 1), this.v));
+ }
+ };
+ VerletManager.prototype.OLDbounds = function (particle) {
+ if(particle.pos.y > this.height - 1) {
+ particle.pos.y = this.height - 1;
+ }
+ if(particle.pos.x < 0) {
+ var vx = particle.pos.x - particle.lastPos.x;
+ var vy = particle.pos.y - particle.lastPos.y;
+ if(vx == 0) {
+ particle.pos.x = 0;
+ } else {
+ var t = -particle.lastPos.x / vx;
+ particle.pos.x = particle.lastPos.x + t * vx;
+ particle.pos.y = particle.lastPos.y + t * vy;
+ }
+ }
+ if(particle.pos.x > this.width - 1) {
+ particle.pos.x = this.width - 1;
+ }
+ };
+ VerletManager.prototype.createPoint = function (pos) {
+ var composite = new Phaser.Verlet.Composite(this._game);
+ composite.particles.push(new Phaser.Verlet.Particle(pos));
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.createLineSegments = function (vertices, stiffness) {
+ var i;
+ var composite = new Phaser.Verlet.Composite(this._game);
+ for(i in vertices) {
+ composite.particles.push(new Phaser.Verlet.Particle(vertices[i]));
+ if(i > 0) {
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[i], composite.particles[i - 1], stiffness));
+ }
+ }
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.createCloth = function (origin, width, height, segments, pinMod, stiffness) {
+ var composite = new Phaser.Verlet.Composite(this._game);
+ var xStride = width / segments;
+ var yStride = height / segments;
+ var x, y;
+ for(y = 0; y < segments; ++y) {
+ for(x = 0; x < segments; ++x) {
+ var px = origin.x + x * xStride - width / 2 + xStride / 2;
+ var py = origin.y + y * yStride - height / 2 + yStride / 2;
+ composite.particles.push(new Phaser.Verlet.Particle(new Phaser.Vector2(px, py)));
+ if(x > 0) {
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[y * segments + x], composite.particles[y * segments + x - 1], stiffness));
+ }
+ if(y > 0) {
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[y * segments + x], composite.particles[(y - 1) * segments + x], stiffness));
+ }
+ }
+ }
+ for(x = 0; x < segments; ++x) {
+ if(x % pinMod == 0) {
+ composite.pin(x);
+ }
+ }
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.createTire = function (origin, radius, segments, spokeStiffness, treadStiffness) {
+ var stride = (2 * Math.PI) / segments;
+ var i;
+ var composite = new Phaser.Verlet.Composite(this._game);
+ // particles
+ for(i = 0; i < segments; ++i) {
+ var theta = i * stride;
+ composite.particles.push(new Verlet.Particle(new Phaser.Vector2(origin.x + Math.cos(theta) * radius, origin.y + Math.sin(theta) * radius)));
+ }
+ var center = new Verlet.Particle(origin);
+ composite.particles.push(center);
+ // constraints
+ for(i = 0; i < segments; ++i) {
+ composite.constraints.push(new Verlet.DistanceConstraint(composite.particles[i], composite.particles[(i + 1) % segments], treadStiffness));
+ composite.constraints.push(new Verlet.DistanceConstraint(composite.particles[i], center, spokeStiffness));
+ composite.constraints.push(new Verlet.DistanceConstraint(composite.particles[i], composite.particles[(i + 5) % segments], treadStiffness));
+ }
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.update = function () {
+ if(this.composites.length == 0) {
+ return;
+ }
+ var i, j, c;
+ for(c in this.composites) {
+ for(i in this.composites[c].particles) {
+ var particles = this.composites[c].particles;
+ // calculate velocity
+ var velocity = particles[i].pos.sub(particles[i].lastPos).scale(this.friction);
+ // ground friction
+ if(particles[i].pos.y >= this.height - 1 && velocity.length2() > 0.000001) {
+ var m = velocity.length();
+ velocity.x /= m;
+ velocity.y /= m;
+ velocity.mutableScale(m * this.groundFriction);
+ }
+ // save last good state
+ particles[i].lastPos.mutableSet(particles[i].pos);
+ // gravity
+ particles[i].pos.mutableAdd(this.gravity);
+ // inertia
+ particles[i].pos.mutableAdd(velocity);
+ }
+ }
+ // handle dragging of entities
+ if(this.draggedEntity) {
+ this.draggedEntity.pos.mutableSet(new Phaser.Vector2(this._game.input.x, this._game.input.y));
+ }
+ // relax
+ var stepCoef = 1 / this.step;
+ for(c in this.composites) {
+ var constraints = this.composites[c].constraints;
+ for(i = 0; i < this.step; ++i) {
+ for(j in constraints) {
+ constraints[j].relax(stepCoef);
+ }
+ }
+ }
+ // bounds checking
+ for(c in this.composites) {
+ var particles = this.composites[c].particles;
+ for(i in particles) {
+ this.bounds(particles[i]);
+ }
+ }
+ };
+ VerletManager.prototype.mouseDownHandler = function () {
+ var nearest = this.nearestEntity();
+ if(nearest) {
+ this.draggedEntity = nearest;
+ }
+ };
+ VerletManager.prototype.mouseUpHandler = function () {
+ this.draggedEntity = null;
+ };
+ VerletManager.prototype.nearestEntity = function () {
+ var c, i;
+ var d2Nearest = 0;
+ var entity = null;
+ var constraintsNearest = null;
+ // find nearest point
+ for(c in this.composites) {
+ var particles = this.composites[c].particles;
+ for(i in particles) {
+ var d2 = particles[i].pos.dist2(new Phaser.Vector2(this._game.input.x, this._game.input.y));
+ if(d2 <= this.selectionRadius * this.selectionRadius && (entity == null || d2 < d2Nearest)) {
+ entity = particles[i];
+ constraintsNearest = this.composites[c].constraints;
+ d2Nearest = d2;
+ }
+ }
+ }
+ // search for pinned constraints for this entity
+ for(i in constraintsNearest) {
+ if(constraintsNearest[i] instanceof Verlet.PinConstraint && constraintsNearest[i].a == entity) {
+ entity = constraintsNearest[i];
+ }
+ }
+ return entity;
+ };
+ VerletManager.prototype.render = function () {
+ var i, c;
+ for(c in this.composites) {
+ // draw constraints
+ if(this.composites[c].drawConstraints) {
+ this.composites[c].drawConstraints(this.context, this.composites[c]);
+ } else {
+ var constraints = this.composites[c].constraints;
+ for(i in constraints) {
+ constraints[i].render(this.context);
+ }
+ }
+ // draw particles
+ if(this.composites[c].drawParticles) {
+ this.composites[c].drawParticles(this.context, this.composites[c]);
+ } else {
+ var particles = this.composites[c].particles;
+ for(i in particles) {
+ particles[i].render(this.context);
+ }
+ }
+ }
+ // highlight nearest / dragged entity
+ var nearest = this.draggedEntity || this.nearestEntity();
+ if(nearest) {
+ this.context.beginPath();
+ this.context.arc(nearest.pos.x, nearest.pos.y, 8, 0, 2 * Math.PI);
+ this.context.strokeStyle = this.highlightColor;
+ this.context.stroke();
+ }
+ };
+ return VerletManager;
+ })();
+ Verlet.VerletManager = VerletManager;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
///
/**
* Phaser - World
@@ -14847,7 +15581,9 @@ var Phaser;
///
///
///
+///
///
+///
///
///
///
@@ -15025,6 +15761,7 @@ var Phaser;
this.rnd = new Phaser.RandomDataGenerator([
(Date.now() * Math.random()).toString()
]);
+ this.verlet = new Phaser.Verlet.VerletManager(this, width, height);
this.framerate = 60;
this.isBooted = true;
this.input.start();
@@ -15075,6 +15812,7 @@ var Phaser;
this.tweens.update();
this.input.update();
this.stage.update();
+ this.verlet.update();
this._accumulator += this.time.delta;
if(this._accumulator > this._maxAccumulation) {
this._accumulator = this._maxAccumulation;
diff --git a/Tests/scrollzones/blasteroids.js b/Tests/scrollzones/blasteroids.js
index 3fd1963e..f484ec2d 100644
--- a/Tests/scrollzones/blasteroids.js
+++ b/Tests/scrollzones/blasteroids.js
@@ -21,6 +21,9 @@
emitter = myGame.createEmitter(myGame.stage.centerX + 16, myGame.stage.centerY + 12);
emitter.makeParticles('jet', 250, false, 0);
emitter.setRotation(0, 0);
+ // Looks like a smoke trail!
+ //emitter.globalCompositeOperation = 'xor';
+ emitter.globalCompositeOperation = 'lighter';
bullets = myGame.createGroup(50);
// Create our bullet pool
for(var i = 0; i < 50; i++) {
diff --git a/Tests/scrollzones/blasteroids.ts b/Tests/scrollzones/blasteroids.ts
index 45080fd2..af366a33 100644
--- a/Tests/scrollzones/blasteroids.ts
+++ b/Tests/scrollzones/blasteroids.ts
@@ -33,6 +33,12 @@
emitter.makeParticles('jet', 250, false, 0);
emitter.setRotation(0, 0);
+ // Looks like a smoke trail!
+ //emitter.globalCompositeOperation = 'xor';
+
+ // Looks way cool :)
+ emitter.globalCompositeOperation = 'lighter';
+
bullets = myGame.createGroup(50);
// Create our bullet pool
diff --git a/build/phaser.d.ts b/build/phaser.d.ts
index ab96e09d..6fcb1ad4 100644
--- a/build/phaser.d.ts
+++ b/build/phaser.d.ts
@@ -2071,6 +2071,16 @@ module Phaser {
**/
public polar(length, angle): void;
/**
+ * Rotates the point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param {Number} angle The angle in radians (unless asDegrees is true) to return the point from.
+ * @param {Boolean} asDegrees Is the given angle in radians (false) or degrees (true)?
+ * @param {Number} distance An optional distance constraint between the point and the anchor
+ * @return The modified point object
+ */
+ public rotate(cx: number, cy: number, angle: number, asDegrees?: bool, distance?: number): Point;
+ /**
* Sets the x and y values of this Point object to the given coordinates.
* @method setTo
* @param {Number} x - The horizontal position of this point.
@@ -2578,7 +2588,7 @@ module Phaser {
* @param {Number} y The y coordinate of the top-left corner of the quad.
* @param {Number} width The width of the quad.
* @param {Number} height The height of the quad.
- * @return {Quad } This object
+ * @return {Quad} This object
**/
constructor(x?: number, y?: number, width?: number, height?: number);
public x: number;
@@ -4250,6 +4260,23 @@ module Phaser {
* @return The array
*/
public shuffleArray(array);
+ /**
+ * Returns the distance from this Point object to the given Point object.
+ * @method distanceFrom
+ * @param {Point} target - The destination Point object.
+ * @param {Boolean} round - Round the distance to the nearest integer (default false)
+ * @return {Number} The distance between this Point object and the destination Point object.
+ **/
+ static distanceBetween(x1: number, y1: number, x2: number, y2: number): number;
+ /**
+ * Rotates a point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param angle {number} The angle of the rotation in radians
+ * @param point {Point} The point object to perform the rotation on
+ * @return The modified point object
+ */
+ public rotatePoint(x: number, y: number, angle: number, point);
}
}
/**
@@ -4261,6 +4288,23 @@ module Phaser {
class Group extends Basic {
constructor(game: Game, MaxSize?: number);
/**
+ * Internal tracker for the maximum capacity of the group.
+ * Default is 0, or no max capacity.
+ */
+ private _maxSize;
+ /**
+ * Internal helper variable for recycling objects a la Emitter.
+ */
+ private _marker;
+ /**
+ * Helper for sort.
+ */
+ private _sortIndex;
+ /**
+ * Helper for sort.
+ */
+ private _sortOrder;
+ /**
* Use with sort() to sort in ascending order.
*/
static ASCENDING: number;
@@ -4279,22 +4323,18 @@ module Phaser {
*/
public length: number;
/**
- * Internal tracker for the maximum capacity of the group.
- * Default is 0, or no max capacity.
+ * You can set a globalCompositeOperation that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to apply an effect like 'lighten' to a whole group of children as it saves doing it one-by-one.
+ * If this value is set it will call a canvas context save and restore before and after the render pass.
+ * Set to null to disable.
*/
- private _maxSize;
+ public globalCompositeOperation: string;
/**
- * Internal helper variable for recycling objects a la Emitter.
+ * You can set an alpha value on this Group that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to alpha a whole group of children as it saves doing it one-by-one.
+ * Set to 0 to disable.
*/
- private _marker;
- /**
- * Helper for sort.
- */
- private _sortIndex;
- /**
- * Helper for sort.
- */
- private _sortOrder;
+ public alpha: number;
/**
* Override this function to handle any deleting or "shutdown" type operations you might need,
* such as removing traditional Flash children like Basic objects.
@@ -5731,6 +5771,235 @@ module Phaser {
}
}
/**
+* Phaser - Vector2
+*
+* A simple 2-dimensional vector class. Based on the one included with verlet-js by Sub Protocol released under MIT
+*/
+module Phaser {
+ class Vector2 {
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ constructor(x?: number, y?: number);
+ public x: number;
+ public y: number;
+ public setTo(x: number, y: number): Vector2;
+ public add(v: Vector2, output?: Vector2): Vector2;
+ public sub(v: Vector2, output?: Vector2): Vector2;
+ public mul(v: Vector2, output?: Vector2): Vector2;
+ public div(v: Vector2, output?: Vector2): Vector2;
+ public scale(coef: number, output?: Vector2): Vector2;
+ public mutableSet(v: Vector2): Vector2;
+ public mutableAdd(v: Vector2): Vector2;
+ public mutableSub(v: Vector2): Vector2;
+ public mutableMul(v: Vector2): Vector2;
+ public mutableDiv(v: Vector2): Vector2;
+ public mutableScale(coef: number): Vector2;
+ public equals(v: Vector2): bool;
+ public epsilonEquals(v: Vector2, epsilon: number): bool;
+ public length(): number;
+ public length2(): number;
+ public dist(v: Vector2): number;
+ public dist2(v: Vector2): number;
+ public normal(output?: Vector2): Vector2;
+ public dot(v: Vector2): number;
+ public angle(v: Vector2): number;
+ public angle2(vLeft: Vector2, vRight: Vector2): number;
+ public rotate(origin, theta, output?: Vector2): Vector2;
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {string} a string representation of the object.
+ **/
+ public toString(): string;
+ }
+}
+/**
+* Phaser - Verlet - Particle
+*
+*
+*/
+module Phaser.Verlet {
+ class Particle {
+ /**
+ * Creates a new Particle object.
+ * @class Particle
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Particle} This object
+ **/
+ constructor(pos: Vector2);
+ public pos: Vector2;
+ public lastPos: Vector2;
+ public render(ctx): void;
+ }
+}
+/**
+* Phaser - PinConstraint
+*
+* Constrains to static / fixed point
+*/
+module Phaser.Verlet {
+ class PinConstraint {
+ /**
+ * Creates a new PinConstraint object.
+ * @class PinConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {PinConstraint} This object
+ **/
+ constructor(a: Particle, pos: Vector2);
+ public a: Particle;
+ public pos: Vector2;
+ public relax(): void;
+ public render(ctx): void;
+ }
+}
+/**
+* Phaser - Verlet - Composite
+*
+*
+*/
+module Phaser.Verlet {
+ class Composite {
+ /**
+ * Creates a new Composite object.
+ * @class Composite
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Composite} This object
+ **/
+ constructor(game: Game);
+ private _game;
+ public particles: Particle[];
+ public constraints;
+ public drawParticles;
+ public drawConstraints;
+ public createDistanceConstraint(a: Particle, b: Particle, stiffness: number, distance?: number): DistanceConstraint;
+ public createAngleConstraint(a: Particle, b: Particle, c: Particle, stiffness: number): AngleConstraint;
+ public createPinConstraint(a: Particle, pos: Vector2): PinConstraint;
+ public pin(index, pos?): PinConstraint;
+ }
+}
+/**
+* Phaser - DistanceConstraint
+*
+* Constrains to initial distance
+*/
+module Phaser.Verlet {
+ class DistanceConstraint {
+ /**
+ * Creates a new DistanceConstraint object.
+ * @class DistanceConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {DistanceConstraint} This object
+ **/
+ constructor(a: Particle, b: Particle, stiffness: number, distance?: number);
+ public a: Particle;
+ public b: Particle;
+ public distance: number;
+ public stiffness: number;
+ public relax(stepCoef: number): void;
+ public render(ctx): void;
+ }
+}
+/**
+* Phaser - AngleConstraint
+*
+* constrains 3 particles to an angle
+*/
+module Phaser.Verlet {
+ class AngleConstraint {
+ /**
+ * Creates a new AngleConstraint object.
+ * @class AngleConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {AngleConstraint} This object
+ **/
+ constructor(a: Particle, b: Particle, c: Particle, stiffness: number);
+ public a: Particle;
+ public b: Particle;
+ public c: Particle;
+ public angle: number;
+ public stiffness: number;
+ public relax(stepCoef: number): void;
+ public render(ctx): void;
+ }
+}
+/**
+* Phaser - Verlet
+*
+* Based on verlet-js by Sub Protocol released under MIT
+*/
+module Phaser.Verlet {
+ class VerletManager {
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ constructor(game: Game, width: number, height: number);
+ private _game;
+ public composites: any[];
+ public width: number;
+ public height: number;
+ public step: number;
+ public gravity: Vector2;
+ public friction: number;
+ public groundFriction: number;
+ public selectionRadius: number;
+ public draggedEntity;
+ public highlightColor: string;
+ /**
+ * This class is actually a wrapper of canvas.
+ * @type {HTMLCanvasElement}
+ */
+ public canvas: HTMLCanvasElement;
+ /**
+ * Canvas context of this object.
+ * @type {CanvasRenderingContext2D}
+ */
+ public context: CanvasRenderingContext2D;
+ /**
+ * Computes time of intersection of a particle with a wall
+ *
+ * @param {Vec2} line wall's root position
+ * @param {Vec2} p particle's position
+ * @param {Vec2} dir walls's direction
+ * @param {Vec2} v particle's velocity
+ */
+ public intersectionTime(wall, p, dir, v): number;
+ public intersectionPoint(wall, p, dir, v): Vector2;
+ private v;
+ public bounds(particle: Particle): void;
+ public OLDbounds(particle: Particle): void;
+ public createPoint(pos: Vector2): Composite;
+ public createLineSegments(vertices, stiffness): Composite;
+ public createCloth(origin, width, height, segments, pinMod, stiffness): Composite;
+ public createTire(origin, radius, segments, spokeStiffness, treadStiffness): Composite;
+ public update(): void;
+ private mouseDownHandler();
+ private mouseUpHandler();
+ public nearestEntity();
+ public render(): void;
+ }
+}
+/**
* Phaser - World
*
* "This world is but a canvas to our imagination." - Henry David Thoreau
@@ -8590,6 +8859,11 @@ module Phaser {
*/
public tweens: TweenManager;
/**
+ * Reference to the verlet manager.
+ * @type {VerletManager}
+ */
+ public verlet: Verlet.VerletManager;
+ /**
* Reference to the world.
* @type {World}
*/
diff --git a/build/phaser.js b/build/phaser.js
index 54aa3d08..1d5e6f54 100644
--- a/build/phaser.js
+++ b/build/phaser.js
@@ -3286,6 +3286,27 @@ var Phaser;
**/
function (length, angle) {
};
+ Point.prototype.rotate = /**
+ * Rotates the point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param {Number} angle The angle in radians (unless asDegrees is true) to return the point from.
+ * @param {Boolean} asDegrees Is the given angle in radians (false) or degrees (true)?
+ * @param {Number} distance An optional distance constraint between the point and the anchor
+ * @return The modified point object
+ */
+ function (cx, cy, angle, asDegrees, distance) {
+ if (typeof asDegrees === "undefined") { asDegrees = false; }
+ if (typeof distance === "undefined") { distance = null; }
+ if(asDegrees) {
+ angle = angle * Phaser.GameMath.DEG_TO_RAD;
+ }
+ // Get distance from origin (cx/cy) to this point
+ if(distance === null) {
+ distance = Math.sqrt(((cx - this.x) * (cx - this.x)) + ((cy - this.y) * (cy - this.y)));
+ }
+ return this.setTo(cx + distance * Math.cos(angle), cy + distance * Math.sin(angle));
+ };
Point.prototype.setTo = /**
* Sets the x and y values of this Point object to the given coordinates.
* @method setTo
@@ -4081,7 +4102,7 @@ var Phaser;
* @param {Number} y The y coordinate of the top-left corner of the quad.
* @param {Number} width The width of the quad.
* @param {Number} height The height of the quad.
- * @return {Quad } This object
+ * @return {Quad} This object
**/
function Quad(x, y, width, height) {
if (typeof x === "undefined") { x = 0; }
@@ -5290,7 +5311,6 @@ var Phaser;
continue;
}
if(QuadTree._object.collisionMask.checkHullIntersection(checkObject.collisionMask)) {
- console.log('quad hull');
//Execute callback functions if they exist
if((QuadTree._processingCallback == null) || QuadTree._processingCallback(QuadTree._object, checkObject)) {
overlapProcessed = true;
@@ -7312,6 +7332,35 @@ var Phaser;
}
return array;
};
+ GameMath.distanceBetween = /**
+ * Returns the distance from this Point object to the given Point object.
+ * @method distanceFrom
+ * @param {Point} target - The destination Point object.
+ * @param {Boolean} round - Round the distance to the nearest integer (default false)
+ * @return {Number} The distance between this Point object and the destination Point object.
+ **/
+ function distanceBetween(x1, y1, x2, y2) {
+ var dx = x1 - x2;
+ var dy = y1 - y2;
+ return Math.sqrt(dx * dx + dy * dy);
+ };
+ GameMath.prototype.rotatePoint = /**
+ * Rotates a point around the x/y coordinates given to the desired angle
+ * @param x {number} The x coordinate of the anchor point
+ * @param y {number} The y coordinate of the anchor point
+ * @param angle {number} The angle of the rotation in radians
+ * @param point {Point} The point object to perform the rotation on
+ * @return The modified point object
+ */
+ function (x, y, angle, point) {
+ var s = Math.sin(angle);
+ var c = Math.cos(angle);
+ point.x -= x;
+ point.y -= y;
+ var newX = point.x * c - point.y * s;
+ var newY = point.x * s - point.y * c;
+ return point.setTo(newX + x, newY + y);
+ };
return GameMath;
})();
Phaser.GameMath = GameMath;
@@ -7330,6 +7379,19 @@ var Phaser;
function Group(game, MaxSize) {
if (typeof MaxSize === "undefined") { MaxSize = 0; }
_super.call(this, game);
+ /**
+ * You can set a globalCompositeOperation that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to apply an effect like 'lighten' to a whole group of children as it saves doing it one-by-one.
+ * If this value is set it will call a canvas context save and restore before and after the render pass.
+ * Set to null to disable.
+ */
+ this.globalCompositeOperation = null;
+ /**
+ * You can set an alpha value on this Group that will be applied before the render method is called on this Groups children.
+ * This is useful if you wish to alpha a whole group of children as it saves doing it one-by-one.
+ * Set to 0 to disable.
+ */
+ this.alpha = 0;
this.isGroup = true;
this.members = [];
this.length = 0;
@@ -7384,6 +7446,14 @@ var Phaser;
if(this.ignoreGlobalRender && forceRender == false) {
return;
}
+ if(this.globalCompositeOperation) {
+ this._game.stage.context.save();
+ this._game.stage.context.globalCompositeOperation = this.globalCompositeOperation;
+ }
+ if(this.alpha > 0) {
+ var prevAlpha = this._game.stage.context.globalAlpha;
+ this._game.stage.context.globalAlpha = this.alpha;
+ }
var basic;
var i = 0;
while(i < this.length) {
@@ -7392,6 +7462,12 @@ var Phaser;
basic.render(camera, cameraOffsetX, cameraOffsetY, forceRender);
}
}
+ if(this.alpha > 0) {
+ this._game.stage.context.globalAlpha = prevAlpha;
+ }
+ if(this.globalCompositeOperation) {
+ this._game.stage.context.restore();
+ }
};
Object.defineProperty(Group.prototype, "maxSize", {
get: /**
@@ -9286,6 +9362,9 @@ var Phaser;
this.canvas.style['ms-touch-action'] = 'none';
this.canvas.style['touch-action'] = 'none';
this.canvas.style.backgroundColor = 'rgb(0,0,0)';
+ this.canvas.oncontextmenu = function (event) {
+ event.preventDefault();
+ };
this.context = this.canvas.getContext('2d');
this.offset = this.getOffset(this.canvas);
this.bounds = new Phaser.Quad(this.offset.x, this.offset.y, width, height);
@@ -10278,6 +10357,661 @@ var Phaser;
})();
Phaser.TweenManager = TweenManager;
})(Phaser || (Phaser = {}));
+///
+/**
+* Phaser - Vector2
+*
+* A simple 2-dimensional vector class. Based on the one included with verlet-js by Sub Protocol released under MIT
+*/
+var Phaser;
+(function (Phaser) {
+ var Vector2 = (function () {
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ function Vector2(x, y) {
+ if (typeof x === "undefined") { x = 0; }
+ if (typeof y === "undefined") { y = 0; }
+ this.x = x;
+ this.y = y;
+ }
+ Vector2.prototype.setTo = function (x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ };
+ Vector2.prototype.add = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x + v.x, this.y + v.y);
+ };
+ Vector2.prototype.sub = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x - v.x, this.y - v.y);
+ };
+ Vector2.prototype.mul = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x * v.x, this.y * v.y);
+ };
+ Vector2.prototype.div = function (v, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x / v.x, this.y / v.y);
+ };
+ Vector2.prototype.scale = function (coef, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ return output.setTo(this.x * coef, this.y * coef);
+ };
+ Vector2.prototype.mutableSet = function (v) {
+ this.x = v.x;
+ this.y = v.y;
+ return this;
+ };
+ Vector2.prototype.mutableAdd = function (v) {
+ this.x += v.x;
+ this.y += v.y;
+ return this;
+ };
+ Vector2.prototype.mutableSub = function (v) {
+ this.x -= v.x;
+ this.y -= v.y;
+ return this;
+ };
+ Vector2.prototype.mutableMul = function (v) {
+ this.x *= v.x;
+ this.y *= v.y;
+ return this;
+ };
+ Vector2.prototype.mutableDiv = function (v) {
+ this.x /= v.x;
+ this.y /= v.y;
+ return this;
+ };
+ Vector2.prototype.mutableScale = function (coef) {
+ this.x *= coef;
+ this.y *= coef;
+ return this;
+ };
+ Vector2.prototype.equals = function (v) {
+ return this.x == v.x && this.y == v.y;
+ };
+ Vector2.prototype.epsilonEquals = function (v, epsilon) {
+ return Math.abs(this.x - v.x) <= epsilon && Math.abs(this.y - v.y) <= epsilon;
+ };
+ Vector2.prototype.length = function () {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ };
+ Vector2.prototype.length2 = function () {
+ return this.x * this.x + this.y * this.y;
+ };
+ Vector2.prototype.dist = function (v) {
+ return Math.sqrt(this.dist2(v));
+ };
+ Vector2.prototype.dist2 = function (v) {
+ return ((v.x - this.x) * (v.x - this.x)) + ((v.y - this.y) * (v.y - this.y));
+ };
+ Vector2.prototype.normal = function (output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ var m = Math.sqrt(this.x * this.x + this.y * this.y);
+ return output.setTo(this.x / m, this.y / m);
+ };
+ Vector2.prototype.dot = function (v) {
+ return this.x * v.x + this.y * v.y;
+ };
+ Vector2.prototype.angle = function (v) {
+ return Math.atan2(this.x * v.y - this.y * v.x, this.x * v.x + this.y * v.y);
+ };
+ Vector2.prototype.angle2 = function (vLeft, vRight) {
+ return vLeft.sub(this).angle(vRight.sub(this));
+ };
+ Vector2.prototype.rotate = function (origin, theta, output) {
+ if (typeof output === "undefined") { output = new Vector2(); }
+ var x = this.x - origin.x;
+ var y = this.y - origin.y;
+ return output.setTo(x * Math.cos(theta) - y * Math.sin(theta) + origin.x, x * Math.sin(theta) + y * Math.cos(theta) + origin.y);
+ };
+ Vector2.prototype.toString = /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {string} a string representation of the object.
+ **/
+ function () {
+ return "[{Vector2 (x=" + this.x + " y=" + this.y + ")}]";
+ };
+ return Vector2;
+ })();
+ Phaser.Vector2 = Vector2;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ /**
+ * Phaser - Verlet - Particle
+ *
+ *
+ */
+ (function (Verlet) {
+ var Particle = (function () {
+ /**
+ * Creates a new Particle object.
+ * @class Particle
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Particle} This object
+ **/
+ function Particle(pos) {
+ this.pos = (new Phaser.Vector2()).mutableSet(pos);
+ this.lastPos = (new Phaser.Vector2()).mutableSet(pos);
+ }
+ Particle.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.arc(this.pos.x, this.pos.y, 2, 0, 2 * Math.PI);
+ ctx.fillStyle = "#2dad8f";
+ ctx.fill();
+ };
+ return Particle;
+ })();
+ Verlet.Particle = Particle;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ /**
+ * Phaser - PinConstraint
+ *
+ * Constrains to static / fixed point
+ */
+ (function (Verlet) {
+ var PinConstraint = (function () {
+ /**
+ * Creates a new PinConstraint object.
+ * @class PinConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {PinConstraint} This object
+ **/
+ function PinConstraint(a, pos) {
+ this.a = a;
+ this.pos = (new Phaser.Vector2()).mutableSet(pos);
+ }
+ PinConstraint.prototype.relax = function () {
+ this.a.pos.mutableSet(this.pos);
+ };
+ PinConstraint.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.arc(this.pos.x, this.pos.y, 6, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,153,255,0.1)";
+ ctx.fill();
+ };
+ return PinConstraint;
+ })();
+ Verlet.PinConstraint = PinConstraint;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ ///
+ /**
+ * Phaser - Verlet - Composite
+ *
+ *
+ */
+ (function (Verlet) {
+ var Composite = (function () {
+ /**
+ * Creates a new Composite object.
+ * @class Composite
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Composite} This object
+ **/
+ function Composite(game) {
+ this.drawParticles = null;
+ this.drawConstraints = null;
+ this._game = game;
+ this.particles = [];
+ this.constraints = [];
+ }
+ Composite.prototype.createDistanceConstraint = // Map sprites to particles
+ function (a, b, stiffness, distance) {
+ if (typeof distance === "undefined") { distance = null; }
+ this.constraints.push(new Phaser.Verlet.DistanceConstraint(a, b, stiffness, distance));
+ return this.constraints[this.constraints.length - 1];
+ };
+ Composite.prototype.createAngleConstraint = function (a, b, c, stiffness) {
+ this.constraints.push(new Phaser.Verlet.AngleConstraint(a, b, c, stiffness));
+ return this.constraints[this.constraints.length - 1];
+ };
+ Composite.prototype.createPinConstraint = function (a, pos) {
+ this.constraints.push(new Phaser.Verlet.PinConstraint(a, pos));
+ return this.constraints[this.constraints.length - 1];
+ };
+ Composite.prototype.pin = function (index, pos) {
+ if (typeof pos === "undefined") { pos = null; }
+ if(pos == null) {
+ pos = this.particles[index].pos;
+ }
+ var pc = new Phaser.Verlet.PinConstraint(this.particles[index], pos);
+ this.constraints.push(pc);
+ return pc;
+ };
+ return Composite;
+ })();
+ Verlet.Composite = Composite;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ /**
+ * Phaser - DistanceConstraint
+ *
+ * Constrains to initial distance
+ */
+ (function (Verlet) {
+ var DistanceConstraint = (function () {
+ /**
+ * Creates a new DistanceConstraint object.
+ * @class DistanceConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {DistanceConstraint} This object
+ **/
+ function DistanceConstraint(a, b, stiffness, distance) {
+ if (typeof distance === "undefined") { distance = null; }
+ this.a = a;
+ this.b = b;
+ if(distance === null) {
+ this.distance = a.pos.sub(b.pos).length();
+ } else {
+ this.distance = distance;
+ }
+ this.stiffness = stiffness;
+ }
+ DistanceConstraint.prototype.relax = function (stepCoef) {
+ var normal = this.a.pos.sub(this.b.pos);
+ var m = normal.length2();
+ normal.mutableScale(((this.distance * this.distance - m) / m) * this.stiffness * stepCoef);
+ this.a.pos.mutableAdd(normal);
+ this.b.pos.mutableSub(normal);
+ };
+ DistanceConstraint.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.moveTo(this.a.pos.x, this.a.pos.y);
+ ctx.lineTo(this.b.pos.x, this.b.pos.y);
+ ctx.strokeStyle = "#d8dde2";
+ ctx.stroke();
+ };
+ return DistanceConstraint;
+ })();
+ Verlet.DistanceConstraint = DistanceConstraint;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ /**
+ * Phaser - AngleConstraint
+ *
+ * constrains 3 particles to an angle
+ */
+ (function (Verlet) {
+ var AngleConstraint = (function () {
+ /**
+ * Creates a new AngleConstraint object.
+ * @class AngleConstraint
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {AngleConstraint} This object
+ **/
+ function AngleConstraint(a, b, c, stiffness) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.angle = this.b.pos.angle2(this.a.pos, this.c.pos);
+ this.stiffness = stiffness;
+ }
+ AngleConstraint.prototype.relax = function (stepCoef) {
+ var angle = this.b.pos.angle2(this.a.pos, this.c.pos);
+ var diff = angle - this.angle;
+ if(diff <= -Math.PI) {
+ diff += 2 * Math.PI;
+ } else if(diff >= Math.PI) {
+ diff -= 2 * Math.PI;
+ }
+ diff *= stepCoef * this.stiffness;
+ this.a.pos = this.a.pos.rotate(this.b.pos, diff);
+ this.c.pos = this.c.pos.rotate(this.b.pos, -diff);
+ this.b.pos = this.b.pos.rotate(this.a.pos, diff);
+ this.b.pos = this.b.pos.rotate(this.c.pos, -diff);
+ };
+ AngleConstraint.prototype.render = function (ctx) {
+ ctx.beginPath();
+ ctx.moveTo(this.a.pos.x, this.a.pos.y);
+ ctx.lineTo(this.b.pos.x, this.b.pos.y);
+ ctx.lineTo(this.c.pos.x, this.c.pos.y);
+ var tmp = ctx.lineWidth;
+ ctx.lineWidth = 5;
+ ctx.strokeStyle = "rgba(255,255,0,0.2)";
+ ctx.stroke();
+ ctx.lineWidth = tmp;
+ };
+ return AngleConstraint;
+ })();
+ Verlet.AngleConstraint = AngleConstraint;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
+var Phaser;
+(function (Phaser) {
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /**
+ * Phaser - Verlet
+ *
+ * Based on verlet-js by Sub Protocol released under MIT
+ */
+ (function (Verlet) {
+ var VerletManager = (function () {
+ /**
+ * Creates a new Vector2 object.
+ * @class Vector2
+ * @constructor
+ * @param {Number} x The x coordinate of vector2
+ * @param {Number} y The y coordinate of vector2
+ * @return {Vector2} This object
+ **/
+ function VerletManager(game, width, height) {
+ this.composites = [];
+ this.step = 32;
+ this.selectionRadius = 20;
+ this.draggedEntity = null;
+ this.highlightColor = '#4f545c';
+ this.v = new Phaser.Vector2();
+ this._game = game;
+ this.width = width;
+ this.height = height;
+ this.gravity = new Phaser.Vector2(0, 0.2);
+ this.friction = 0.99;
+ this.groundFriction = 0.8;
+ this.canvas = game.stage.canvas;
+ this.context = game.stage.context;
+ this._game.input.onDown.add(this.mouseDownHandler, this);
+ this._game.input.onUp.add(this.mouseUpHandler, this);
+ }
+ VerletManager.prototype.intersectionTime = /**
+ * Computes time of intersection of a particle with a wall
+ *
+ * @param {Vec2} line wall's root position
+ * @param {Vec2} p particle's position
+ * @param {Vec2} dir walls's direction
+ * @param {Vec2} v particle's velocity
+ */
+ function (wall, p, dir, v) {
+ if(dir.x != 0) {
+ var denominator = v.y - dir.y * v.x / dir.x;
+ if(denominator == 0) {
+ return undefined;
+ }// Movement is parallel to wall
+
+ var numerator = wall.y + dir.y * (p.x - wall.x) / dir.x - p.y;
+ return numerator / denominator;
+ } else {
+ if(v.x == 0) {
+ return undefined;
+ }// parallel again
+
+ var denominator = v.x;
+ var numerator = wall.x - p.x;
+ return numerator / denominator;
+ }
+ };
+ VerletManager.prototype.intersectionPoint = function (wall, p, dir, v) {
+ var t = this.intersectionTime(wall, p, dir, v);
+ return new Phaser.Vector2(p.x + v.x * t, p.y + v.y * t);
+ };
+ VerletManager.prototype.bounds = function (particle) {
+ this.v.mutableSet(particle.pos);
+ this.v.mutableSub(particle.lastPos);
+ if(particle.pos.y > this.height - 1) {
+ particle.pos.mutableSet(this.intersectionPoint(new Phaser.Vector2(0, this.height - 1), particle.lastPos, new Phaser.Vector2(1, 0), this.v));
+ }
+ if(particle.pos.x < 0) {
+ particle.pos.mutableSet(this.intersectionPoint(new Phaser.Vector2(0, 0), particle.pos, new Phaser.Vector2(0, 1), this.v));
+ }
+ if(particle.pos.x > this.width - 1) {
+ particle.pos.mutableSet(this.intersectionPoint(new Phaser.Vector2(this.width - 1, 0), particle.pos, new Phaser.Vector2(0, 1), this.v));
+ }
+ };
+ VerletManager.prototype.OLDbounds = function (particle) {
+ if(particle.pos.y > this.height - 1) {
+ particle.pos.y = this.height - 1;
+ }
+ if(particle.pos.x < 0) {
+ var vx = particle.pos.x - particle.lastPos.x;
+ var vy = particle.pos.y - particle.lastPos.y;
+ if(vx == 0) {
+ particle.pos.x = 0;
+ } else {
+ var t = -particle.lastPos.x / vx;
+ particle.pos.x = particle.lastPos.x + t * vx;
+ particle.pos.y = particle.lastPos.y + t * vy;
+ }
+ }
+ if(particle.pos.x > this.width - 1) {
+ particle.pos.x = this.width - 1;
+ }
+ };
+ VerletManager.prototype.createPoint = function (pos) {
+ var composite = new Phaser.Verlet.Composite(this._game);
+ composite.particles.push(new Phaser.Verlet.Particle(pos));
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.createLineSegments = function (vertices, stiffness) {
+ var i;
+ var composite = new Phaser.Verlet.Composite(this._game);
+ for(i in vertices) {
+ composite.particles.push(new Phaser.Verlet.Particle(vertices[i]));
+ if(i > 0) {
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[i], composite.particles[i - 1], stiffness));
+ }
+ }
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.createCloth = function (origin, width, height, segments, pinMod, stiffness) {
+ var composite = new Phaser.Verlet.Composite(this._game);
+ var xStride = width / segments;
+ var yStride = height / segments;
+ var x, y;
+ for(y = 0; y < segments; ++y) {
+ for(x = 0; x < segments; ++x) {
+ var px = origin.x + x * xStride - width / 2 + xStride / 2;
+ var py = origin.y + y * yStride - height / 2 + yStride / 2;
+ composite.particles.push(new Phaser.Verlet.Particle(new Phaser.Vector2(px, py)));
+ if(x > 0) {
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[y * segments + x], composite.particles[y * segments + x - 1], stiffness));
+ }
+ if(y > 0) {
+ composite.constraints.push(new Phaser.Verlet.DistanceConstraint(composite.particles[y * segments + x], composite.particles[(y - 1) * segments + x], stiffness));
+ }
+ }
+ }
+ for(x = 0; x < segments; ++x) {
+ if(x % pinMod == 0) {
+ composite.pin(x);
+ }
+ }
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.createTire = function (origin, radius, segments, spokeStiffness, treadStiffness) {
+ var stride = (2 * Math.PI) / segments;
+ var i;
+ var composite = new Phaser.Verlet.Composite(this._game);
+ // particles
+ for(i = 0; i < segments; ++i) {
+ var theta = i * stride;
+ composite.particles.push(new Verlet.Particle(new Phaser.Vector2(origin.x + Math.cos(theta) * radius, origin.y + Math.sin(theta) * radius)));
+ }
+ var center = new Verlet.Particle(origin);
+ composite.particles.push(center);
+ // constraints
+ for(i = 0; i < segments; ++i) {
+ composite.constraints.push(new Verlet.DistanceConstraint(composite.particles[i], composite.particles[(i + 1) % segments], treadStiffness));
+ composite.constraints.push(new Verlet.DistanceConstraint(composite.particles[i], center, spokeStiffness));
+ composite.constraints.push(new Verlet.DistanceConstraint(composite.particles[i], composite.particles[(i + 5) % segments], treadStiffness));
+ }
+ this.composites.push(composite);
+ return composite;
+ };
+ VerletManager.prototype.update = function () {
+ if(this.composites.length == 0) {
+ return;
+ }
+ var i, j, c;
+ for(c in this.composites) {
+ for(i in this.composites[c].particles) {
+ var particles = this.composites[c].particles;
+ // calculate velocity
+ var velocity = particles[i].pos.sub(particles[i].lastPos).scale(this.friction);
+ // ground friction
+ if(particles[i].pos.y >= this.height - 1 && velocity.length2() > 0.000001) {
+ var m = velocity.length();
+ velocity.x /= m;
+ velocity.y /= m;
+ velocity.mutableScale(m * this.groundFriction);
+ }
+ // save last good state
+ particles[i].lastPos.mutableSet(particles[i].pos);
+ // gravity
+ particles[i].pos.mutableAdd(this.gravity);
+ // inertia
+ particles[i].pos.mutableAdd(velocity);
+ }
+ }
+ // handle dragging of entities
+ if(this.draggedEntity) {
+ this.draggedEntity.pos.mutableSet(new Phaser.Vector2(this._game.input.x, this._game.input.y));
+ }
+ // relax
+ var stepCoef = 1 / this.step;
+ for(c in this.composites) {
+ var constraints = this.composites[c].constraints;
+ for(i = 0; i < this.step; ++i) {
+ for(j in constraints) {
+ constraints[j].relax(stepCoef);
+ }
+ }
+ }
+ // bounds checking
+ for(c in this.composites) {
+ var particles = this.composites[c].particles;
+ for(i in particles) {
+ this.bounds(particles[i]);
+ }
+ }
+ };
+ VerletManager.prototype.mouseDownHandler = function () {
+ var nearest = this.nearestEntity();
+ if(nearest) {
+ this.draggedEntity = nearest;
+ }
+ };
+ VerletManager.prototype.mouseUpHandler = function () {
+ this.draggedEntity = null;
+ };
+ VerletManager.prototype.nearestEntity = function () {
+ var c, i;
+ var d2Nearest = 0;
+ var entity = null;
+ var constraintsNearest = null;
+ // find nearest point
+ for(c in this.composites) {
+ var particles = this.composites[c].particles;
+ for(i in particles) {
+ var d2 = particles[i].pos.dist2(new Phaser.Vector2(this._game.input.x, this._game.input.y));
+ if(d2 <= this.selectionRadius * this.selectionRadius && (entity == null || d2 < d2Nearest)) {
+ entity = particles[i];
+ constraintsNearest = this.composites[c].constraints;
+ d2Nearest = d2;
+ }
+ }
+ }
+ // search for pinned constraints for this entity
+ for(i in constraintsNearest) {
+ if(constraintsNearest[i] instanceof Verlet.PinConstraint && constraintsNearest[i].a == entity) {
+ entity = constraintsNearest[i];
+ }
+ }
+ return entity;
+ };
+ VerletManager.prototype.render = function () {
+ var i, c;
+ for(c in this.composites) {
+ // draw constraints
+ if(this.composites[c].drawConstraints) {
+ this.composites[c].drawConstraints(this.context, this.composites[c]);
+ } else {
+ var constraints = this.composites[c].constraints;
+ for(i in constraints) {
+ constraints[i].render(this.context);
+ }
+ }
+ // draw particles
+ if(this.composites[c].drawParticles) {
+ this.composites[c].drawParticles(this.context, this.composites[c]);
+ } else {
+ var particles = this.composites[c].particles;
+ for(i in particles) {
+ particles[i].render(this.context);
+ }
+ }
+ }
+ // highlight nearest / dragged entity
+ var nearest = this.draggedEntity || this.nearestEntity();
+ if(nearest) {
+ this.context.beginPath();
+ this.context.arc(nearest.pos.x, nearest.pos.y, 8, 0, 2 * Math.PI);
+ this.context.strokeStyle = this.highlightColor;
+ this.context.stroke();
+ }
+ };
+ return VerletManager;
+ })();
+ Verlet.VerletManager = VerletManager;
+ })(Phaser.Verlet || (Phaser.Verlet = {}));
+ var Verlet = Phaser.Verlet;
+})(Phaser || (Phaser = {}));
///
/**
* Phaser - World
@@ -14847,7 +15581,9 @@ var Phaser;
///
///
///
+///
///
+///
///
///
///
@@ -15025,6 +15761,7 @@ var Phaser;
this.rnd = new Phaser.RandomDataGenerator([
(Date.now() * Math.random()).toString()
]);
+ this.verlet = new Phaser.Verlet.VerletManager(this, width, height);
this.framerate = 60;
this.isBooted = true;
this.input.start();
@@ -15075,6 +15812,7 @@ var Phaser;
this.tweens.update();
this.input.update();
this.stage.update();
+ this.verlet.update();
this._accumulator += this.time.delta;
if(this._accumulator > this._maxAccumulation) {
this._accumulator = this._maxAccumulation;