mirror of
https://github.com/photonstorm/phaser
synced 2024-12-22 11:03:23 +00:00
597 lines
25 KiB
TypeScript
597 lines
25 KiB
TypeScript
/// <reference path="../../_definitions.ts" />
|
|
|
|
/**
|
|
* Phaser - Physics - Projection
|
|
*/
|
|
|
|
module Phaser.Physics.Projection {
|
|
|
|
export class Circle22Deg {
|
|
|
|
public static CollideS(x, y, oH, oV, obj: Phaser.Physics.Circle, t: Phaser.Physics.TileMapCell) {
|
|
|
|
//if the object is in a cell pointed at by signy, no collision will ever occur
|
|
//otherwise,
|
|
//
|
|
//if we're colliding diagonally:
|
|
// -collide vs. the appropriate vertex
|
|
//if obj is in this tile: collide vs slope or vertex
|
|
//if obj is horiz neighb in direction of slope: collide vs. slope or vertex
|
|
//if obj is horiz neighb against the slope:
|
|
// if(distance in y from circle to 90deg corner of tile < 1/2 tileheight, collide vs. face)
|
|
// else(collide vs. corner of slope) (vert collision with a non-grid-aligned vert)
|
|
//if obj is vert neighb against direction of slope: collide vs. face
|
|
|
|
var signx = t.signx;
|
|
var signy = t.signy;
|
|
|
|
if (0 < (signy * oV))
|
|
{
|
|
//object will never collide vs tile, it can't reach that far
|
|
return Phaser.Physics.Circle.COL_NONE;
|
|
}
|
|
else if (oH == 0)
|
|
{
|
|
if (oV == 0)
|
|
{
|
|
//colliding with current tile
|
|
//we could only be colliding vs the slope OR a vertex
|
|
//look at the vector form the closest vert to the circle to decide
|
|
|
|
var sx = t.sx;
|
|
var sy = t.sy;
|
|
|
|
var r = obj.radius;
|
|
var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost
|
|
var oy = obj.pos.y - t.pos.y;//point on the circle, relative to the tile corner
|
|
|
|
//if the component of (ox,oy) parallel to the normal's righthand normal
|
|
//has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy)
|
|
//then we project by the vertex, otherwise by the normal or axially.
|
|
//note that this is simply a VERY tricky/weird method of determining
|
|
//if the circle is in side the slope/face's voronio region, or that of the vertex.
|
|
|
|
var perp = (ox * -sy) + (oy * sx);
|
|
|
|
if (0 < (perp * signx * signy))
|
|
{
|
|
//collide vs. vertex
|
|
var len = Math.sqrt(ox * ox + oy * oy);
|
|
var pen = r - len;
|
|
|
|
if (0 < pen)
|
|
{
|
|
//note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0
|
|
ox /= len;
|
|
oy /= len;
|
|
|
|
obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//collide vs. slope or vs axis
|
|
ox -= r * sx;//this gives us the vector from
|
|
oy -= r * sy;//a point on the slope to the innermost point on the circle
|
|
|
|
//if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope
|
|
//and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy)
|
|
var dp = (ox * sx) + (oy * sy);
|
|
|
|
if (dp < 0)
|
|
{
|
|
//collision; project delta onto slope and use this to displace the object
|
|
sx *= -dp;//(sx,sy) is now the projection vector
|
|
sy *= -dp;
|
|
|
|
var lenN = Math.sqrt(sx * sx + sy * sy);
|
|
var lenP;
|
|
|
|
//find the smallest axial projection vector
|
|
if (x < y)
|
|
{
|
|
//penetration in x is smaller
|
|
lenP = x;
|
|
y = 0;
|
|
//get sign for projection along x-axis
|
|
if ((obj.pos.x - t.pos.x) < 0)
|
|
{
|
|
x *= -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//penetration in y is smaller
|
|
lenP = y;
|
|
x = 0;
|
|
//get sign for projection along y-axis
|
|
if ((obj.pos.y - t.pos.y) < 0)
|
|
{
|
|
y *= -1;
|
|
}
|
|
}
|
|
|
|
if (lenP < lenN)
|
|
{
|
|
obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t);
|
|
|
|
return Phaser.Physics.Circle.COL_AXIS;
|
|
}
|
|
else
|
|
{
|
|
obj.reportCollisionVsWorld(sx, sy, t.sx, t.sy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//colliding vertically; we can assume that (signy*oV) < 0
|
|
//due to the first conditional far above
|
|
|
|
obj.reportCollisionVsWorld(0, y * oV, 0, oV, t);
|
|
|
|
return Phaser.Physics.Circle.COL_AXIS;
|
|
}
|
|
}
|
|
else if (oV == 0)
|
|
{
|
|
//colliding horizontally
|
|
if ((signx * oH) < 0)
|
|
{
|
|
//colliding with face/edge OR with corner of wedge, depending on our position vertically
|
|
|
|
//collide vs. vertex
|
|
//get diag vertex position
|
|
var vx = t.pos.x - (signx * t.xw);
|
|
var vy = t.pos.y;
|
|
|
|
var dx = obj.pos.x - vx;//calc vert->circle vector
|
|
var dy = obj.pos.y - vy;
|
|
|
|
if ((dy * signy) < 0)
|
|
{
|
|
//colliding vs face
|
|
obj.reportCollisionVsWorld(x * oH, 0, oH, 0, t);
|
|
|
|
return Phaser.Physics.Circle.COL_AXIS;
|
|
}
|
|
else
|
|
{
|
|
//colliding vs. vertex
|
|
|
|
var len = Math.sqrt(dx * dx + dy * dy);
|
|
var pen = obj.radius - len;
|
|
|
|
if (0 < pen)
|
|
{
|
|
//vertex is in the circle; project outward
|
|
if (len == 0)
|
|
{
|
|
//project out by 45deg
|
|
dx = oH / Math.SQRT2;
|
|
dy = oV / Math.SQRT2;
|
|
}
|
|
else
|
|
{
|
|
dx /= len;
|
|
dy /= len;
|
|
}
|
|
|
|
obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//we could only be colliding vs the slope OR a vertex
|
|
//look at the vector form the closest vert to the circle to decide
|
|
|
|
var sx = t.sx;
|
|
var sy = t.sy;
|
|
|
|
var ox = obj.pos.x - (t.pos.x + (oH * t.xw));//this gives is the coordinates of the innermost
|
|
var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//point on the circle, relative to the closest tile vert
|
|
|
|
//if the component of (ox,oy) parallel to the normal's righthand normal
|
|
//has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy)
|
|
//then we project by the normal, otherwise by the vertex.
|
|
//(NOTE: this is the opposite logic of the vertical case;
|
|
// for vertical, if the perp prod and the slope's slope agree, it's outside.
|
|
// for horizontal, if the perp prod and the slope's slope agree, circle is inside.
|
|
// ..but this is only a property of flahs' coord system (i.e the rules might swap
|
|
// in righthanded systems))
|
|
//note that this is simply a VERY tricky/weird method of determining
|
|
//if the circle is in side the slope/face's voronio region, or that of the vertex.
|
|
var perp = (ox * -sy) + (oy * sx);
|
|
|
|
if ((perp * signx * signy) < 0)
|
|
{
|
|
//collide vs. vertex
|
|
var len = Math.sqrt(ox * ox + oy * oy);
|
|
var pen = obj.radius - len;
|
|
|
|
if (0 < pen)
|
|
{
|
|
//note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0
|
|
ox /= len;
|
|
oy /= len;
|
|
|
|
obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//collide vs. slope
|
|
|
|
//if the component of (ox,oy) parallel to the normal is less than the circle radius, we're
|
|
//penetrating the slope. note that this method of penetration calculation doesn't hold
|
|
//in general (i.e it won't work if the circle is in the slope), but works in this case
|
|
//because we know the circle is in a neighboring cell
|
|
var dp = (ox * sx) + (oy * sy);
|
|
var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case..
|
|
|
|
if (0 < pen)
|
|
{
|
|
//collision; circle out along normal by penetration amount
|
|
obj.reportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//colliding diagonally; due to the first conditional above,
|
|
//obj is vertically offset against slope, and offset in either direction horizontally
|
|
|
|
//collide vs. vertex
|
|
//get diag vertex position
|
|
var vx = t.pos.x + (oH * t.xw);
|
|
var vy = t.pos.y + (oV * t.yw);
|
|
|
|
var dx = obj.pos.x - vx;//calc vert->circle vector
|
|
var dy = obj.pos.y - vy;
|
|
|
|
var len = Math.sqrt(dx * dx + dy * dy);
|
|
var pen = obj.radius - len;
|
|
|
|
if (0 < pen)
|
|
{
|
|
//vertex is in the circle; project outward
|
|
if (len == 0)
|
|
{
|
|
//project out by 45deg
|
|
dx = oH / Math.SQRT2;
|
|
dy = oV / Math.SQRT2;
|
|
}
|
|
else
|
|
{
|
|
dx /= len;
|
|
dy /= len;
|
|
}
|
|
|
|
obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
|
|
return Phaser.Physics.Circle.COL_NONE;
|
|
|
|
}
|
|
|
|
public static CollideB(x, y, oH, oV, obj: Phaser.Physics.Circle, t: Phaser.Physics.TileMapCell) {
|
|
|
|
//if we're colliding diagonally:
|
|
// -if we're in the cell pointed at by the normal, collide vs slope, else
|
|
// collide vs. the appropriate corner/vertex
|
|
//
|
|
//if obj is in this tile: collide as with aabb
|
|
//
|
|
//if obj is horiz or vertical neighbor AGAINST the slope: collide with edge
|
|
//
|
|
//if obj is horiz neighb in direction of slope: collide vs. slope or vertex or edge
|
|
//
|
|
//if obj is vert neighb in direction of slope: collide vs. slope or vertex
|
|
|
|
var signx = t.signx;
|
|
var signy = t.signy;
|
|
var sx: number;
|
|
var sy: number;
|
|
|
|
if (oH == 0)
|
|
{
|
|
if (oV == 0)
|
|
{
|
|
//colliding with current cell
|
|
|
|
sx = t.sx;
|
|
sy = t.sy;
|
|
|
|
var r = obj.radius;
|
|
var ox = (obj.pos.x - (sx * r)) - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost
|
|
var oy = (obj.pos.y - (sy * r)) - (t.pos.y + (signy * t.yw));//point on the AABB, relative to a point on the slope
|
|
|
|
//if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope
|
|
//and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy)
|
|
var dp = (ox * sx) + (oy * sy);
|
|
|
|
if (dp < 0)
|
|
{
|
|
//collision; project delta onto slope and use this to displace the object
|
|
sx *= -dp;//(sx,sy) is now the projection vector
|
|
sy *= -dp;
|
|
|
|
var lenN = Math.sqrt(sx * sx + sy * sy);
|
|
var lenP;
|
|
|
|
//find the smallest axial projection vector
|
|
if (x < y)
|
|
{
|
|
//penetration in x is smaller
|
|
lenP = x;
|
|
y = 0;
|
|
//get sign for projection along x-axis
|
|
if ((obj.pos.x - t.pos.x) < 0)
|
|
{
|
|
x *= -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//penetration in y is smaller
|
|
lenP = y;
|
|
x = 0;
|
|
//get sign for projection along y-axis
|
|
if ((obj.pos.y - t.pos.y) < 0)
|
|
{
|
|
y *= -1;
|
|
}
|
|
}
|
|
|
|
if (lenP < lenN)
|
|
{
|
|
obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t);
|
|
|
|
return Phaser.Physics.Circle.COL_AXIS;
|
|
}
|
|
else
|
|
{
|
|
obj.reportCollisionVsWorld(sx, sy, t.sx, t.sy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//colliding vertically
|
|
|
|
if ((signy * oV) < 0)
|
|
{
|
|
//colliding with face/edge
|
|
obj.reportCollisionVsWorld(0, y * oV, 0, oV, t);
|
|
|
|
return Phaser.Physics.Circle.COL_AXIS;
|
|
}
|
|
else
|
|
{
|
|
//we could only be colliding vs the slope OR a vertex
|
|
//look at the vector form the closest vert to the circle to decide
|
|
|
|
sx = t.sx;
|
|
sy = t.sy;
|
|
|
|
var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost
|
|
var oy = obj.pos.y - (t.pos.y + (signy * t.yw));//point on the circle, relative to the closest tile vert
|
|
|
|
//if the component of (ox,oy) parallel to the normal's righthand normal
|
|
//has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy)
|
|
//then we project by the vertex, otherwise by the normal.
|
|
//note that this is simply a VERY tricky/weird method of determining
|
|
//if the circle is in side the slope/face's voronio region, or that of the vertex.
|
|
var perp = (ox * -sy) + (oy * sx);
|
|
|
|
if (0 < (perp * signx * signy))
|
|
{
|
|
//collide vs. vertex
|
|
var len = Math.sqrt(ox * ox + oy * oy);
|
|
var pen = obj.radius - len;
|
|
|
|
if (0 < pen)
|
|
{
|
|
//note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0
|
|
ox /= len;
|
|
oy /= len;
|
|
|
|
obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//collide vs. slope
|
|
|
|
//if the component of (ox,oy) parallel to the normal is less than the circle radius, we're
|
|
//penetrating the slope. note that this method of penetration calculation doesn't hold
|
|
//in general (i.e it won't work if the circle is in the slope), but works in this case
|
|
//because we know the circle is in a neighboring cell
|
|
var dp = (ox * sx) + (oy * sy);
|
|
var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case..
|
|
|
|
if (0 < pen)
|
|
{
|
|
//collision; circle out along normal by penetration amount
|
|
obj.reportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (oV == 0)
|
|
{
|
|
//colliding horizontally
|
|
|
|
if ((signx * oH) < 0)
|
|
{
|
|
//colliding with face/edge
|
|
obj.reportCollisionVsWorld(x * oH, 0, oH, 0, t);
|
|
|
|
return Phaser.Physics.Circle.COL_AXIS;
|
|
}
|
|
else
|
|
{
|
|
//colliding with edge, slope, or vertex
|
|
|
|
var ox = obj.pos.x - (t.pos.x + (signx * t.xw));//this gives is the coordinates of the innermost
|
|
var oy = obj.pos.y - t.pos.y;//point on the circle, relative to the closest tile vert
|
|
|
|
if ((oy * signy) < 0)
|
|
{
|
|
//we're colliding with the halfface
|
|
obj.reportCollisionVsWorld(x * oH, 0, oH, 0, t);
|
|
|
|
return Phaser.Physics.Circle.COL_AXIS;
|
|
}
|
|
else
|
|
{
|
|
//colliding with the vertex or slope
|
|
|
|
sx = t.sx;
|
|
sy = t.sy;
|
|
|
|
//if the component of (ox,oy) parallel to the normal's righthand normal
|
|
//has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy)
|
|
//then we project by the slope, otherwise by the vertex.
|
|
//note that this is simply a VERY tricky/weird method of determining
|
|
//if the circle is in side the slope/face's voronio region, or that of the vertex.
|
|
var perp = (ox * -sy) + (oy * sx);
|
|
|
|
if ((perp * signx * signy) < 0)
|
|
{
|
|
//collide vs. vertex
|
|
var len = Math.sqrt(ox * ox + oy * oy);
|
|
var pen = obj.radius - len;
|
|
|
|
if (0 < pen)
|
|
{
|
|
//note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0
|
|
ox /= len;
|
|
oy /= len;
|
|
|
|
obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//collide vs. slope
|
|
|
|
//if the component of (ox,oy) parallel to the normal is less than the circle radius, we're
|
|
//penetrating the slope. note that this method of penetration calculation doesn't hold
|
|
//in general (i.e it won't work if the circle is in the slope), but works in this case
|
|
//because we know the circle is in a neighboring cell
|
|
var dp = (ox * sx) + (oy * sy);
|
|
var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case..
|
|
|
|
if (0 < pen)
|
|
{
|
|
//collision; circle out along normal by penetration amount
|
|
obj.reportCollisionVsWorld(sx * pen, sy * pen, t.sx, t.sy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//colliding diagonally
|
|
if (0 < ((signx * oH) + (signy * oV)))
|
|
{
|
|
//the dotprod of slope normal and cell offset is strictly positive,
|
|
//therefore obj is in the diagonal neighb pointed at by the normal.
|
|
|
|
//collide vs slope
|
|
|
|
//we should really precalc this at compile time, but for now, fuck it
|
|
var slen: number = Math.sqrt(2 * 2 + 1 * 1);//the raw slope is (-2,-1)
|
|
sx = (signx * 1) / slen;//get slope _unit_ normal;
|
|
sy = (signy * 2) / slen;//raw RH normal is (1,-2)
|
|
|
|
var r = obj.radius;
|
|
var ox = (obj.pos.x - (sx * r)) - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost
|
|
var oy = (obj.pos.y - (sy * r)) - (t.pos.y + (signy * t.yw));//point on the circle, relative to a point on the slope
|
|
|
|
//if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope
|
|
//and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy)
|
|
var dp = (ox * sx) + (oy * sy);
|
|
|
|
if (dp < 0)
|
|
{
|
|
//collision; project delta onto slope and use this to displace the object
|
|
//(sx,sy)*-dp is the projection vector
|
|
obj.reportCollisionVsWorld(-sx * dp, -sy * dp, t.sx, t.sy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
return Phaser.Physics.Circle.COL_NONE;
|
|
}
|
|
else
|
|
{
|
|
//collide vs the appropriate vertex
|
|
var vx = t.pos.x + (oH * t.xw);
|
|
var vy = t.pos.y + (oV * t.yw);
|
|
|
|
var dx = obj.pos.x - vx;//calc vert->circle vector
|
|
var dy = obj.pos.y - vy;
|
|
|
|
var len = Math.sqrt(dx * dx + dy * dy);
|
|
var pen = obj.radius - len;
|
|
|
|
if (0 < pen)
|
|
{
|
|
//vertex is in the circle; project outward
|
|
if (len == 0)
|
|
{
|
|
//project out by 45deg
|
|
dx = oH / Math.SQRT2;
|
|
dy = oV / Math.SQRT2;
|
|
}
|
|
else
|
|
{
|
|
dx /= len;
|
|
dy /= len;
|
|
}
|
|
|
|
obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t);
|
|
|
|
return Phaser.Physics.Circle.COL_OTHER;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return Phaser.Physics.Circle.COL_NONE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|