@@ -54,7 +54,7 @@ Phaser.Plugin.ArcadeSlopes.prototype.constructor = Phaser.Plugin.ArcadeSlopes;
54
54
* @constant
55
55
* @type {string }
56
56
*/
57
- Phaser . Plugin . ArcadeSlopes . VERSION = '0.2.0-beta2 ' ;
57
+ Phaser . Plugin . ArcadeSlopes . VERSION = '0.2.0' ;
58
58
59
59
/**
60
60
* The Separating Axis Theorem collision solver type.
@@ -232,6 +232,7 @@ Phaser.Plugin.ArcadeSlopes.Facade.prototype.enableBody = function (body) {
232
232
body . slopes = body . slopes || {
233
233
debug : false ,
234
234
friction : new Phaser . Point ( ) ,
235
+ heuristics : null ,
235
236
preferY : false ,
236
237
pullUp : 0 ,
237
238
pullDown : 0 ,
@@ -296,7 +297,7 @@ Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemapLayer = function (laye
296
297
* @return {boolean } - Whether the body was separated.
297
298
*/
298
299
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 ) ;
300
301
} ;
301
302
302
303
/**
@@ -814,12 +815,23 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer = function () {
814
815
*/
815
816
this . restraints = { } ;
816
817
818
+ /**
819
+ * A reusable separation axis vector.
820
+ *
821
+ * @property {SAT.Vector } separationAxis
822
+ */
823
+ this . separationAxis = new SAT . Vector ( ) ;
824
+
817
825
// Define all of the default restraints
818
826
this . setDefaultRestraints ( ) ;
819
827
} ;
820
828
821
829
/**
822
830
* 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.
823
835
*
824
836
* @method Phaser.Plugin.ArcadeSlopes.SatRestrainer#restrain
825
837
* @param {Phaser.Plugin.ArcadeSlopes.SatSolver } solver - The SAT solver.
@@ -879,9 +891,17 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.restrain = function (solver,
879
891
separate = separate . call ( this , body , tile , response ) ;
880
892
}
881
893
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 ) ;
885
905
}
886
906
887
907
return false ;
@@ -921,6 +941,8 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps = function (direction)
921
941
overlapX : [ 0 , 1 ] ,
922
942
overlapY : 0
923
943
} ;
944
+ case 'any' :
945
+ return { } ;
924
946
}
925
947
926
948
console . warn ( 'Unknown overlap direction \'' + direction + '\'' ) ;
@@ -941,7 +963,7 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps = function (direction)
941
963
* @param {object } restraints - The restraints to prepare.
942
964
* @return {object } - The prepared restraints.
943
965
*/
944
- Phaser . Plugin . ArcadeSlopes . SatRestrainer . prepareRestraints = function ( restraints ) {
966
+ Phaser . Plugin . ArcadeSlopes . SatRestrainer . prepareRestraints = function ( restraints ) {
945
967
var prepared = { } ;
946
968
947
969
for ( var type in restraints ) {
@@ -955,16 +977,22 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function(restraints
955
977
if ( rule . direction ) {
956
978
var resolved = Phaser . Plugin . ArcadeSlopes . SatRestrainer . resolveOverlaps ( rule . direction ) ;
957
979
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
+ }
960
987
}
961
988
962
989
// Resolve neighbour types from their string representations
963
990
for ( var nt in rule . types ) {
964
991
rule . types [ nt ] = Phaser . Plugin . ArcadeSlopes . TileSlope . resolveType ( rule . types [ nt ] ) ;
965
992
}
966
993
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
968
996
if ( rule . separate !== false && typeof rule . separate !== 'function' ) {
969
997
rule . separate = true ;
970
998
}
@@ -978,6 +1006,62 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function(restraints
978
1006
return prepared ;
979
1007
} ;
980
1008
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
+
981
1065
/**
982
1066
* Set all of the default SAT collision handling restraints.
983
1067
*
@@ -991,6 +1075,33 @@ Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function(restraints
991
1075
Phaser . Plugin . ArcadeSlopes . SatRestrainer . prototype . setDefaultRestraints = function ( ) {
992
1076
var restraints = { } ;
993
1077
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
+
994
1105
restraints . HALF_TOP = [
995
1106
{
996
1107
direction : 'left' ,
@@ -2118,8 +2229,10 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collide = function (i, body, tile
2118
2229
tile . slope . polygon . pos . x = tile . worldX + tilemapLayer . getCollisionOffsetX ( ) ;
2119
2230
tile . slope . polygon . pos . y = tile . worldY + tilemapLayer . getCollisionOffsetY ( ) ;
2120
2231
2232
+ // Reuse the body's response or create one for it
2121
2233
var response = body . slopes . sat . response || new SAT . Response ( ) ;
2122
2234
2235
+ // Reset the response
2123
2236
Phaser . Plugin . ArcadeSlopes . SatSolver . resetResponse ( response ) ;
2124
2237
2125
2238
// Test for an overlap and bail if there isn't one
@@ -2171,8 +2284,8 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collideOnAxis = function (body, t
2171
2284
// Update the body's polygon position and velocity vector
2172
2285
this . updateValues ( body ) ;
2173
2286
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 ) {
2176
2289
return false ;
2177
2290
}
2178
2291
@@ -2248,38 +2361,36 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldSeparate = function (i, bod
2248
2361
return false ;
2249
2362
}
2250
2363
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 ) {
2253
2366
return false ;
2254
2367
}
2255
2368
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
+ }
2258
2374
}
2259
2375
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 ) {
2261
2378
return false ;
2262
2379
}
2263
2380
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 ) {
2265
2382
return false ;
2266
2383
}
2267
2384
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 ) {
2270
2386
return false ;
2271
2387
}
2272
2388
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 ) {
2280
2390
return false ;
2281
2391
}
2282
2392
2393
+ // Otherwise we should separate normally
2283
2394
return true ;
2284
2395
} ;
2285
2396
@@ -2346,7 +2457,7 @@ Phaser.Plugin.ArcadeSlopes.TileSlope = function (type, tile, polygon, line, edge
2346
2457
this . line = line ;
2347
2458
2348
2459
/**
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?
2350
2461
*
2351
2462
* @property {object } edges
2352
2463
*/
0 commit comments