Merged the impact physics runner

This commit is contained in:
Richard Davey 2017-06-22 00:47:35 +01:00
parent 001a0cb0d3
commit a1579c8fd4
13 changed files with 693 additions and 1 deletions

View file

@ -1,4 +1,4 @@
var CHECKSUM = {
build: 'b1e0db40-5608-11e7-9343-73f81cbe0a9c'
build: '93f8c470-56db-11e7-a08e-87dbe325345b'
};
module.exports = CHECKSUM;

View file

@ -0,0 +1,195 @@
// Phaser.Physics.Impact.Body
var Class = require('../../utils/Class');
var UpdateVelocity = require('./UpdateVelocity');
var UpdateMotion = require('./UpdateMotion');
var Trace = require('./Trace');
var COLLIDES = require('./COLLIDES');
var TYPE = require('./TYPE');
/**
* An Impact.js compatible physics body.
* This re-creates the properties you'd get on an Entity and the math needed to update them.
*
* @class
*/
var Body = new Class({
initialize:
function Body (world, x, y)
{
this.world = world;
this.enabled = true;
this.size = { x: 16, y: 16 };
this.offset = { x: 0, y: 0 };
this.pos = { x: x, y: y };
this.last = { x: 0, y: 0 };
this.vel = { x: 0, y: 0 };
this.accel = { x: 0, y: 0 };
this.friction = { x: 0, y: 0 };
this.maxVel = { x: 100, y: 100 };
this.gravityFactor = 1;
this.standing = false;
this.bounciness = 0;
this.minBounceVelocity = 40;
this.type = TYPE.NONE;
this.checkAgainst = TYPE.NONE;
this.collides = COLLIDES.NEVER;
// min 44 deg, max 136 deg
this.slopeStanding = { min: 0.767944870877505, max: 2.3736477827122884 };
},
update: function (delta)
{
this.last.x = this.pos.x;
this.last.y = this.pos.y;
this.vel.y += this.world.gravity * delta * this.gravityFactor;
UpdateVelocity(this, delta);
var mx = this.vel.x * delta;
var my = this.vel.y * delta;
var res = Trace(this.pos.x, this.pos.y, mx, my, this.size.x, this.size.y);
UpdateMotion(this, res);
},
skipHash: function ()
{
return (!this.enabled || (this.type === 0 && this.checkAgainst === 0 && this.collides === 0));
},
touches: function (other)
{
return !(
this.pos.x >= other.pos.x + other.size.x ||
this.pos.x + this.size.x <= other.pos.x ||
this.pos.y >= other.pos.y + other.size.y ||
this.pos.y + this.size.y <= other.pos.y
);
},
setVelocityX: function (x)
{
this.vel.x = x;
return this;
},
setVelocityY: function (y)
{
this.vel.y = y;
return this;
},
setVelocity: function (x, y)
{
this.vel.x = x;
this.vel.y = y;
return this;
},
setMaxVelocity: function (x, y)
{
if (y === undefined) { y = x; }
this.maxVel.x = x;
this.maxVel.y = y;
return this;
},
setTypeNone: function ()
{
this.type = TYPE.NONE;
return this;
},
setTypeA: function ()
{
this.type = TYPE.A;
return this;
},
setTypeB: function ()
{
this.type = TYPE.B;
return this;
},
setCheckAgainstNone: function ()
{
this.checkAgainst = TYPE.NONE;
return this;
},
setCheckAgainstA: function ()
{
this.checkAgainst = TYPE.A;
return this;
},
setCheckAgainstB: function ()
{
this.checkAgainst = TYPE.B;
return this;
},
setCollidesNever: function ()
{
this.collides = COLLIDES.NEVER;
return this;
},
setLite: function ()
{
this.collides = COLLIDES.LITE;
return this;
},
setPassive: function ()
{
this.collides = COLLIDES.PASSIVE;
return this;
},
setActive: function ()
{
this.collides = COLLIDES.ACTIVE;
return this;
},
setFixed: function ()
{
this.collides = COLLIDES.FIXED;
return this;
},
check: function( other ) {},
collideWith: function( other, axis ) {}
});
module.exports = Body;

View file

@ -0,0 +1,17 @@
// Collision Types - Determine if and how entities collide with each other
// In ACTIVE vs. LITE or FIXED vs. ANY collisions, only the "weak" entity moves,
// while the other one stays fixed. In ACTIVE vs. ACTIVE and ACTIVE vs. PASSIVE
// collisions, both entities are moved. LITE or PASSIVE entities don't collide
// with other LITE or PASSIVE entities at all. The behaiviour for FIXED vs.
// FIXED collisions is undefined.
module.exports = {
NEVER: 0,
LITE: 1,
PASSIVE: 2,
ACTIVE: 4,
FIXED: 8
};

View file

@ -0,0 +1,35 @@
var Trace = require('./Trace');
var SeperateX = function (world, left, right, weak)
{
var nudge = left.pos.x + left.size.x - right.pos.x;
// We have a weak entity, so just move this one
if (weak)
{
var strong = (left === weak) ? right : left;
weak.vel.x = -weak.vel.x * weak.bounciness + strong.vel.x;
var resWeak = Trace(weak.pos.x, weak.pos.y, weak === left ? -nudge : nudge, 0, weak.size.x, weak.size.y);
weak.pos.x = resWeak.pos.x;
}
else
{
var v2 = (left.vel.x - right.vel.x) / 2;
left.vel.x = -v2;
right.vel.x = v2;
var resLeft = Trace(left.pos.x, left.pos.y, -nudge / 2, 0, left.size.x, left.size.y);
left.pos.x = Math.floor(resLeft.pos.x);
var resRight = Trace(right.pos.x, right.pos.y, nudge / 2, 0, right.size.x, right.size.y);
right.pos.x = Math.ceil(resRight.pos.x);
}
};
module.exports = SeperateX;

View file

@ -0,0 +1,62 @@
var Trace = require('./Trace');
var SeperateY = function (world, top, bottom, weak)
{
var nudge = (top.pos.y + top.size.y - bottom.pos.y);
if (weak)
{
var strong = (top === weak) ? bottom : top;
weak.vel.y = -weak.vel.y * weak.bounciness + strong.vel.y;
// Riding on a platform?
var nudgeX = 0;
if (weak === top && Math.abs(weak.vel.y - strong.vel.y) < weak.minBounceVelocity)
{
weak.standing = true;
nudgeX = strong.vel.x * world.delta;
}
var resWeak = Trace(weak.pos.x, weak.pos.y, nudgeX, weak == top ? -nudge : nudge, weak.size.x, weak.size.y);
weak.pos.y = resWeak.pos.y;
weak.pos.x = resWeak.pos.x;
}
else if (world.gravity && (bottom.standing || top.vel.y > 0))
{
var resTop = Trace(top.pos.x, top.pos.y, 0, -(top.pos.y + top.size.y - bottom.pos.y), top.size.x, top.size.y);
top.pos.y = resTop.pos.y;
if (top.bounciness > 0 && top.vel.y > top.minBounceVelocity)
{
top.vel.y *= -top.bounciness;
}
else
{
top.standing = true;
top.vel.y = 0;
}
}
else
{
var v2 = (top.vel.y - bottom.vel.y) / 2;
top.vel.y = -v2;
bottom.vel.y = v2;
var nudgeX = bottom.vel.x * world.delta;
var resTop = Trace(top.pos.x, top.pos.y, nudgeX, -nudge/2, top.size.x, top.size.y);
top.pos.y = resTop.pos.y;
var resBottom = Trace(bottom.pos.x, bottom.pos.y, 0, nudge/2, bottom.size.x, bottom.size.y);
bottom.pos.y = resBottom.pos.y;
}
};
module.exports = SeperateY;

View file

@ -0,0 +1,51 @@
var SeperateX = require('./SeperateX');
var SeperateY = require('./SeperateY');
var COLLIDES = require('./COLLIDES');
var TYPE = require('./TYPE');
// Impact Physics Solver
var Solver = function (world, bodyA, bodyB)
{
var weak = null;
if (bodyA.collides === COLLIDES.LITE || bodyB.collides === COLLIDES.FIXED)
{
weak = bodyA;
}
else if (bodyB.collides === COLLIDES.LITE || bodyA.collides === COLLIDES.FIXED)
{
weak = bodyB;
}
if (bodyA.last.x + bodyA.size.x > bodyB.last.x && bodyA.last.x < bodyB.last.x + bodyB.size.x)
{
if (bodyA.last.y < bodyB.last.y)
{
SeperateY(world, bodyA, bodyB, weak);
}
else
{
SeperateY(world, bodyB, bodyA, weak);
}
bodyA.collideWith(bodyB, 'y');
bodyB.collideWith(bodyA, 'y');
}
else if (bodyA.last.y + bodyA.size.y > bodyB.last.y && bodyA.last.y < bodyB.last.y + bodyB.size.y)
{
if (bodyA.last.x < bodyB.last.x)
{
SeperateX(world, bodyA, bodyB, weak);
}
else
{
SeperateX(world, bodyB, bodyA, weak);
}
bodyA.collideWith(bodyB, 'x');
bodyB.collideWith(bodyA, 'x');
}
};
module.exports = Solver;

View file

@ -0,0 +1,16 @@
// Collision Types - Determine if and how entities collide with each other
// In ACTIVE vs. LITE or FIXED vs. ANY collisions, only the "weak" entity moves,
// while the other one stays fixed. In ACTIVE vs. ACTIVE and ACTIVE vs. PASSIVE
// collisions, both entities are moved. LITE or PASSIVE entities don't collide
// with other LITE or PASSIVE entities at all. The behaiviour for FIXED vs.
// FIXED collisions is undefined.
module.exports = {
NONE: 0,
A: 1,
B: 2,
BOTH: 3
};

View file

@ -0,0 +1,10 @@
var Trace = function (x, y, vx, vy)
{
return {
collision: { x: false, y: false, slope: false },
pos: { x: x + vx, y: y + vy },
tile: { x: 0, y: 0 }
};
};
module.exports = Trace;

View file

@ -0,0 +1,79 @@
// Set up the trace-result
// var res = {
// collision: {x: false, y: false, slope: false},
// pos: {x: x, y: y},
// tile: {x: 0, y: 0}
// };
var UpdateMotion = function (body, res)
{
body.standing = false;
// Y
if (res.collision.y)
{
if (body.bounciness > 0 && Math.abs(body.vel.y) > body.minBounceVelocity)
{
body.vel.y *= -body.bounciness;
}
else
{
if (body.vel.y > 0)
{
body.standing = true;
}
body.vel.y = 0;
}
}
// X
if (res.collision.x)
{
if (body.bounciness > 0 && Math.abs(body.vel.x) > body.minBounceVelocity)
{
body.vel.x *= -body.bounciness;
}
else
{
body.vel.x = 0;
}
}
// SLOPE
if (res.collision.slope)
{
var s = res.collision.slope;
if (body.bounciness > 0)
{
var proj = body.vel.x * s.nx + body.vel.y * s.ny;
body.vel.x -= s.nx * proj * 2;
body.vel.y -= s.ny * proj * 2;
body.vel.x *= body.bounciness;
body.vel.y *= body.bounciness;
}
else
{
var lengthSquared = s.x * s.x + s.y * s.y;
var dot = (body.vel.x * s.x + body.vel.y * s.y) / lengthSquared;
body.vel.x = s.x * dot;
body.vel.y = s.y * dot;
var angle = Math.atan2(s.x, s.y);
if (angle > body.slopeStanding.min && angle < body.slopeStanding.max)
{
body.standing = true;
}
}
}
body.pos.x = res.pos.x;
body.pos.y = res.pos.y;
};
module.exports = UpdateMotion;

View file

@ -0,0 +1,73 @@
var Clamp = require('../../math/Clamp');
var UpdateVelocity = function (body, delta)
{
var vel = body.vel.x;
var accel = body.accel.x;
var friction = body.friction.x;
var max = body.maxVel.x;
var frictionDelta;
// X
if (accel)
{
body.vel.x = Clamp(vel + accel * delta, -max, max);
}
else if (friction)
{
frictionDelta = friction * delta;
if (vel - frictionDelta > 0)
{
body.vel.x = vel - frictionDelta;
}
else if (vel + frictionDelta < 0)
{
body.vel.x = vel + frictionDelta;
}
else
{
body.vel.x = 0;
}
}
else
{
body.vel.x = Clamp(vel, -max, max);
}
vel = body.vel.y;
accel = body.accel.y;
friction = body.friction.y;
max = body.maxVel.y;
// Y
if (accel)
{
body.vel.y = Clamp(vel + accel * delta, -max, max);
}
else if (friction)
{
frictionDelta = friction * delta;
if (vel - frictionDelta > 0)
{
body.vel.y = vel - frictionDelta;
}
else if (vel + frictionDelta < 0)
{
body.vel.y = vel + frictionDelta;
}
else
{
body.vel.y = 0;
}
}
else
{
body.vel.y = Clamp(vel, -max, max);
}
return body;
};
module.exports = UpdateVelocity;

View file

@ -0,0 +1,143 @@
// Phaser.Physics.Impact.World
var Class = require('../../utils/Class');
var Set = require('../../structs/Set');
var Body = require('./Body');
var Solver = require('./Solver');
var COLLIDES = require('./COLLIDES');
var TYPE = require('./TYPE');
var World = new Class({
initialize:
function World ()
{
this.bodies = new Set();
this.gravity = 0;
this.delta = 0;
// Spatial hash cell dimensions
this.cellSize = 64;
},
create: function (x, y)
{
var body = new Body(this, x, y);
this.bodies.set(body);
return body;
},
update: function (time, delta)
{
if (this.bodies.size === 0)
{
return;
}
// Impact uses a divided delta value
delta /= 1000;
this.delta = delta;
// Update all bodies
this.bodies.iterate(function (body) {
if (body.enabled)
{
body.update(delta);
}
});
// Run collision against them all
var hash = {};
var size = this.cellSize;
var bodies = this.bodies.entries;
for (var i = 0; i < bodies.length; i++)
{
var body = bodies[i];
if (body.skipHash())
{
continue;
}
else
{
this.checkHash(body, hash, size);
}
}
},
// Check the body against the spatial hash
checkHash: function (body, hash, size)
{
var checked = {};
var xmin = Math.floor(body.pos.x / size);
var ymin = Math.floor(body.pos.y / size);
var xmax = Math.floor((body.pos.x + body.size.x) / size) + 1;
var ymax = Math.floor((body.pos.y + body.size.y) / size) + 1;
for (var x = xmin; x < xmax; x++)
{
for (var y = ymin; y < ymax; y++)
{
if (!hash[x])
{
hash[x] = {};
hash[x][y] = [body];
}
else if (!hash[x][y])
{
hash[x][y] = [body];
}
else
{
var cell = hash[x][y];
for (var c = 0; c < cell.length; c++)
{
if (body.touches(cell[c]) && !checked[cell[c].id])
{
checked[cell[c].id] = true;
this.checkBodies(body, cell[c]);
}
}
cell.push(body);
}
}
}
},
checkBodies: function (bodyA, bodyB)
{
// bitwise checks
if (bodyA.checkAgainst & bodyB.type)
{
bodyA.check(bodyB);
}
if (bodyB.checkAgainst & bodyA.type)
{
bodyB.check(bodyA);
}
if (bodyA.collides && bodyB.collides && bodyA.collides + bodyB.collides > COLLIDES.ACTIVE)
{
Solver(this, bodyA, bodyB);
}
}
});
module.exports = World;

View file

@ -0,0 +1,10 @@
// Phaser.Physics.Impact
module.exports = {
Body: require('./Body'),
World: require('./World'),
COLLIDES: require('./COLLIDES'),
TYPE: require('./TYPE'),
};

View file

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