phaser/v3/src/physics/arcade/Body.js
2017-11-09 15:32:45 +00:00

804 lines
17 KiB
JavaScript

// Phaser.Physics.Arcade.Body
var CircleContains = require('../../geom/circle/Contains');
var Class = require('../../utils/Class');
var CONST = require('./const');
var PhysicsEvent = require('./events');
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;
// If true this Body will dispatch events
this.onWorldBounds = false;
this.onCollide = false;
this.onOverlap = false;
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.syncBounds = false;
this.isMoving = false;
this.stopVelocityOnCollide = true;
// read-only
this.physicsType = CONST.DYNAMIC_BODY;
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);
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.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)
{
if (this.checkWorldBounds() && this.onWorldBounds)
{
this.world.events.dispatch(new PhysicsEvent.WORLD_BOUNDS(this));
}
}
}
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.displayOriginX + (sprite.scaleX * this.offset.x);
this.position.y = y - sprite.displayOriginY + (sprite.scaleY * this.offset.y);
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);
},
setCollideWorldBounds: function (value)
{
this.collideWorldBounds = value;
return this;
},
setVelocity: function (x, y)
{
this.velocity.set(x, y);
return this;
},
setVelocityX: function (value)
{
this.velocity.x = value;
return this;
},
setVelocityY: function (value)
{
this.velocity.y = value;
return this;
},
setBounce: function (x, y)
{
this.bounce.set(x, y);
return this;
},
setBounceX: function (value)
{
this.bounce.x = value;
return this;
},
setBounceY: function (value)
{
this.bounce.y = value;
return this;
},
setAcceleration: function (x, y)
{
this.acceleration.set(x, y);
return this;
},
setAccelerationX: function (value)
{
this.acceleration.x = value;
return this;
},
setAccelerationY: function (value)
{
this.acceleration.y = value;
return this;
},
setDrag: function (x, y)
{
this.drag.set(x, y);
return this;
},
setDragX: function (value)
{
this.drag.x = value;
return this;
},
setDragY: function (value)
{
this.drag.y = value;
return this;
},
setGravity: function (x, y)
{
this.gravity.set(x, y);
return this;
},
setGravityX: function (value)
{
this.gravity.x = value;
return this;
},
setGravityY: function (value)
{
this.gravity.y = value;
return this;
},
setFriction: function (x, y)
{
this.friction.set(x, y);
return this;
},
setFrictionX: function (value)
{
this.friction.x = value;
return this;
},
setFrictionY: function (value)
{
this.friction.y = value;
return this;
},
setAngularVelocity: function (value)
{
this.angularVelocity = value;
return this;
},
setAngularAcceleration: function (value)
{
this.angularAcceleration = value;
return this;
},
setAngularDrag: function (value)
{
this.angularDrag = value;
return this;
},
setMass: function (value)
{
this.mass = value;
return this;
},
setImmovable: function (value)
{
this.immovable = value;
return this;
},
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;