2017-11-06 04:50:07 +00:00
|
|
|
// Phaser.Physics.Arcade.World
|
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
var Body = require('./Body');
|
2017-11-06 04:50:07 +00:00
|
|
|
var Class = require('../../utils/Class');
|
|
|
|
var Rectangle = require('../../geom/rectangle/Rectangle');
|
|
|
|
var Vector2 = require('../../math/Vector2');
|
2017-11-08 17:18:41 +00:00
|
|
|
var DistanceBetween = require('../../math/distance/DistanceBetween');
|
|
|
|
var Clamp = require('../../math/Clamp');
|
2017-11-06 04:50:07 +00:00
|
|
|
var CONST = require('./const');
|
2017-11-08 17:18:41 +00:00
|
|
|
var GetValue = require('../../utils/object/GetValue');
|
|
|
|
var Set = require('../../structs/Set');
|
2017-11-06 04:50:07 +00:00
|
|
|
|
|
|
|
var World = new Class({
|
|
|
|
|
|
|
|
initialize:
|
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
function World (scene, config)
|
2017-11-06 04:50:07 +00:00
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
this.scene = scene;
|
2017-11-06 04:50:07 +00:00
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.events = scene.sys.events;
|
2017-11-06 04:50:07 +00:00
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.bodies = new Set();
|
2017-11-06 04:50:07 +00:00
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.gravity = new Vector2(GetValue(config, 'gravity.x', 0), GetValue(config, 'gravity.y', 0));
|
2017-11-06 04:50:07 +00:00
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.bounds = new Rectangle(
|
|
|
|
GetValue(config, 'x', 0),
|
|
|
|
GetValue(config, 'y', 0),
|
|
|
|
GetValue(config, 'width', scene.sys.game.config.width),
|
|
|
|
GetValue(config, 'height', scene.sys.game.config.height)
|
|
|
|
);
|
2017-11-06 04:50:07 +00:00
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.checkCollision = {
|
|
|
|
up: GetValue(config, 'checkCollision.up', true),
|
|
|
|
down: GetValue(config, 'checkCollision.down', true),
|
|
|
|
left: GetValue(config, 'checkCollision.left', true),
|
|
|
|
right: GetValue(config, 'checkCollision.right', true)
|
|
|
|
};
|
2017-11-06 04:50:07 +00:00
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.maxObjects = GetValue(config, 'maxObjects', 10);
|
|
|
|
|
|
|
|
this.maxLevels = GetValue(config, 'maxLevels',40);
|
|
|
|
|
|
|
|
this.OVERLAP_BIAS = GetValue(config, 'overlapBias', 4);
|
|
|
|
|
|
|
|
this.forceX = GetValue(config, 'forceX', false);
|
2017-11-06 04:50:07 +00:00
|
|
|
|
|
|
|
this.sortDirection = CONST.LEFT_RIGHT;
|
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.skipQuadTree = GetValue(config, 'skipQuadTree', true);
|
2017-11-06 04:50:07 +00:00
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.isPaused = GetValue(config, 'isPaused', false);
|
2017-11-06 04:50:07 +00:00
|
|
|
|
|
|
|
// this.quadTree = new Phaser.QuadTree(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels);
|
|
|
|
|
|
|
|
this._total = 0;
|
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
this.drawDebug = GetValue(config, 'debug', false);
|
|
|
|
this.debugGraphic;
|
|
|
|
|
2017-11-06 04:50:07 +00:00
|
|
|
// this.setBoundsToWorld();
|
2017-11-08 17:18:41 +00:00
|
|
|
|
|
|
|
this.defaults = {
|
|
|
|
debugShowBody: GetValue(config, 'debugShowBody', true),
|
|
|
|
debugShowVelocity: GetValue(config, 'debugShowVelocity', true),
|
|
|
|
bodyDebugColor: GetValue(config, 'debugBodyColor', 0xff00ff),
|
|
|
|
velocityDebugColor: GetValue(config, 'debugVelocityColor', 0x00ff00)
|
|
|
|
};
|
|
|
|
|
|
|
|
if (this.drawDebug)
|
|
|
|
{
|
|
|
|
this.createDebugGraphic();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
createDebugGraphic: function ()
|
|
|
|
{
|
|
|
|
var graphic = this.scene.sys.add.graphics({ x: 0, y: 0 });
|
|
|
|
|
|
|
|
graphic.setZ(Number.MAX_SAFE_INTEGER);
|
|
|
|
|
|
|
|
this.debugGraphic = graphic;
|
|
|
|
|
|
|
|
this.drawDebug = true;
|
|
|
|
|
|
|
|
return graphic;
|
2017-11-06 04:50:07 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
setBounds: function (x, y, width, height)
|
|
|
|
{
|
|
|
|
this.bounds.setTo(x, y, width, height);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
pause: function ()
|
|
|
|
{
|
|
|
|
this.isPaused = true;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
resume: function ()
|
|
|
|
{
|
|
|
|
this.isPaused = false;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function (time, delta)
|
|
|
|
{
|
|
|
|
if (this.isPaused || this.bodies.size === 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this.delta = Math.min(delta / 1000, this.maxStep) * this.timeScale;
|
|
|
|
delta /= 1000;
|
|
|
|
|
|
|
|
this.delta = delta;
|
|
|
|
|
|
|
|
// Update all active bodies
|
|
|
|
|
|
|
|
var i;
|
|
|
|
var body;
|
|
|
|
var bodies = this.bodies.entries;
|
|
|
|
var len = bodies.length;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
body = bodies[i];
|
|
|
|
|
|
|
|
if (body.enable)
|
|
|
|
{
|
|
|
|
body.update(delta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
postUpdate: function ()
|
|
|
|
{
|
|
|
|
var i;
|
|
|
|
var body;
|
|
|
|
var bodies = this.bodies.entries;
|
|
|
|
var len = bodies.length;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
body = bodies[i];
|
|
|
|
|
|
|
|
if (body.enable)
|
|
|
|
{
|
|
|
|
body.postUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.drawDebug)
|
|
|
|
{
|
|
|
|
var graphics = this.debugGraphic;
|
|
|
|
|
|
|
|
graphics.clear();
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
body = bodies[i];
|
|
|
|
|
|
|
|
if (body.willDrawDebug())
|
|
|
|
{
|
|
|
|
body.drawDebug(graphics);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-11-06 04:50:07 +00:00
|
|
|
updateMotion: function (body)
|
|
|
|
{
|
|
|
|
if (body.allowRotation)
|
|
|
|
{
|
|
|
|
var velocityDelta = this.computeVelocity(0, body, body.angularVelocity, body.angularAcceleration, body.angularDrag, body.maxAngular) - body.angularVelocity;
|
|
|
|
|
|
|
|
body.angularVelocity += velocityDelta;
|
2017-11-08 17:18:41 +00:00
|
|
|
body.rotation += (body.angularVelocity * this.delta);
|
2017-11-06 04:50:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
body.velocity.x = this.computeVelocity(1, body, body.velocity.x, body.acceleration.x, body.drag.x, body.maxVelocity.x);
|
|
|
|
body.velocity.y = this.computeVelocity(2, body, body.velocity.y, body.acceleration.y, body.drag.y, body.maxVelocity.y);
|
|
|
|
},
|
|
|
|
|
|
|
|
computeVelocity: function (axis, body, velocity, acceleration, drag, max)
|
|
|
|
{
|
|
|
|
if (max === undefined) { max = 10000; }
|
|
|
|
|
|
|
|
if (axis === 1 && body.allowGravity)
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
velocity += (this.gravity.x + body.gravity.x) * this.delta;
|
2017-11-06 04:50:07 +00:00
|
|
|
}
|
|
|
|
else if (axis === 2 && body.allowGravity)
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
velocity += (this.gravity.y + body.gravity.y) * this.delta;
|
2017-11-06 04:50:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (acceleration)
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
velocity += acceleration * this.delta;
|
2017-11-06 04:50:07 +00:00
|
|
|
}
|
|
|
|
else if (drag && body.allowDrag)
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
drag *= this.delta;
|
2017-11-06 04:50:07 +00:00
|
|
|
|
|
|
|
if (velocity - drag > 0)
|
|
|
|
{
|
|
|
|
velocity -= drag;
|
|
|
|
}
|
|
|
|
else if (velocity + drag < 0)
|
|
|
|
{
|
|
|
|
velocity += drag;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
velocity = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (velocity > max)
|
|
|
|
{
|
|
|
|
velocity = max;
|
|
|
|
}
|
|
|
|
else if (velocity < -max)
|
|
|
|
{
|
|
|
|
velocity = -max;
|
|
|
|
}
|
|
|
|
|
|
|
|
return velocity;
|
|
|
|
},
|
|
|
|
|
|
|
|
overlap: function (object1, object2, overlapCallback, processCallback, callbackContext)
|
|
|
|
{
|
|
|
|
overlapCallback = overlapCallback || null;
|
|
|
|
processCallback = processCallback || null;
|
|
|
|
callbackContext = callbackContext || overlapCallback;
|
|
|
|
|
|
|
|
this._total = 0;
|
|
|
|
|
|
|
|
this.collideObjects(object1, object2, overlapCallback, processCallback, callbackContext, true);
|
|
|
|
|
|
|
|
return (this._total > 0);
|
|
|
|
},
|
|
|
|
|
|
|
|
collide: function (object1, object2, collideCallback, processCallback, callbackContext)
|
|
|
|
{
|
|
|
|
collideCallback = collideCallback || null;
|
|
|
|
processCallback = processCallback || null;
|
|
|
|
callbackContext = callbackContext || collideCallback;
|
|
|
|
|
|
|
|
this._total = 0;
|
|
|
|
|
|
|
|
this.collideObjects(object1, object2, collideCallback, processCallback, callbackContext, false);
|
|
|
|
|
|
|
|
return (this._total > 0);
|
|
|
|
},
|
|
|
|
|
|
|
|
sortLeftRight: function (a, b)
|
|
|
|
{
|
|
|
|
if (!a.body || !b.body)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.body.x - b.body.x;
|
|
|
|
},
|
|
|
|
|
|
|
|
sortRightLeft: function (a, b)
|
|
|
|
{
|
|
|
|
if (!a.body || !b.body)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.body.x - a.body.x;
|
|
|
|
},
|
|
|
|
|
|
|
|
sortTopBottom: function (a, b)
|
|
|
|
{
|
|
|
|
if (!a.body || !b.body)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.body.y - b.body.y;
|
|
|
|
},
|
|
|
|
|
|
|
|
sortBottomTop: function (a, b)
|
|
|
|
{
|
|
|
|
if (!a.body || !b.body)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.body.y - a.body.y;
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
sort: function (group, sortDirection)
|
|
|
|
{
|
|
|
|
if (group.physicsSortDirection !== null)
|
|
|
|
{
|
|
|
|
sortDirection = group.physicsSortDirection;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (sortDirection === undefined) { sortDirection = this.sortDirection; }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sortDirection === Phaser.Physics.Arcade.LEFT_RIGHT)
|
|
|
|
{
|
|
|
|
// Game world is say 2000x600 and you start at 0
|
|
|
|
group.hash.sort(this.sortLeftRight);
|
|
|
|
}
|
|
|
|
else if (sortDirection === Phaser.Physics.Arcade.RIGHT_LEFT)
|
|
|
|
{
|
|
|
|
// Game world is say 2000x600 and you start at 2000
|
|
|
|
group.hash.sort(this.sortRightLeft);
|
|
|
|
}
|
|
|
|
else if (sortDirection === Phaser.Physics.Arcade.TOP_BOTTOM)
|
|
|
|
{
|
|
|
|
// Game world is say 800x2000 and you start at 0
|
|
|
|
group.hash.sort(this.sortTopBottom);
|
|
|
|
}
|
|
|
|
else if (sortDirection === Phaser.Physics.Arcade.BOTTOM_TOP)
|
|
|
|
{
|
|
|
|
// Game world is say 800x2000 and you start at 2000
|
|
|
|
group.hash.sort(this.sortBottomTop);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
*/
|
|
|
|
|
|
|
|
collideObjects: function (object1, object2, collideCallback, processCallback, callbackContext, overlapOnly)
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
var i;
|
|
|
|
|
2017-11-06 04:50:07 +00:00
|
|
|
if (!Array.isArray(object1) && Array.isArray(object2))
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
for (i = 0; i < object2.length; i++)
|
2017-11-06 04:50:07 +00:00
|
|
|
{
|
|
|
|
if (!object2[i]) { continue; }
|
|
|
|
|
|
|
|
this.collideHandler(object1, object2[i], collideCallback, processCallback, callbackContext, overlapOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Array.isArray(object1) && !Array.isArray(object2))
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
for (i = 0; i < object1.length; i++)
|
2017-11-06 04:50:07 +00:00
|
|
|
{
|
|
|
|
if (!object1[i]) { continue; }
|
|
|
|
|
|
|
|
this.collideHandler(object1[i], object2, collideCallback, processCallback, callbackContext, overlapOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Array.isArray(object1) && Array.isArray(object2))
|
|
|
|
{
|
2017-11-08 17:18:41 +00:00
|
|
|
for (i = 0; i < object1.length; i++)
|
2017-11-06 04:50:07 +00:00
|
|
|
{
|
|
|
|
if (!object1[i]) { continue; }
|
|
|
|
|
|
|
|
for (var j = 0; j < object2.length; j++)
|
|
|
|
{
|
|
|
|
if (!object2[j]) { continue; }
|
|
|
|
|
|
|
|
this.collideHandler(object1[i], object2[j], collideCallback, processCallback, callbackContext, overlapOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.collideHandler(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-11-08 17:18:41 +00:00
|
|
|
collideHandler: function (object1, object2, collideCallback, processCallback, callbackContext, overlapOnly)
|
|
|
|
{
|
|
|
|
// Only collide valid objects
|
|
|
|
// if (object2 === undefined && object1.physicsType === Phaser.GROUP)
|
|
|
|
// {
|
|
|
|
// this.sort(object1);
|
|
|
|
// this.collideGroupVsSelf(object1, collideCallback, processCallback, callbackContext, overlapOnly);
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// If neither of the objects are set or exist then bail out
|
|
|
|
if (!object1 || !object2 || !object1.enable || !object2.enable)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.collideSpriteVsSprite(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly);
|
|
|
|
},
|
|
|
|
|
|
|
|
collideSpriteVsSprite: function (sprite1, sprite2, collideCallback, processCallback, callbackContext, overlapOnly)
|
|
|
|
{
|
|
|
|
if (!sprite1.body || !sprite2.body)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.separate(sprite1.body, sprite2.body, processCallback, callbackContext, overlapOnly))
|
|
|
|
{
|
|
|
|
if (collideCallback)
|
|
|
|
{
|
|
|
|
collideCallback.call(callbackContext, sprite1, sprite2);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._total++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
separate: function (body1, body2, processCallback, callbackContext, overlapOnly)
|
|
|
|
{
|
|
|
|
if (
|
|
|
|
!body1.enable ||
|
|
|
|
!body2.enable ||
|
|
|
|
body1.checkCollision.none ||
|
|
|
|
body2.checkCollision.none ||
|
|
|
|
!this.intersects(body1, body2))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// They overlap. Is there a custom process callback? If it returns true then we can carry on, otherwise we should abort.
|
|
|
|
if (processCallback && processCallback.call(callbackContext, body1.gameObject, body2.gameObject) === false)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Circle vs. Circle quick bail out
|
|
|
|
if (body1.isCircle && body2.isCircle)
|
|
|
|
{
|
|
|
|
return this.separateCircle(body1, body2, overlapOnly);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We define the behavior of bodies in a collision circle and rectangle
|
|
|
|
// If a collision occurs in the corner points of the rectangle, the body behave like circles
|
|
|
|
|
|
|
|
// Either body1 or body2 is a circle
|
|
|
|
if (body1.isCircle !== body2.isCircle)
|
|
|
|
{
|
|
|
|
var bodyRect = (body1.isCircle) ? body2 : body1;
|
|
|
|
var bodyCircle = (body1.isCircle) ? body1 : body2;
|
|
|
|
|
|
|
|
var rect = {
|
|
|
|
x: bodyRect.x,
|
|
|
|
y: bodyRect.y,
|
|
|
|
right: bodyRect.right,
|
|
|
|
bottom: bodyRect.bottom
|
|
|
|
};
|
|
|
|
|
|
|
|
var circle = bodyCircle.center;
|
|
|
|
|
|
|
|
if (circle.y < rect.y || circle.y > rect.bottom)
|
|
|
|
{
|
|
|
|
if (circle.x < rect.x || circle.x > rect.right)
|
|
|
|
{
|
|
|
|
return this.separateCircle(body1, body2, overlapOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var resultX = false;
|
|
|
|
var resultY = false;
|
|
|
|
|
|
|
|
// Do we separate on x or y first?
|
|
|
|
if (this.forceX || Math.abs(this.gravity.y + body1.gravity.y) < Math.abs(this.gravity.x + body1.gravity.x))
|
|
|
|
{
|
|
|
|
resultX = this.separateX(body1, body2, overlapOnly);
|
|
|
|
|
|
|
|
// Are they still intersecting? Let's do the other axis then
|
|
|
|
if (this.intersects(body1, body2))
|
|
|
|
{
|
|
|
|
resultY = this.separateY(body1, body2, overlapOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
resultY = this.separateY(body1, body2, overlapOnly);
|
|
|
|
|
|
|
|
// Are they still intersecting? Let's do the other axis then
|
|
|
|
if (this.intersects(body1, body2))
|
|
|
|
{
|
|
|
|
resultX = this.separateX(body1, body2, overlapOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var result = (resultX || resultY);
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
if (overlapOnly)
|
|
|
|
{
|
|
|
|
if (body1.onOverlap)
|
|
|
|
{
|
|
|
|
body1.onOverlap.dispatch(body1.sprite, body2.sprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body2.onOverlap)
|
|
|
|
{
|
|
|
|
body2.onOverlap.dispatch(body2.sprite, body1.sprite);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (body1.onCollide)
|
|
|
|
{
|
|
|
|
body1.onCollide.dispatch(body1.sprite, body2.sprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body2.onCollide)
|
|
|
|
{
|
|
|
|
body2.onCollide.dispatch(body2.sprite, body1.sprite);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
intersects: function (body1, body2)
|
|
|
|
{
|
|
|
|
if (body1 === body2)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body1.isCircle)
|
|
|
|
{
|
|
|
|
if (body2.isCircle)
|
|
|
|
{
|
|
|
|
// Circle vs. Circle
|
|
|
|
return DistanceBetween(body1.center.x, body1.center.y, body2.center.x, body2.center.y) <= (body1.halfWidth + body2.halfWidth);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Circle vs. Rect
|
|
|
|
return this.circleBodyIntersects(body1, body2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (body2.isCircle)
|
|
|
|
{
|
|
|
|
// Rect vs. Circle
|
|
|
|
return this.circleBodyIntersects(body2, body1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Rect vs. Rect
|
|
|
|
if (body1.right <= body2.position.x)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body1.bottom <= body2.position.y)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body1.position.x >= body2.right)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body1.position.y >= body2.bottom)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
circleBodyIntersects: function (circle, body)
|
|
|
|
{
|
|
|
|
var x = Clamp(circle.center.x, body.left, body.right);
|
|
|
|
var y = Clamp(circle.center.y, body.top, body.bottom);
|
|
|
|
|
|
|
|
var dx = (circle.center.x - x) * (circle.center.x - x);
|
|
|
|
var dy = (circle.center.y - y) * (circle.center.y - y);
|
|
|
|
|
|
|
|
return (dx + dy) <= (circle.halfWidth * circle.halfWidth);
|
|
|
|
},
|
|
|
|
|
|
|
|
separateCircle: function (body1, body2, overlapOnly)
|
|
|
|
{
|
|
|
|
// Set the bounding box overlap values
|
|
|
|
this.getOverlapX(body1, body2);
|
|
|
|
this.getOverlapY(body1, body2);
|
|
|
|
|
|
|
|
var dx = body2.center.x - body1.center.x;
|
|
|
|
var dy = body2.center.y - body1.center.y;
|
|
|
|
|
|
|
|
var angleCollision = Math.atan2(dy, dx);
|
|
|
|
|
|
|
|
var overlap = 0;
|
|
|
|
|
|
|
|
if (body1.isCircle !== body2.isCircle)
|
|
|
|
{
|
|
|
|
var rect = {
|
|
|
|
x: (body2.isCircle) ? body1.position.x : body2.position.x,
|
|
|
|
y: (body2.isCircle) ? body1.position.y : body2.position.y,
|
|
|
|
right: (body2.isCircle) ? body1.right : body2.right,
|
|
|
|
bottom: (body2.isCircle) ? body1.bottom : body2.bottom
|
|
|
|
};
|
|
|
|
|
|
|
|
var circle = {
|
|
|
|
x: (body1.isCircle) ? body1.center.x : body2.center.x,
|
|
|
|
y: (body1.isCircle) ? body1.center.y : body2.center.y,
|
|
|
|
radius: (body1.isCircle) ? body1.halfWidth : body2.halfWidth
|
|
|
|
};
|
|
|
|
|
|
|
|
if (circle.y < rect.y)
|
|
|
|
{
|
|
|
|
if (circle.x < rect.x)
|
|
|
|
{
|
|
|
|
overlap = DistanceBetween(circle.x, circle.y, rect.x, rect.y) - circle.radius;
|
|
|
|
}
|
|
|
|
else if (circle.x > rect.right)
|
|
|
|
{
|
|
|
|
overlap = DistanceBetween(circle.x, circle.y, rect.right, rect.y) - circle.radius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (circle.y > rect.bottom)
|
|
|
|
{
|
|
|
|
if (circle.x < rect.x)
|
|
|
|
{
|
|
|
|
overlap = DistanceBetween(circle.x, circle.y, rect.x, rect.bottom) - circle.radius;
|
|
|
|
}
|
|
|
|
else if (circle.x > rect.right)
|
|
|
|
{
|
|
|
|
overlap = DistanceBetween(circle.x, circle.y, rect.right, rect.bottom) - circle.radius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
overlap *= -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
overlap = (body1.halfWidth + body2.halfWidth) - DistanceBetween(body1.center.x, body1.center.y, body2.center.x, body2.center.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can't separate two immovable bodies, or a body with its own custom separation logic
|
|
|
|
if (overlapOnly || overlap === 0 || (body1.immovable && body2.immovable) || body1.customSeparateX || body2.customSeparateX)
|
|
|
|
{
|
|
|
|
if (overlap !== 0)
|
|
|
|
{
|
|
|
|
if (body1.onOverlap)
|
|
|
|
{
|
|
|
|
body1.onOverlap.dispatch(body1.sprite, body2.sprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body2.onOverlap)
|
|
|
|
{
|
|
|
|
body2.onOverlap.dispatch(body2.sprite, body1.sprite);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return true if there was some overlap, otherwise false
|
|
|
|
return (overlap !== 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transform the velocity vector to the coordinate system oriented along the direction of impact.
|
|
|
|
// This is done to eliminate the vertical component of the velocity
|
|
|
|
var v1 = {
|
|
|
|
x: body1.velocity.x * Math.cos(angleCollision) + body1.velocity.y * Math.sin(angleCollision),
|
|
|
|
y: body1.velocity.x * Math.sin(angleCollision) - body1.velocity.y * Math.cos(angleCollision)
|
|
|
|
};
|
|
|
|
|
|
|
|
var v2 = {
|
|
|
|
x: body2.velocity.x * Math.cos(angleCollision) + body2.velocity.y * Math.sin(angleCollision),
|
|
|
|
y: body2.velocity.x * Math.sin(angleCollision) - body2.velocity.y * Math.cos(angleCollision)
|
|
|
|
};
|
|
|
|
|
|
|
|
// We expect the new velocity after impact
|
|
|
|
var tempVel1 = ((body1.mass - body2.mass) * v1.x + 2 * body2.mass * v2.x) / (body1.mass + body2.mass);
|
|
|
|
var tempVel2 = (2 * body1.mass * v1.x + (body2.mass - body1.mass) * v2.x) / (body1.mass + body2.mass);
|
|
|
|
|
|
|
|
// We convert the vector to the original coordinate system and multiplied by factor of rebound
|
|
|
|
if (!body1.immovable)
|
|
|
|
{
|
|
|
|
body1.velocity.x = (tempVel1 * Math.cos(angleCollision) - v1.y * Math.sin(angleCollision)) * body1.bounce.x;
|
|
|
|
body1.velocity.y = (v1.y * Math.cos(angleCollision) + tempVel1 * Math.sin(angleCollision)) * body1.bounce.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!body2.immovable)
|
|
|
|
{
|
|
|
|
body2.velocity.x = (tempVel2 * Math.cos(angleCollision) - v2.y * Math.sin(angleCollision)) * body2.bounce.x;
|
|
|
|
body2.velocity.y = (v2.y * Math.cos(angleCollision) + tempVel2 * Math.sin(angleCollision)) * body2.bounce.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
// When the collision angle is almost perpendicular to the total initial velocity vector
|
|
|
|
// (collision on a tangent) vector direction can be determined incorrectly.
|
|
|
|
// This code fixes the problem
|
|
|
|
|
|
|
|
if (Math.abs(angleCollision) < Math.PI / 2)
|
|
|
|
{
|
|
|
|
if ((body1.velocity.x > 0) && !body1.immovable && (body2.velocity.x > body1.velocity.x))
|
|
|
|
{
|
|
|
|
body1.velocity.x *= -1;
|
|
|
|
}
|
|
|
|
else if ((body2.velocity.x < 0) && !body2.immovable && (body1.velocity.x < body2.velocity.x))
|
|
|
|
{
|
|
|
|
body2.velocity.x *= -1;
|
|
|
|
}
|
|
|
|
else if ((body1.velocity.y > 0) && !body1.immovable && (body2.velocity.y > body1.velocity.y))
|
|
|
|
{
|
|
|
|
body1.velocity.y *= -1;
|
|
|
|
}
|
|
|
|
else if ((body2.velocity.y < 0) && !body2.immovable && (body1.velocity.y < body2.velocity.y))
|
|
|
|
{
|
|
|
|
body2.velocity.y *= -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Math.abs(angleCollision) > Math.PI / 2)
|
|
|
|
{
|
|
|
|
if ((body1.velocity.x < 0) && !body1.immovable && (body2.velocity.x < body1.velocity.x))
|
|
|
|
{
|
|
|
|
body1.velocity.x *= -1;
|
|
|
|
}
|
|
|
|
else if ((body2.velocity.x > 0) && !body2.immovable && (body1.velocity.x > body2.velocity.x))
|
|
|
|
{
|
|
|
|
body2.velocity.x *= -1;
|
|
|
|
}
|
|
|
|
else if ((body1.velocity.y < 0) && !body1.immovable && (body2.velocity.y < body1.velocity.y))
|
|
|
|
{
|
|
|
|
body1.velocity.y *= -1;
|
|
|
|
}
|
|
|
|
else if ((body2.velocity.y > 0) && !body2.immovable && (body1.velocity.x > body2.velocity.y))
|
|
|
|
{
|
|
|
|
body2.velocity.y *= -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!body1.immovable)
|
|
|
|
{
|
|
|
|
body1.x += (body1.velocity.x * this.game.time.physicsElapsed) - overlap * Math.cos(angleCollision);
|
|
|
|
body1.y += (body1.velocity.y * this.game.time.physicsElapsed) - overlap * Math.sin(angleCollision);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!body2.immovable)
|
|
|
|
{
|
|
|
|
body2.x += (body2.velocity.x * this.game.time.physicsElapsed) + overlap * Math.cos(angleCollision);
|
|
|
|
body2.y += (body2.velocity.y * this.game.time.physicsElapsed) + overlap * Math.sin(angleCollision);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body1.onCollide)
|
|
|
|
{
|
|
|
|
body1.onCollide.dispatch(body1.sprite, body2.sprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body2.onCollide)
|
|
|
|
{
|
|
|
|
body2.onCollide.dispatch(body2.sprite, body1.sprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
getOverlapX: function (body1, body2, overlapOnly)
|
|
|
|
{
|
|
|
|
var overlap = 0;
|
|
|
|
var maxOverlap = body1.deltaAbsX() + body2.deltaAbsX() + this.OVERLAP_BIAS;
|
|
|
|
|
|
|
|
if (body1.deltaX() === 0 && body2.deltaX() === 0)
|
|
|
|
{
|
|
|
|
// They overlap but neither of them are moving
|
|
|
|
body1.embedded = true;
|
|
|
|
body2.embedded = true;
|
|
|
|
}
|
|
|
|
else if (body1.deltaX() > body2.deltaX())
|
|
|
|
{
|
|
|
|
// Body1 is moving right and / or Body2 is moving left
|
|
|
|
overlap = body1.right - body2.x;
|
|
|
|
|
|
|
|
if ((overlap > maxOverlap && !overlapOnly) || body1.checkCollision.right === false || body2.checkCollision.left === false)
|
|
|
|
{
|
|
|
|
overlap = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
body1.touching.none = false;
|
|
|
|
body1.touching.right = true;
|
|
|
|
body2.touching.none = false;
|
|
|
|
body2.touching.left = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (body1.deltaX() < body2.deltaX())
|
|
|
|
{
|
|
|
|
// Body1 is moving left and/or Body2 is moving right
|
|
|
|
overlap = body1.x - body2.width - body2.x;
|
|
|
|
|
|
|
|
if ((-overlap > maxOverlap && !overlapOnly) || body1.checkCollision.left === false || body2.checkCollision.right === false)
|
|
|
|
{
|
|
|
|
overlap = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
body1.touching.none = false;
|
|
|
|
body1.touching.left = true;
|
|
|
|
body2.touching.none = false;
|
|
|
|
body2.touching.right = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resets the overlapX to zero if there is no overlap, or to the actual pixel value if there is
|
|
|
|
body1.overlapX = overlap;
|
|
|
|
body2.overlapX = overlap;
|
|
|
|
|
|
|
|
return overlap;
|
|
|
|
},
|
|
|
|
|
|
|
|
getOverlapY: function (body1, body2, overlapOnly)
|
|
|
|
{
|
|
|
|
var overlap = 0;
|
|
|
|
var maxOverlap = body1.deltaAbsY() + body2.deltaAbsY() + this.OVERLAP_BIAS;
|
|
|
|
|
|
|
|
if (body1.deltaY() === 0 && body2.deltaY() === 0)
|
|
|
|
{
|
|
|
|
// They overlap but neither of them are moving
|
|
|
|
body1.embedded = true;
|
|
|
|
body2.embedded = true;
|
|
|
|
}
|
|
|
|
else if (body1.deltaY() > body2.deltaY())
|
|
|
|
{
|
|
|
|
// Body1 is moving down and/or Body2 is moving up
|
|
|
|
overlap = body1.bottom - body2.y;
|
|
|
|
|
|
|
|
if ((overlap > maxOverlap && !overlapOnly) || body1.checkCollision.down === false || body2.checkCollision.up === false)
|
|
|
|
{
|
|
|
|
overlap = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
body1.touching.none = false;
|
|
|
|
body1.touching.down = true;
|
|
|
|
body2.touching.none = false;
|
|
|
|
body2.touching.up = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (body1.deltaY() < body2.deltaY())
|
|
|
|
{
|
|
|
|
// Body1 is moving up and/or Body2 is moving down
|
|
|
|
overlap = body1.y - body2.bottom;
|
|
|
|
|
|
|
|
if ((-overlap > maxOverlap && !overlapOnly) || body1.checkCollision.up === false || body2.checkCollision.down === false)
|
|
|
|
{
|
|
|
|
overlap = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
body1.touching.none = false;
|
|
|
|
body1.touching.up = true;
|
|
|
|
body2.touching.none = false;
|
|
|
|
body2.touching.down = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resets the overlapY to zero if there is no overlap, or to the actual pixel value if there is
|
|
|
|
body1.overlapY = overlap;
|
|
|
|
body2.overlapY = overlap;
|
|
|
|
|
|
|
|
return overlap;
|
|
|
|
},
|
|
|
|
|
|
|
|
separateX: function (body1, body2, overlapOnly)
|
|
|
|
{
|
|
|
|
var overlap = this.getOverlapX(body1, body2, overlapOnly);
|
|
|
|
|
|
|
|
// Can't separate two immovable bodies, or a body with its own custom separation logic
|
|
|
|
if (overlapOnly || overlap === 0 || (body1.immovable && body2.immovable) || body1.customSeparateX || body2.customSeparateX)
|
|
|
|
{
|
|
|
|
// return true if there was some overlap, otherwise false
|
|
|
|
return (overlap !== 0) || (body1.embedded && body2.embedded);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust their positions and velocities accordingly (if there was any overlap)
|
|
|
|
var v1 = body1.velocity.x;
|
|
|
|
var v2 = body2.velocity.x;
|
|
|
|
|
|
|
|
if (!body1.immovable && !body2.immovable)
|
|
|
|
{
|
|
|
|
overlap *= 0.5;
|
|
|
|
|
|
|
|
body1.x -= overlap;
|
|
|
|
body2.x += overlap;
|
|
|
|
|
|
|
|
var nv1 = Math.sqrt((v2 * v2 * body2.mass) / body1.mass) * ((v2 > 0) ? 1 : -1);
|
|
|
|
var nv2 = Math.sqrt((v1 * v1 * body1.mass) / body2.mass) * ((v1 > 0) ? 1 : -1);
|
|
|
|
var avg = (nv1 + nv2) * 0.5;
|
|
|
|
|
|
|
|
nv1 -= avg;
|
|
|
|
nv2 -= avg;
|
|
|
|
|
|
|
|
body1.velocity.x = avg + nv1 * body1.bounce.x;
|
|
|
|
body2.velocity.x = avg + nv2 * body2.bounce.x;
|
|
|
|
}
|
|
|
|
else if (!body1.immovable)
|
|
|
|
{
|
|
|
|
body1.x -= overlap;
|
|
|
|
body1.velocity.x = v2 - v1 * body1.bounce.x;
|
|
|
|
|
|
|
|
// This is special case code that handles things like vertically moving platforms you can ride
|
|
|
|
if (body2.moves)
|
|
|
|
{
|
|
|
|
body1.y += (body2.y - body2.prev.y) * body2.friction.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
body2.x += overlap;
|
|
|
|
body2.velocity.x = v1 - v2 * body2.bounce.x;
|
|
|
|
|
|
|
|
// This is special case code that handles things like vertically moving platforms you can ride
|
|
|
|
if (body1.moves)
|
|
|
|
{
|
|
|
|
body2.y += (body1.y - body1.prev.y) * body1.friction.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got this far then there WAS overlap, and separation is complete, so return true
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
separateY: function (body1, body2, overlapOnly)
|
|
|
|
{
|
|
|
|
var overlap = this.getOverlapY(body1, body2, overlapOnly);
|
|
|
|
|
|
|
|
// Can't separate two immovable bodies, or a body with its own custom separation logic
|
|
|
|
if (overlapOnly || overlap === 0 || (body1.immovable && body2.immovable) || body1.customSeparateY || body2.customSeparateY)
|
|
|
|
{
|
|
|
|
// return true if there was some overlap, otherwise false
|
|
|
|
return (overlap !== 0) || (body1.embedded && body2.embedded);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust their positions and velocities accordingly (if there was any overlap)
|
|
|
|
var v1 = body1.velocity.y;
|
|
|
|
var v2 = body2.velocity.y;
|
|
|
|
|
|
|
|
if (!body1.immovable && !body2.immovable)
|
|
|
|
{
|
|
|
|
overlap *= 0.5;
|
|
|
|
|
|
|
|
body1.y -= overlap;
|
|
|
|
body2.y += overlap;
|
|
|
|
|
|
|
|
var nv1 = Math.sqrt((v2 * v2 * body2.mass) / body1.mass) * ((v2 > 0) ? 1 : -1);
|
|
|
|
var nv2 = Math.sqrt((v1 * v1 * body1.mass) / body2.mass) * ((v1 > 0) ? 1 : -1);
|
|
|
|
var avg = (nv1 + nv2) * 0.5;
|
|
|
|
|
|
|
|
nv1 -= avg;
|
|
|
|
nv2 -= avg;
|
|
|
|
|
|
|
|
body1.velocity.y = avg + nv1 * body1.bounce.y;
|
|
|
|
body2.velocity.y = avg + nv2 * body2.bounce.y;
|
|
|
|
}
|
|
|
|
else if (!body1.immovable)
|
|
|
|
{
|
|
|
|
body1.y -= overlap;
|
|
|
|
body1.velocity.y = v2 - v1 * body1.bounce.y;
|
|
|
|
|
|
|
|
// This is special case code that handles things like horizontal moving platforms you can ride
|
|
|
|
if (body2.moves)
|
|
|
|
{
|
|
|
|
body1.x += (body2.x - body2.prev.x) * body2.friction.x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
body2.y += overlap;
|
|
|
|
body2.velocity.y = v1 - v2 * body2.bounce.y;
|
|
|
|
|
|
|
|
// This is special case code that handles things like horizontal moving platforms you can ride
|
|
|
|
if (body1.moves)
|
|
|
|
{
|
|
|
|
body2.x += (body1.x - body1.prev.x) * body1.friction.x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got this far then there WAS overlap, and separation is complete, so return true
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
enable: function (object)
|
|
|
|
{
|
|
|
|
var i = 1;
|
|
|
|
|
|
|
|
if (Array.isArray(object))
|
|
|
|
{
|
|
|
|
i = object.length;
|
|
|
|
|
|
|
|
while (i--)
|
|
|
|
{
|
|
|
|
if (object[i].hasOwnProperty('children'))
|
|
|
|
{
|
|
|
|
// If it's a Group then we do it on the children regardless
|
|
|
|
this.enable(object[i].children.entries);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.enableBody(object[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (object.hasOwnProperty('children'))
|
|
|
|
{
|
|
|
|
// If it's a Group then we do it on the children regardless
|
|
|
|
this.enable(object.children.entries);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.enableBody(object);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
enableBody: function (object)
|
|
|
|
{
|
|
|
|
if (object.body === null)
|
|
|
|
{
|
|
|
|
object.body = new Body(this, object);
|
|
|
|
|
|
|
|
this.bodies.set(object.body);
|
|
|
|
}
|
|
|
|
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
2017-11-06 04:50:07 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = World;
|