Skip to content

Commit 6c116d2

Browse files
committed
Released 0.2.0
1 parent 48b7cdb commit 6c116d2

File tree

2 files changed

+140
-29
lines changed

2 files changed

+140
-29
lines changed

dist/phaser-arcade-slopes.js

+138-27
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Phaser.Plugin.ArcadeSlopes.prototype.constructor = Phaser.Plugin.ArcadeSlopes;
5454
* @constant
5555
* @type {string}
5656
*/
57-
Phaser.Plugin.ArcadeSlopes.VERSION = '0.2.0-beta2';
57+
Phaser.Plugin.ArcadeSlopes.VERSION = '0.2.0';
5858

5959
/**
6060
* The Separating Axis Theorem collision solver type.
@@ -232,6 +232,7 @@ Phaser.Plugin.ArcadeSlopes.Facade.prototype.enableBody = function (body) {
232232
body.slopes = body.slopes || {
233233
debug: false,
234234
friction: new Phaser.Point(),
235+
heuristics: null,
235236
preferY: false,
236237
pullUp: 0,
237238
pullDown: 0,
@@ -296,7 +297,7 @@ Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemapLayer = function (laye
296297
* @return {boolean} - Whether the body was separated.
297298
*/
298299
Phaser.Plugin.ArcadeSlopes.Facade.prototype.collide = function (i, body, tile, tilemapLayer, overlapOnly) {
299-
return this.solvers[this.defaultSolver].collide(i, body, tile, tilemapLayer, overlapOnly);
300+
return this.solvers.sat.collide(i, body, tile, tilemapLayer, overlapOnly);
300301
};
301302

302303
/**
@@ -814,12 +815,23 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer = function () {
814815
*/
815816
this.restraints = {};
816817

818+
/**
819+
* A reusable separation axis vector.
820+
*
821+
* @property {SAT.Vector} separationAxis
822+
*/
823+
this.separationAxis = new SAT.Vector();
824+
817825
// Define all of the default restraints
818826
this.setDefaultRestraints();
819827
};
820828

821829
/**
822830
* Restrain the given SAT body-tile collision context based on the set rules.
831+
*
832+
* Returns false if the collision is handled by a restraint condition, either
833+
* triggering separation itself in the best case or skipping it entirely in the
834+
* worst case.
823835
*
824836
* @method Phaser.Plugin.ArcadeSlopes.SatRestrainer#restrain
825837
* @param {Phaser.Plugin.ArcadeSlopes.SatSolver} solver - The SAT solver.
@@ -879,9 +891,17 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.restrain = function (solver,
879891
separate = separate.call(this, body, tile, response);
880892
}
881893

882-
// Collide on the tile's preferred axis if desired and available
883-
if (separate && tile.slope.axis) {
884-
solver.collideOnAxis(body, tile, tile.slope.axis);
894+
// Separate on the tile's preferred axis by default
895+
var separationAxis = tile.slope.axis;
896+
897+
// Use the restraint decision as the axis if it's a vector
898+
if (separate instanceof SAT.Vector) {
899+
separationAxis = separate;
900+
}
901+
902+
// Collide on the separation axis if desired and available
903+
if (separate && separationAxis) {
904+
solver.collideOnAxis(body, tile, separationAxis);
885905
}
886906

887907
return false;
@@ -921,6 +941,8 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps = function (direction)
921941
overlapX: [0, 1],
922942
overlapY: 0
923943
};
944+
case 'any':
945+
return {};
924946
}
925947

926948
console.warn('Unknown overlap direction \'' + direction + '\'');
@@ -941,7 +963,7 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps = function (direction)
941963
* @param {object} restraints - The restraints to prepare.
942964
* @return {object} - The prepared restraints.
943965
*/
944-
Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function(restraints) {
966+
Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function (restraints) {
945967
var prepared = {};
946968

947969
for (var type in restraints) {
@@ -955,16 +977,22 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function(restraints
955977
if (rule.direction) {
956978
var resolved = Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps(rule.direction);
957979

958-
rule.overlapX = resolved.overlapX;
959-
rule.overlapY = resolved.overlapY;
980+
if (resolved.hasOwnProperty('overlapX')) {
981+
rule.overlapX = resolved.overlapX;
982+
}
983+
984+
if (resolved.hasOwnProperty('overlapY')) {
985+
rule.overlapY = resolved.overlapY;
986+
}
960987
}
961988

962989
// Resolve neighbour types from their string representations
963990
for (var nt in rule.types) {
964991
rule.types[nt] = Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(rule.types[nt]);
965992
}
966993

967-
// Conveniently set separate to true unless it's already false
994+
// Conveniently set separate to true unless it's already false or
995+
// it's a function that resolves a separation decision
968996
if (rule.separate !== false && typeof rule.separate !== 'function') {
969997
rule.separate = true;
970998
}
@@ -978,6 +1006,62 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function(restraints
9781006
return prepared;
9791007
};
9801008

1009+
/**
1010+
* Eagerly separate a body from a square tile.
1011+
*
1012+
* This is used for full tile separation constraints to avoid tiny bodies
1013+
* slipping between tile seams.
1014+
*
1015+
* Ignores any non-colliding or internal edges.
1016+
*
1017+
* Returns a desired axis to separate on, if it can.
1018+
*
1019+
* @param {Phaser.Physics.Arcade.Body} body - The physics body.
1020+
* @param {Phaser.Tile} tile - The tile.
1021+
* @param {SAT.Response} response - The initial collision response.
1022+
* @return {SAT.Vector|boolean}
1023+
*/
1024+
Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.fullTileSeparation = function (body, tile, response) {
1025+
// If the body is above the tile center and top collisions are allowed,
1026+
// and we're moving down, and more vertically than horizontally
1027+
if (body.top < tile.worldY + tile.centerY && tile.collideUp && tile.slope.edges.top !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.velocity.y > 0 && Math.abs(body.velocity.y) > Math.abs(body.velocity.x)) {
1028+
this.separationAxis.x = 0;
1029+
this.separationAxis.y = -1;
1030+
1031+
return this.separationAxis;
1032+
}
1033+
1034+
// If the body is below the tile center and bottom collisions are allowed,
1035+
// and we're moving up, and more vertically than horizontally
1036+
if (body.bottom > tile.worldY + tile.centerY && tile.collideDown && tile.slope.edges.bottom !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.slopes.velocity.y < 0 && Math.abs(body.slopes.velocity.y) > Math.abs(body.slopes.velocity.x)) {
1037+
this.separationAxis.x = 0;
1038+
this.separationAxis.y = 1;
1039+
1040+
return this.separationAxis;
1041+
}
1042+
1043+
// If the body is left of the tile center and left collisions are allowed,
1044+
// and we're moving right, and more horizontally than vertically
1045+
if (body.left < tile.worldX + tile.centerX && tile.collideLeft && tile.slope.edges.left !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.slopes.velocity.x > 0 && Math.abs(body.slopes.velocity.x) > Math.abs(body.slopes.velocity.y)) {
1046+
this.separationAxis.x = -1;
1047+
this.separationAxis.y = 0;
1048+
1049+
return this.separationAxis;
1050+
}
1051+
1052+
// If the body is right of the tile center and right collisions are allowed,
1053+
// and we're moving left, and more horizontally than vertically
1054+
if (body.right > tile.worldX + tile.centerX && tile.collideRight && tile.slope.edges.right !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.slopes.velocity.x < 0 && Math.abs(body.slopes.velocity.x) > Math.abs(body.slopes.velocity.y)) {
1055+
this.separationAxis.x = 1;
1056+
this.separationAxis.y = 0;
1057+
1058+
return this.separationAxis;
1059+
}
1060+
1061+
// Otherwise separate normally
1062+
return true;
1063+
};
1064+
9811065
/**
9821066
* Set all of the default SAT collision handling restraints.
9831067
*
@@ -991,6 +1075,33 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function(restraints
9911075
Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.setDefaultRestraints = function () {
9921076
var restraints = {};
9931077

1078+
restraints.FULL = [
1079+
{
1080+
direction: 'up',
1081+
neighbour: 'above',
1082+
types: this.resolve('bottomLeft', 'bottomRight'),
1083+
separate: this.fullTileSeparation
1084+
},
1085+
{
1086+
direction: 'down',
1087+
neighbour: 'below',
1088+
types: this.resolve('topLeft', 'topRight'),
1089+
separate: this.fullTileSeparation
1090+
},
1091+
{
1092+
direction: 'left',
1093+
neighbour: 'left',
1094+
types: this.resolve('topRight', 'bottomRight'),
1095+
separate: this.fullTileSeparation
1096+
},
1097+
{
1098+
direction: 'right',
1099+
neighbour: 'right',
1100+
types: this.resolve('topLeft', 'bottomLeft'),
1101+
separate: this.fullTileSeparation
1102+
}
1103+
];
1104+
9941105
restraints.HALF_TOP = [
9951106
{
9961107
direction: 'left',
@@ -2118,8 +2229,10 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collide = function (i, body, tile
21182229
tile.slope.polygon.pos.x = tile.worldX + tilemapLayer.getCollisionOffsetX();
21192230
tile.slope.polygon.pos.y = tile.worldY + tilemapLayer.getCollisionOffsetY();
21202231

2232+
// Reuse the body's response or create one for it
21212233
var response = body.slopes.sat.response || new SAT.Response();
21222234

2235+
// Reset the response
21232236
Phaser.Plugin.ArcadeSlopes.SatSolver.resetResponse(response);
21242237

21252238
// Test for an overlap and bail if there isn't one
@@ -2171,8 +2284,8 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collideOnAxis = function (body, t
21712284
// Update the body's polygon position and velocity vector
21722285
this.updateValues(body);
21732286

2174-
// Bail out if we don't have everything we need
2175-
if (!this.shouldCollide(body, tile)) {
2287+
// Bail out if we don't have everything we need or the body is circular
2288+
if (!this.shouldCollide(body, tile) || body.isCircle) {
21762289
return false;
21772290
}
21782291

@@ -2248,38 +2361,36 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldSeparate = function (i, bod
22482361
return false;
22492362
}
22502363

2251-
// Ignore any non-colliding or internal edges
2252-
if ((!tile.collideUp || tile.slope.edges.top === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.y < 0 && response.overlapN.x === 0) {
2364+
// Only separate if the body is moving into the tile
2365+
if (response.overlapV.clone().scale(-1).dot(body.slopes.velocity) < 0) {
22532366
return false;
22542367
}
22552368

2256-
if ((!tile.collideDown || tile.slope.edges.bottom === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.y > 0 && response.overlapN.x === 0) {
2257-
return false;
2369+
// Run any separation restrainers if appropriate
2370+
if ((this.options.restrain || body.slopes.heuristics) && body.slopes.heuristics !== false && !body.isCircle) {
2371+
if (this.restrain(body, tile, response)) {
2372+
return false;
2373+
}
22582374
}
22592375

2260-
if ((!tile.collideLeft || tile.slope.edges.left === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.x < 0 && response.overlapN.y === 0) {
2376+
// Ignore any non-colliding or internal edges
2377+
if ((!tile.collideUp || tile.slope.edges.top === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.y < 0 && response.overlapN.x === 0) {
22612378
return false;
22622379
}
22632380

2264-
if ((!tile.collideRight || tile.slope.edges.right === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.x > 0 && response.overlapN.y === 0) {
2381+
if ((!tile.collideDown || tile.slope.edges.bottom === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.y > 0 && response.overlapN.x === 0) {
22652382
return false;
22662383
}
22672384

2268-
// Only separate if the body is moving into the tile
2269-
if (response.overlapV.clone().scale(-1).dot(body.slopes.velocity) < 0) {
2385+
if ((!tile.collideLeft || tile.slope.edges.left === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.x < 0 && response.overlapN.y === 0) {
22702386
return false;
22712387
}
22722388

2273-
// Skip restraints if they are disabled or the body is circular
2274-
if (!this.options.restrain || body.isCircle) {
2275-
return true;
2276-
}
2277-
2278-
// Run any separation restrainers
2279-
if (this.restrain(body, tile, response)) {
2389+
if ((!tile.collideRight || tile.slope.edges.right === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.x > 0 && response.overlapN.y === 0) {
22802390
return false;
22812391
}
22822392

2393+
// Otherwise we should separate normally
22832394
return true;
22842395
};
22852396

@@ -2346,7 +2457,7 @@ Phaser.Plugin.ArcadeSlopes.TileSlope = function (type, tile, polygon, line, edge
23462457
this.line = line;
23472458

23482459
/**
2349-
* The flags for each edge of the tile; empty, solid or interesting?
2460+
* The flags for each edge of the tile: empty, solid or interesting?
23502461
*
23512462
* @property {object} edges
23522463
*/

0 commit comments

Comments
 (0)