Ported over Arcade Physics into V3 and started working through the classes and functions.

This commit is contained in:
Richard Davey 2017-11-08 17:18:41 +00:00
parent 7cf82f320b
commit 9117b909a8
8 changed files with 1565 additions and 20 deletions

View file

@ -0,0 +1,20 @@
var Class = require('../../utils/Class');
var Factory = require('./Factory');
var World = require('./World');
var Arcade = new Class({
initialize:
function Arcade (physicsManager, config)
{
this.config = config;
physicsManager.world = new World(physicsManager.scene, config);
physicsManager.add = new Factory(physicsManager.world);
}
});
module.exports = Arcade;

View file

@ -0,0 +1,660 @@
// Phaser.Physics.Arcade.Body
var CircleContains = require('../../geom/circle/Contains');
var Class = require('../../utils/Class');
var CONST = require('./const');
// var DegToRad = require('../../math/DegToRad');
// var RadToDeg = require('../../math/RadToDeg');
var Rectangle = require('../../geom/rectangle/Rectangle');
var RectangleContains = require('../../geom/rectangle/Contains');
var Vector2 = require('../../math/Vector2');
var Body = new Class({
initialize:
function Body (world, gameObject)
{
this.world = world;
this.gameObject = gameObject;
this.debugShowBody = world.defaults.debugShowBody;
this.debugShowVelocity = world.defaults.debugShowVelocity;
this.debugBodyColor = world.defaults.bodyDebugColor;
this.enable = true;
this.isCircle = false;
this.radius = 0;
this.offset = new Vector2();
this.position = new Vector2(gameObject.x, gameObject.y);
this.prev = new Vector2(this.position.x, this.position.y);
this.allowRotation = true;
this.rotation = gameObject.angle;
this.preRotation = gameObject.angle;
this.width = gameObject.width;
this.height = gameObject.height;
this.sourceWidth = gameObject.width;
this.sourceHeight = gameObject.height;
if (gameObject.frame)
{
this.sourceWidth = gameObject.frame.realWidth;
this.sourceHeight = gameObject.frame.realHeight;
}
this.halfWidth = Math.abs(gameObject.width / 2);
this.halfHeight = Math.abs(gameObject.height / 2);
this.center = new Vector2(gameObject.x + this.halfWidth, gameObject.y + this.halfHeight);
this.velocity = new Vector2();
this.newVelocity = new Vector2();
this.deltaMax = new Vector2();
this.acceleration = new Vector2();
this.allowDrag = true;
this.drag = new Vector2();
this.allowGravity = true;
this.gravity = new Vector2();
this.bounce = new Vector2();
this.worldBounce = null;
// this.onWorldBounds = null;
// this.onCollide = null;
// this.onOverlap = null;
this.maxVelocity = new Vector2(10000, 10000);
this.friction = new Vector2(1, 0);
this.angularVelocity = 0;
this.angularAcceleration = 0;
this.angularDrag = 0;
this.maxAngular = 1000;
this.mass = 1;
this.angle = 0;
this.speed = 0;
this.facing = CONST.FACING_NONE;
this.immovable = false;
this.moves = true;
this.customSeparateX = false;
this.customSeparateY = false;
this.overlapX = 0;
this.overlapY = 0;
this.overlapR = 0;
this.embedded = false;
this.collideWorldBounds = false;
this.checkCollision = { none: false, up: true, down: true, left: true, right: true };
this.touching = { none: true, up: false, down: false, left: false, right: false };
this.wasTouching = { none: true, up: false, down: false, left: false, right: false };
this.blocked = { none: true, up: false, down: false, left: false, right: false };
this.tilePadding = new Vector2();
this.dirty = false;
this.skipQuadTree = false;
this.syncBounds = false;
this.isMoving = false;
this.stopVelocityOnCollide = true;
// this.moveTimer = 0;
// this.moveDistance = 0;
// this.moveDuration = 0;
// this.moveTarget = null;
// this.moveEnd = null;
// this.onMoveComplete = new Phaser.Signal();
// this.movementCallback = null;
// this.movementCallbackContext = null;
this._reset = true;
this._sx = gameObject.scaleX;
this._sy = gameObject.scaleY;
this._dx = 0;
this._dy = 0;
this._bounds = new Rectangle();
},
updateBounds: function ()
{
var sprite = this.gameObject;
if (this.syncBounds)
{
var b = sprite.getBounds(this._bounds);
// b.ceilAll();
if (b.width !== this.width || b.height !== this.height)
{
this.width = b.width;
this.height = b.height;
this._reset = true;
}
}
else
{
var asx = Math.abs(sprite.scaleX);
var asy = Math.abs(sprite.scaleY);
if (asx !== this._sx || asy !== this._sy)
{
this.width = this.sourceWidth * asx;
this.height = this.sourceHeight * asy;
this._sx = asx;
this._sy = asy;
this._reset = true;
}
}
if (this._reset)
{
this.halfWidth = Math.floor(this.width / 2);
this.halfHeight = Math.floor(this.height / 2);
this.updateCenter();
}
},
updateCenter: function ()
{
this.center.set(this.position.x + this.halfWidth, this.position.y + this.halfHeight);
},
update: function (delta)
{
this.dirty = true;
// Store and reset collision flags
this.wasTouching.none = this.touching.none;
this.wasTouching.up = this.touching.up;
this.wasTouching.down = this.touching.down;
this.wasTouching.left = this.touching.left;
this.wasTouching.right = this.touching.right;
this.touching.none = true;
this.touching.up = false;
this.touching.down = false;
this.touching.left = false;
this.touching.right = false;
this.blocked.none = true;
this.blocked.up = false;
this.blocked.down = false;
this.blocked.left = false;
this.blocked.right = false;
this.overlapR = 0;
this.overlapX = 0;
this.overlapY = 0;
this.embedded = false;
this.updateBounds();
var sprite = this.gameObject;
this.position.x = sprite.x - sprite.displayOriginX + (sprite.scaleX * this.offset.x);
this.position.y = sprite.y - sprite.displayOriginY + (sprite.scaleY * this.offset.y);
// this.position.x -= this.sprite.scale.x < 0 ? this.width : 0;
// this.position.y -= this.sprite.scale.y < 0 ? this.height : 0;
// this.position.x = sprite.x + (sprite.scaleX * this.offset.x);
// this.position.y = sprite.y + (sprite.scaleY * this.offset.y);
this.updateCenter();
this.rotation = sprite.angle;
this.preRotation = this.rotation;
if (this._reset)
{
this.prev.x = this.position.x;
this.prev.y = this.position.y;
}
if (this.moves)
{
this.world.updateMotion(this);
this.newVelocity.set(this.velocity.x * delta, this.velocity.y * delta);
this.position.x += this.newVelocity.x;
this.position.y += this.newVelocity.y;
this.updateCenter();
if (this.position.x !== this.prev.x || this.position.y !== this.prev.y)
{
this.angle = Math.atan2(this.velocity.y, this.velocity.x);
}
this.speed = Math.sqrt(this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y);
// Now the State update will throw collision checks at the Body
// And finally we'll integrate the new position back to the Sprite in postUpdate
if (this.collideWorldBounds)
{
this.checkWorldBounds();
// if (this.checkWorldBounds() && this.onWorldBounds)
// {
// this.onWorldBounds.dispatch(this.sprite, this.blocked.up, this.blocked.down, this.blocked.left, this.blocked.right);
// }
}
}
this._dx = this.deltaX();
this._dy = this.deltaY();
this._reset = false;
},
// Feeds the body results back into the parent gameobject (if there is one)
postUpdate: function ()
{
// Only allow postUpdate to be called once per frame
if (!this.enable || !this.dirty)
{
return;
}
this.dirty = false;
if (this.deltaX() < 0)
{
this.facing = CONST.FACING_LEFT;
}
else if (this.deltaX() > 0)
{
this.facing = CONST.FACING_RIGHT;
}
if (this.deltaY() < 0)
{
this.facing = CONST.FACING_UP;
}
else if (this.deltaY() > 0)
{
this.facing = CONST.FACING_DOWN;
}
if (this.moves)
{
this._dx = this.deltaX();
this._dy = this.deltaY();
if (this.deltaMax.x !== 0 && this._dx !== 0)
{
if (this._dx < 0 && this._dx < -this.deltaMax.x)
{
this._dx = -this.deltaMax.x;
}
else if (this._dx > 0 && this._dx > this.deltaMax.x)
{
this._dx = this.deltaMax.x;
}
}
if (this.deltaMax.y !== 0 && this._dy !== 0)
{
if (this._dy < 0 && this._dy < -this.deltaMax.y)
{
this._dy = -this.deltaMax.y;
}
else if (this._dy > 0 && this._dy > this.deltaMax.y)
{
this._dy = this.deltaMax.y;
}
}
this.gameObject.x += this._dx;
this.gameObject.y += this._dy;
this._reset = true;
}
this.updateCenter();
if (this.allowRotation)
{
this.gameObject.angle += this.deltaZ();
}
this.prev.x = this.position.x;
this.prev.y = this.position.y;
},
checkWorldBounds: function ()
{
var pos = this.position;
var bounds = this.world.bounds;
var check = this.world.checkCollision;
var bx = (this.worldBounce) ? -this.worldBounce.x : -this.bounce.x;
var by = (this.worldBounce) ? -this.worldBounce.y : -this.bounce.y;
if (pos.x < bounds.x && check.left)
{
pos.x = bounds.x;
this.velocity.x *= bx;
this.blocked.left = true;
this.blocked.none = false;
}
else if (this.right > bounds.right && check.right)
{
pos.x = bounds.right - this.width;
this.velocity.x *= bx;
this.blocked.right = true;
this.blocked.none = false;
}
if (pos.y < bounds.y && check.up)
{
pos.y = bounds.y;
this.velocity.y *= by;
this.blocked.up = true;
this.blocked.none = false;
}
else if (this.bottom > bounds.bottom && check.down)
{
pos.y = bounds.bottom - this.height;
this.velocity.y *= by;
this.blocked.down = true;
this.blocked.none = false;
}
return !this.blocked.none;
},
setSize: function (width, height, offsetX, offsetY)
{
if (offsetX === undefined) { offsetX = this.offset.x; }
if (offsetY === undefined) { offsetY = this.offset.y; }
this.sourceWidth = width;
this.sourceHeight = height;
this.width = this.sourceWidth * this._sx;
this.height = this.sourceHeight * this._sy;
this.halfWidth = Math.floor(this.width / 2);
this.halfHeight = Math.floor(this.height / 2);
this.offset.set(offsetX, offsetY);
this.updateCenter();
this.isCircle = false;
this.radius = 0;
return this;
},
setCircle: function (radius, offsetX, offsetY)
{
if (offsetX === undefined) { offsetX = this.offset.x; }
if (offsetY === undefined) { offsetY = this.offset.y; }
if (radius > 0)
{
this.isCircle = true;
this.radius = radius;
this.sourceWidth = radius * 2;
this.sourceHeight = radius * 2;
this.width = this.sourceWidth * this._sx;
this.height = this.sourceHeight * this._sy;
this.halfWidth = Math.floor(this.width / 2);
this.halfHeight = Math.floor(this.height / 2);
this.offset.set(offsetX, offsetY);
this.updateCenter();
}
else
{
this.isCircle = false;
}
return this;
},
reset: function (x, y)
{
this.stop();
var sprite = this.gameObject;
this.position.x = x + (sprite.scaleX * this.offset.x);
this.position.y = y + (sprite.scaleY * this.offset.y);
// this.position.x = (x - (this.sprite.anchor.x * this.sprite.width)) + this.sprite.scale.x * this.offset.x;
// this.position.x -= this.sprite.scale.x < 0 ? this.width : 0;
// this.position.y = (y - (this.sprite.anchor.y * this.sprite.height)) + this.sprite.scale.y * this.offset.y;
// this.position.y -= this.sprite.scale.y < 0 ? this.height : 0;
this.prev.x = this.position.x;
this.prev.y = this.position.y;
this.rotation = this.gameObject.angle;
this.preRotation = this.rotation;
this.updateBounds();
this.updateCenter();
},
stop: function ()
{
this.velocity.set(0);
this.acceleration.set(0);
this.speed = 0;
this.angularVelocity = 0;
this.angularAcceleration = 0;
return this;
},
getBounds: function (obj)
{
obj.x = this.x;
obj.y = this.y;
obj.right = this.right;
obj.bottom = this.bottom;
return obj;
},
hitTest: function (x, y)
{
return (this.isCircle) ? CircleContains(this, x, y) : RectangleContains(this, x, y);
},
onFloor: function ()
{
return this.blocked.down;
},
onCeiling: function ()
{
return this.blocked.up;
},
onWall: function ()
{
return (this.blocked.left || this.blocked.right);
},
deltaAbsX: function ()
{
return (this.deltaX() > 0) ? this.deltaX() : -this.deltaX();
},
deltaAbsY: function ()
{
return (this.deltaY() > 0) ? this.deltaY() : -this.deltaY();
},
deltaX: function ()
{
return this.position.x - this.prev.x;
},
deltaY: function ()
{
return this.position.y - this.prev.y;
},
deltaZ: function ()
{
return this.rotation - this.preRotation;
},
destroy: function ()
{
this.gameObject.body = null;
this.gameObject = null;
},
drawDebug: function (graphic)
{
var pos = this.position;
if (this.debugShowBody)
{
graphic.lineStyle(1, this.debugBodyColor, 1);
graphic.strokeRect(pos.x, pos.y, this.width, this.height);
}
if (this.debugShowVelocity)
{
var x = pos.x + this.halfWidth;
var y = pos.y + this.halfHeight;
graphic.lineStyle(1, this.world.defaults.velocityDebugColor, 1);
graphic.lineBetween(x, y, x + this.velocity.x, y + this.velocity.y);
}
},
willDrawDebug: function ()
{
return (this.debugShowBody || this.debugShowVelocity);
},
x: {
get: function ()
{
return this.position.x;
},
set: function (value)
{
this.position.x = value;
}
},
y: {
get: function ()
{
return this.position.y;
},
set: function (value)
{
this.position.y = value;
}
},
left: {
get: function ()
{
return this.position.x;
}
},
right: {
get: function ()
{
return this.position.x + this.width;
}
},
top: {
get: function ()
{
return this.position.y;
}
},
bottom: {
get: function ()
{
return this.position.y + this.height;
}
}
});
module.exports = Body;

View file

@ -0,0 +1,47 @@
var Body = require('./Body');
var Class = require('../../utils/Class');
// var ImpactBody = require('./ImpactBody');
// var ImpactImage = require('./ImpactImage');
// var ImpactSprite = require('./ImpactSprite');
var Factory = new Class({
initialize:
function Factory (world)
{
this.world = world;
this.sys = world.scene.sys;
},
body: function (gameObject)
{
return new Body(this.world, gameObject);
}
/*
image: function (x, y, key, frame)
{
var image = new ImpactImage(this.world, x, y, key, frame);
this.sys.displayList.add(image);
return image;
},
sprite: function (x, y, key, frame)
{
var sprite = new ImpactSprite(this.world, x, y, key, frame);
this.sys.displayList.add(sprite);
this.sys.updateList.add(sprite);
return sprite;
}
*/
});
module.exports = Factory;

View file

@ -1,41 +1,90 @@
// Phaser.Physics.Arcade.World
var Body = require('./Body');
var Class = require('../../utils/Class');
var Rectangle = require('../../geom/rectangle/Rectangle');
var Vector2 = require('../../math/Vector2');
var DistanceBetween = require('../../math/distance/DistanceBetween');
var Clamp = require('../../math/Clamp');
var CONST = require('./const');
var GetValue = require('../../utils/object/GetValue');
var Set = require('../../structs/Set');
var World = new Class({
initialize:
function World (width, height)
function World (scene, config)
{
this.gravity = new Vector2();
this.scene = scene;
this.bounds = new Rectangle(0, 0, width, height);
this.events = scene.sys.events;
this.checkCollision = { up: true, down: true, left: true, right: true };
this.bodies = new Set();
this.maxObjects = 10;
this.gravity = new Vector2(GetValue(config, 'gravity.x', 0), GetValue(config, 'gravity.y', 0));
this.maxLevels = 4;
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)
);
this.OVERLAP_BIAS = 4;
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)
};
this.forceX = false;
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);
this.sortDirection = CONST.LEFT_RIGHT;
this.skipQuadTree = true;
this.skipQuadTree = GetValue(config, 'skipQuadTree', true);
this.isPaused = false;
this.isPaused = GetValue(config, 'isPaused', false);
// 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;
this.drawDebug = GetValue(config, 'debug', false);
this.debugGraphic;
// this.setBoundsToWorld();
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;
},
setBounds: function (x, y, width, height)
@ -45,6 +94,85 @@ var World = new Class({
return this;
},
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);
}
}
}
},
updateMotion: function (body)
{
if (body.allowRotation)
@ -52,7 +180,7 @@ var World = new Class({
var velocityDelta = this.computeVelocity(0, body, body.angularVelocity, body.angularAcceleration, body.angularDrag, body.maxAngular) - body.angularVelocity;
body.angularVelocity += velocityDelta;
body.rotation += (body.angularVelocity * this.game.time.physicsElapsed);
body.rotation += (body.angularVelocity * this.delta);
}
body.velocity.x = this.computeVelocity(1, body, body.velocity.x, body.acceleration.x, body.drag.x, body.maxVelocity.x);
@ -65,20 +193,20 @@ var World = new Class({
if (axis === 1 && body.allowGravity)
{
velocity += (this.gravity.x + body.gravity.x) * this.game.time.physicsElapsed;
velocity += (this.gravity.x + body.gravity.x) * this.delta;
}
else if (axis === 2 && body.allowGravity)
{
velocity += (this.gravity.y + body.gravity.y) * this.game.time.physicsElapsed;
velocity += (this.gravity.y + body.gravity.y) * this.delta;
}
if (acceleration)
{
velocity += acceleration * this.game.time.physicsElapsed;
velocity += acceleration * this.delta;
}
else if (drag && body.allowDrag)
{
drag *= this.game.time.physicsElapsed;
drag *= this.delta;
if (velocity - drag > 0)
{
@ -209,9 +337,11 @@ var World = new Class({
collideObjects: function (object1, object2, collideCallback, processCallback, callbackContext, overlapOnly)
{
var i;
if (!Array.isArray(object1) && Array.isArray(object2))
{
for (var i = 0; i < object2.length; i++)
for (i = 0; i < object2.length; i++)
{
if (!object2[i]) { continue; }
@ -220,7 +350,7 @@ var World = new Class({
}
else if (Array.isArray(object1) && !Array.isArray(object2))
{
for (var i = 0; i < object1.length; i++)
for (i = 0; i < object1.length; i++)
{
if (!object1[i]) { continue; }
@ -229,7 +359,7 @@ var World = new Class({
}
else if (Array.isArray(object1) && Array.isArray(object2))
{
for (var i = 0; i < object1.length; i++)
for (i = 0; i < object1.length; i++)
{
if (!object1[i]) { continue; }
@ -247,6 +377,660 @@ var World = new Class({
}
},
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;
}
});
module.exports = World;

View file

@ -38,6 +38,12 @@ module.exports = {
* @constant
* @type {number}
*/
BOTTOM_TOP: 4
BOTTOM_TOP: 4,
FACING_NONE: 5,
FACING_UP: 6,
FACING_DOWN: 7,
FACING_LEFT: 8,
FACING_RIGHT: 9
};

View file

@ -0,0 +1,17 @@
// Phaser.Physics.Arcade
// World updated to run off the Phaser main loop.
// Body extended to support additional setter functions.
module.exports = {
Body: require('./Body'),
// COLLIDES: require('./COLLIDES'),
// CollisionMap: require('./CollisionMap'),
// TYPE: require('./TYPE'),
World: require('./World'),
Factory: require('./Factory')
};

View file

@ -1,6 +1,7 @@
// Phaser.Physics
module.exports = {
Arcade: require('./arcade'),
Impact: require('./impact'),
PolyDecomp: require('./poly-decomp/')
};

View file

@ -3,7 +3,8 @@ var GetValue = require('../../utils/object/GetValue');
var Merge = require('../../utils/object/Merge');
var NOOP = require('../../utils/NOOP');
// Physics Systems
// Physics Systems (TODO: Remove from here)
var Arcade = require('../../physics/arcade/Arcade');
var Impact = require('../../physics/impact/Impact');
var PhysicsManager = new Class({
@ -46,6 +47,10 @@ var PhysicsManager = new Class({
switch (system)
{
case 'arcade':
this.system = new Arcade(this, config);
break;
case 'impact':
this.system = new Impact(this, config);
break;
@ -55,6 +60,11 @@ var PhysicsManager = new Class({
update: function (time, delta)
{
this.world.update(time, delta);
},
postUpdate: function ()
{
this.world.postUpdate();
}
});