Added Collision Map support

This commit is contained in:
Richard Davey 2017-06-22 02:40:10 +01:00
parent a1579c8fd4
commit de336e6d35
9 changed files with 349 additions and 33 deletions

View file

@ -1,4 +1,4 @@
var CHECKSUM = {
build: '93f8c470-56db-11e7-a08e-87dbe325345b'
build: 'a534dad0-56ea-11e7-93ca-31a05bf83d09'
};
module.exports = CHECKSUM;

View file

@ -3,7 +3,6 @@
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');
@ -18,14 +17,16 @@ var Body = new Class({
initialize:
function Body (world, x, y)
function Body (world, x, y, sx, sy)
{
if (sx === undefined) { sx = 16; }
if (sy === undefined) { sy = 16; }
this.world = world;
this.enabled = true;
this.size = { x: 16, y: 16 };
this.offset = { x: 0, y: 0 };
this.size = { x: sx, y: sy };
this.pos = { x: x, y: y };
this.last = { x: 0, y: 0 };
this.vel = { x: 0, y: 0 };
@ -57,7 +58,7 @@ var Body = new Class({
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);
var res = this.world.collisionMap.trace(this.pos.x, this.pos.y, mx, my, this.size.x, this.size.y);
UpdateMotion(this, res);
},
@ -77,6 +78,13 @@ var Body = new Class({
);
},
setBounce: function (value)
{
this.bounciness = value;
return this;
},
setVelocityX: function (x)
{
this.vel.x = x;

View file

@ -0,0 +1,242 @@
// Phaser.Physics.Impact.CollisionMap
var Class = require('../../utils/Class');
var DefaultDefs = require('./DefaultDefs');
var CollisionMap = new Class({
initialize:
function CollisionMap (tilesize, data)
{
this.tilesize = tilesize;
this.data = data;
this.width = (Array.isArray(data)) ? data[0].length : 0;
this.height = (Array.isArray(data)) ? data.length : 0;
this.tiledef = DefaultDefs;
},
trace: function (x, y, vx, vy, objectWidth, objectHeight)
{
// Set up the trace-result
var res = {
collision: { x: false, y: false, slope: false },
pos: { x: x, y: y },
tile: { x: 0, y: 0 }
};
if (!this.data)
{
res.pos.x += vx;
res.pos.y += vy;
return res;
}
var steps = Math.ceil(Math.max(Math.abs(vx), Math.abs(vy)) / this.tilesize);
if (steps > 1)
{
var sx = vx / steps;
var sy = vy / steps;
for (var i = 0; i < steps && (sx || sy); i++)
{
this.step(res, x, y, sx, sy, objectWidth, objectHeight, vx, vy, i);
x = res.pos.x;
y = res.pos.y;
if (res.collision.x)
{
sx = 0;
vx = 0;
}
if (res.collision.y)
{
sy = 0;
vy = 0;
}
if (res.collision.slope)
{
break;
}
}
}
else
{
this.step(res, x, y, vx, vy, objectWidth, objectHeight, vx, vy, 0);
}
return res;
},
step: function (res, x, y, vx, vy, width, height, rvx, rvy, step)
{
res.pos.x += vx;
res.pos.y += vy;
var t = 0;
var tilesize = this.tilesize;
var width = this.width;
var height = this.height;
// Horizontal
if (vx)
{
var pxOffsetX = (vx > 0) ? width : 0;
var tileOffsetX = (vx < 0) ? tilesize : 0;
var firstTileY = Math.max(Math.floor(y / tilesize), 0);
var lastTileY = Math.min(Math.ceil((y + height) / tilesize), height);
var tileX = Math.floor((res.pos.x + pxOffsetX) / tilesize);
var prevTileX = Math.floor((x + pxOffsetX) / tilesize);
if (step > 0 || tileX == prevTileX || prevTileX < 0 || prevTileX >= width)
{
prevTileX = -1;
}
if (tileX >= 0 && tileX < width)
{
for (var tileY = firstTileY; tileY < lastTileY; tileY++)
{
if (prevTileX != -1)
{
t = this.data[tileY][prevTileX];
if (t > 1 && this.checkDef(res, t, x, y, rvx, rvy, width, height, prevTileX, tileY))
{
break;
}
}
t = this.data[tileY][tileX];
if (t === 1 || (t > 1 && this.checkDef(res, t, x, y, rvx, rvy, width, height, tileX, tileY)))
{
if (t > 1 && res.collision.slope)
{
break;
}
res.collision.x = true;
res.tile.x = t;
res.pos.x = tileX * tilesize - pxOffsetX + tileOffsetX;
break;
}
}
}
}
// Vertical
if (vy)
{
var pxOffsetY = (vy > 0) ? height : 0;
var tileOffsetY = (vy < 0) ? tilesize : 0;
var firstTileX = Math.max(Math.floor(res.pos.x / tilesize), 0);
var lastTileX = Math.min(Math.ceil((res.pos.x + width) / tilesize), width);
var tileY = Math.floor((res.pos.y + pxOffsetY) / tilesize);
var prevTileY = Math.floor((y + pxOffsetY) / tilesize);
if (step > 0 || tileY == prevTileY || prevTileY < 0 || prevTileY >= height)
{
prevTileY = -1;
}
if (tileY >= 0 && tileY < height)
{
for (var tileX = firstTileX; tileX < lastTileX; tileX++)
{
if (prevTileY != -1)
{
t = this.data[prevTileY][tileX];
if (t > 1 && this.checkDef(res, t, x, y, rvx, rvy, width, height, tileX, prevTileY))
{
break;
}
}
t = this.data[tileY][tileX];
if (t == 1 || (t > 1 && this._checkTileDef(res, t, x, y, rvx, rvy, width, height, tileX, tileY)))
{
if (t > 1 && res.collision.slope)
{
break;
}
res.collision.y = true;
res.tile.y = t;
res.pos.y = tileY * tilesize - pxOffsetY + tileOffsetY;
break;
}
}
}
}
},
checkDef: function (res, t, x, y, vx, vy, width, height, tileX, tileY)
{
var def = this.tiledef[t];
if (!def)
{
return false;
}
var tilesize = this.tilesize;
var lx = (tileX + def[0]) * tilesize;
var ly = (tileY + def[1]) * tilesize;
var lvx = (def[2] - def[0]) * tilesize;
var lvy = (def[3] - def[1]) * tilesize;
var solid = def[4];
var tx = x + vx + (lvy < 0 ? width : 0) - lx;
var ty = y + vy + (lvx > 0 ? height : 0) - ly;
if (lvx * ty - lvy * tx > 0)
{
if (vx * -lvy + vy * lvx < 0)
{
return solid;
}
var length = Math.sqrt(lvx * lvx + lvy * lvy);
var nx = lvy / length;
var ny = -lvx / length;
var proj = tx * nx + ty * ny;
var px = nx * proj;
var py = ny * proj;
if (px * px + py * py >= vx * vx + vy * vy)
{
return true;
}
res.pos.x = x + vx - px;
res.pos.y = y + vy - py;
res.collision.slope = { x: lvx, y: lvy, nx: nx, ny: ny };
return true;
}
return false;
}
});
module.exports = CollisionMap;

View file

@ -0,0 +1,59 @@
var H = 0.5;
var N = 1 / 3;
var M = 2 / 3;
// Tile ID to Slope defs.
// First 4 elements = line data, final = solid or non-solid behind the line
module.exports = {
2: [ 0, 1, 1, 0, true ],
3: [ 0, 1, 1, H, true ],
4: [ 0, H, 1, 0, true ],
5: [ 0, 1, 1, M, true ],
6: [ 0, M, 1, N, true ],
7: [ 0, N, 1, 0, true ],
8: [ H, 1, 0, 0, true ],
9: [ 1, 0, H, 1, true ],
10: [ H, 1, 1, 0, true ],
11: [ 0, 0, H, 1, true ],
12: [ 0, 0, 1, 0, false ],
13: [ 1, 1, 0, 0, true ],
14: [ 1, H, 0, 0, true ],
15: [ 1, 1, 0, H, true ],
16: [ 1, N, 0, 0, true ],
17: [ 1, M, 0, N, true ],
18: [ 1, 1, 0, M, true ],
19: [ 1, 1, H, 0, true ],
20: [ H, 0, 0, 1, true ],
21: [ 0, 1, H, 0, true ],
22: [ H, 0, 1, 1, true ],
23: [ 1, 1, 0, 1, false ],
24: [ 0, 0, 1, 1, true ],
25: [ 0, 0, 1, H, true ],
26: [ 0, H, 1, 1, true ],
27: [ 0, 0, 1, N, true ],
28: [ 0, N, 1, M, true ],
29: [ 0, M, 1, 1, true ],
30: [ N, 1, 0, 0, true ],
31: [ 1, 0, M, 1, true ],
32: [ M, 1, 1, 0, true ],
33: [ 0, 0, N, 1, true ],
34: [ 1, 0, 1, 1, false ],
35: [ 1, 0, 0, 1, true ],
36: [ 1, H, 0, 1, true ],
37: [ 1, 0, 0, H, true ],
38: [ 1, M, 0, 1, true ],
39: [ 1, N, 0, M, true ],
40: [ 1, 0, 0, N, true ],
41: [ M, 1, N, 0, true ],
42: [ M, 0, N, 1, true ],
43: [ N, 1, M, 0, true ],
44: [ N, 0, M, 1, true ],
45: [ 0, 1, 0, 0, false ],
52: [ 1, 1, M, 0, true ],
53: [ N, 0, 0, 1, true ],
54: [ 0, 1, N, 0, true ],
55: [ M, 0, 1, 1, true ]
};

View file

@ -1,4 +1,3 @@
var Trace = require('./Trace');
var SeperateX = function (world, left, right, weak)
{
@ -11,7 +10,7 @@ var SeperateX = function (world, left, right, weak)
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);
var resWeak = world.collisionMap.trace(weak.pos.x, weak.pos.y, weak === left ? -nudge : nudge, 0, weak.size.x, weak.size.y);
weak.pos.x = resWeak.pos.x;
}
@ -22,11 +21,11 @@ var SeperateX = function (world, left, right, weak)
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);
var resLeft = world.collisionMap.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);
var resRight = world.collisionMap.trace(right.pos.x, right.pos.y, nudge / 2, 0, right.size.x, right.size.y);
right.pos.x = Math.ceil(resRight.pos.x);
}

View file

@ -1,4 +1,3 @@
var Trace = require('./Trace');
var SeperateY = function (world, top, bottom, weak)
{
@ -19,14 +18,14 @@ var SeperateY = function (world, top, bottom, weak)
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);
var resWeak = world.collisionMap.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);
var resTop = world.collisionMap.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;
@ -49,11 +48,11 @@ var SeperateY = function (world, top, bottom, weak)
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);
var resTop = world.collisionMap.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);
var resBottom = world.collisionMap.trace(bottom.pos.x, bottom.pos.y, 0, nudge/2, bottom.size.x, bottom.size.y);
bottom.pos.y = resBottom.pos.y;
}

View file

@ -1,10 +0,0 @@
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

@ -4,6 +4,7 @@ var Class = require('../../utils/Class');
var Set = require('../../structs/Set');
var Body = require('./Body');
var Solver = require('./Solver');
var CollisionMap = require('./CollisionMap');
var COLLIDES = require('./COLLIDES');
var TYPE = require('./TYPE');
@ -11,21 +12,26 @@ var World = new Class({
initialize:
function World ()
function World (gravity, cellSize)
{
if (gravity === undefined) { gravity = 0; }
if (cellSize === undefined) { cellSize = 64; }
this.bodies = new Set();
this.gravity = 0;
this.delta = 0;
this.gravity = gravity;
// Spatial hash cell dimensions
this.cellSize = 64;
this.cellSize = cellSize;
this.collisionMap = new CollisionMap();
this.delta = 0;
},
create: function (x, y)
create: function (x, y, sizeX, sizeY)
{
var body = new Body(this, x, y);
var body = new Body(this, x, y, sizeX, sizeY);
this.bodies.set(body);

View file

@ -1,10 +1,23 @@
// Phaser.Physics.Impact
// An Impact.js compatible physics world, body and solver, for those who are used
// to the Impact way of defining and controlling physics bodies. Also works with
// the new Loader support for Weltmeister map data.
//
// World updated to run off the Phaser main loop.
// Body extended to support additional setter functions.
//
// To create the map data you'll need Weltmeister, which comes with Impact
// and can be purchased from http://impactjs.com
//
// My thanks to Dominic Szablewski for his permission to support Impact in Phaser.
module.exports = {
Body: require('./Body'),
World: require('./World'),
COLLIDES: require('./COLLIDES'),
CollisionMap: require('./CollisionMap'),
TYPE: require('./TYPE'),
World: require('./World')
};