diff --git a/flixel/FlxObject.hx b/flixel/FlxObject.hx index 0a9caf524a..d483fb7d99 100644 --- a/flixel/FlxObject.hx +++ b/flixel/FlxObject.hx @@ -159,16 +159,8 @@ class FlxObject extends FlxBasic */ public static function separate(object1:FlxObject, object2:FlxObject):Bool { - var tmp1 = object1.last.copyTo(); - var tmp2 = object2.last.copyTo(); final separatedX = separateX(object1, object2); - object1.last.x = object1.x; - object2.last.x = object2.x; final separatedY = separateY(object1, object2); - object1.last.copyFrom(tmp1); - object2.last.copyFrom(tmp2); - tmp1.put(); - tmp2.put(); return separatedX || separatedY; /* @@ -222,6 +214,11 @@ class FlxObject extends FlxBasic final vel1 = object1.velocity.x; final vel2 = object2.velocity.x; + inline function sameDir(a:Float, b:Float) + { + return (a <= 0 && b <= 0) || (a >= 0 && b >= 0); + } + if (!object1.immovable && !object2.immovable) { #if FLX_4_LEGACY_COLLISION @@ -233,19 +230,25 @@ class FlxObject extends FlxBasic final mass1 = object1.mass; final mass2 = object2.mass; final momentum = mass1 * vel1 + mass2 * vel2; - object1.velocity.x = (momentum + object1.elasticity * mass2 * (vel2 - vel1)) / (mass1 + mass2); - object2.velocity.x = (momentum + object2.elasticity * mass1 * (vel1 - vel2)) / (mass1 + mass2); + + if (sameDir(vel1, overlap)) + object1.velocity.x = (momentum + object1.elasticity * mass2 * (vel2 - vel1)) / (mass1 + mass2); + + if (sameDir(vel2, -overlap)) + object2.velocity.x = (momentum + object2.elasticity * mass1 * (vel1 - vel2)) / (mass1 + mass2); #end } else if (!object1.immovable) { object1.x -= overlap; - object1.velocity.x = vel2 - vel1 * object1.elasticity; + if (sameDir(vel1, overlap)) + object1.velocity.x = vel2 - vel1 * object1.elasticity; } else if (!object2.immovable) { object2.x += overlap; - object2.velocity.x = vel1 - vel2 * object2.elasticity; + if (sameDir(vel2, -overlap)) + object2.velocity.x = vel1 - vel2 * object2.elasticity; } // use collisionDrag properties to determine whether one object @@ -274,6 +277,11 @@ class FlxObject extends FlxBasic final vel1 = object1.velocity.y; final vel2 = object2.velocity.y; + inline function sameDir(a:Float, b:Float) + { + return (a <= 0 && b <= 0) || (a >= 0 && b >= 0); + } + if (!object1.immovable && !object2.immovable) { #if FLX_4_LEGACY_COLLISION @@ -287,19 +295,25 @@ class FlxObject extends FlxBasic final momentum = mass1 * vel1 + mass2 * vel2; final newVel1 = (momentum + object1.elasticity * mass2 * (vel2 - vel1)) / (mass1 + mass2); final newVel2 = (momentum + object2.elasticity * mass1 * (vel1 - vel2)) / (mass1 + mass2); - object1.velocity.y = newVel1; - object2.velocity.y = newVel2; + + if (sameDir(vel1, overlap)) + object1.velocity.y = newVel1; + + if (sameDir(vel2, -overlap)) + object2.velocity.y = newVel2; #end } else if (!object1.immovable) { object1.y -= overlap; - object1.velocity.y = vel2 - vel1 * object1.elasticity; + if (sameDir(vel1, overlap)) + object1.velocity.y = vel2 - vel1 * object1.elasticity; } else if (!object2.immovable) { object2.y += overlap; - object2.velocity.y = vel1 - vel2 * object2.elasticity; + if (sameDir(vel2, -overlap)) + object2.velocity.y = vel1 - vel2 * object2.elasticity; } // use collisionDrag properties to determine whether one object diff --git a/tests/unit/src/flixel/FlxObjectTest.hx b/tests/unit/src/flixel/FlxObjectTest.hx index 5094fcaf08..59849bbed7 100644 --- a/tests/unit/src/flixel/FlxObjectTest.hx +++ b/tests/unit/src/flixel/FlxObjectTest.hx @@ -2,8 +2,8 @@ package flixel; import flixel.FlxObject; import flixel.graphics.FlxGraphic; -import flixel.math.FlxPoint; import flixel.math.FlxMath; +import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.tile.FlxTilemap; import flixel.util.FlxDirectionFlags; @@ -118,18 +118,306 @@ class FlxObjectTest extends FlxTest Assert.isTrue(object1.y > object2.y); } + /** Object moving down onto platform moving right */ @Test - function testSeparateOnBothAxisNewlyOverlapping():Void + function testImmovDragY():Void { - final object1 = new FlxObject(11, -1, 10, 10); + final object1 = new FlxObject(5, 0, 10, 10); + object1.y = 5; + object1.collisionXDrag = IMMOVABLE; final object2 = new FlxObject(0, 10, 10, 10); object2.immovable = true; + object2.x = 5; - object1.setPosition(9, 2); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 should move right with object2 + Assert.areEqual(10, object1.x); + } + + + /** Object moving right onto object moving down */ + @Test + function testImmovDragX():Void + { + final object1 = new FlxObject(0, 5, 10, 10); + object1.x = 5; + object1.collisionYDrag = IMMOVABLE; + final object2 = new FlxObject(10, 0, 10, 10); + object2.immovable = true; + object2.y = 5; Assert.isTrue(FlxObject.separate(object1, object2)); - // X-axis resolves first and no collision + // object1 should move down with object2 + Assert.areEqual(10, object1.y); + } + + /** Object moving down onto platform moving right */ + @Test + function testMovingDragY():Void + { + final object1 = new FlxObject(5, 0, 10, 10); + object1.y = 5; + object1.collisionXDrag = ALWAYS; + final object2 = new FlxObject(0, 10, 10, 10); + object2.x = 5; + + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 should move right with object2 + Assert.areEqual(10, object1.x); + } + + /** Object moving right onto object moving down */ + @Test + function testMovingDragX():Void + { + final object1 = new FlxObject(0, 5, 10, 10); + object1.x = 5; + object1.collisionYDrag = ALWAYS; + final object2 = new FlxObject(10, 0, 10, 10); + object2.y = 5; + + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 should move down with object2 + Assert.areEqual(10, object1.y); + } + + @Test + function testMovingNoDragY():Void + { + final object1 = new FlxObject(5, 0, 10, 10); + object1.y = 5; + object1.collisionXDrag = NEVER; + final object2 = new FlxObject(0, 10, 10, 10); + object2.x = 5; + + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 should NOT move right with object2 + Assert.areEqual(5, object1.x); + } + + @Test + function testMovingNoDragX():Void + { + final object1 = new FlxObject(0, 5, 10, 10); + object1.x = 5; + object1.collisionYDrag = NEVER; + final object2 = new FlxObject(10, 0, 10, 10); + object2.y = 5; + + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 should NOT move down with object2 + Assert.areEqual(5, object1.y); + } + + /** + * Object overlapping the top of an object, with upwards veocity + */ + @Test + function testSeparateOppositeVelocityUp():Void + { + function reset() + { + object1.last.set(0, -10); + object1.setSize(10, 10); + object1.x = object1.last.x; + object1.y = object1.last.y + 2; + object1.velocity.y = -100; + object2.last.set(0, 0); + object2.setPosition(0, 0); + object2.setSize(10, 10); + } + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity + Assert.areEqual(-9, object1.y); + Assert.areEqual(-100, object1.velocity.y); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(-9, object1.y); + Assert.areEqual(-100, object1.velocity.y); + + // test with immovable b + object2.immovable = true; + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity + Assert.areEqual(-10, object1.y); + Assert.areEqual(-100, object1.velocity.y); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(-10, object1.y); + Assert.areEqual(-100, object1.velocity.y); + } + + /** + * Object overlapping the bottom of an object, with downwards veocity + */ + @Test + function testSeparateOppositeVelocityDown():Void + { + function reset() + { + object1.last.set(0, 10); + object1.setSize(10, 10); + object1.x = object1.last.x; + object1.y = object1.last.y - 2; + object1.velocity.y = 100; + object2.last.set(0, 0); + object2.setPosition(0, 0); + object2.setSize(10, 10); + } + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity + Assert.areEqual(9, object1.y); + Assert.areEqual(100, object1.velocity.y); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(9, object1.y); + Assert.areEqual(100, object1.velocity.y); + + // test with immovable b + object2.immovable = true; + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity + Assert.areEqual(10, object1.y); + Assert.areEqual(100, object1.velocity.y); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(10, object1.y); + Assert.areEqual(100, object1.velocity.y); + } + + /** + * Object overlapping the left side of an object, with leftwards veocity + */ + @Test + function testSeparateOppositeVelocityLeft():Void + { + function reset() + { + object1.last.set(-10, 0); + object1.setSize(10, 10); + object1.x = object1.last.x + 2; + object1.y = object1.last.y; + object1.velocity.x = -100; + object2.last.set(0, 0); + object2.setPosition(0, 0); + object2.setSize(10, 10); + } + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity + Assert.areEqual(-9, object1.x); + Assert.areEqual(-100, object1.velocity.x); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(-9, object1.x); + Assert.areEqual(-100, object1.velocity.x); + + // test with immovable b + object2.immovable = true; + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity + Assert.areEqual(-10, object1.x); + Assert.areEqual(-100, object1.velocity.x); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(-10, object1.x); + Assert.areEqual(-100, object1.velocity.x); + } + + /** + * Object overlapping the right side of an object, with rightwards veocity + */ + @Test + function testSeparateOppositeVelocityRight():Void + { + function reset() + { + object1.last.set(10, 0); + object1.setSize(10, 10); + object1.x = object1.last.x - 2; + object1.y = object1.last.y; + object1.velocity.x = 100; + object2.last.set(0, 0); + object2.setPosition(0, 0); + object2.setSize(10, 10); + } + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity Assert.areEqual(9, object1.x); + Assert.areEqual(100, object1.velocity.x); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(9, object1.x); + Assert.areEqual(100, object1.velocity.x); + + // test with immovable b + object2.immovable = true; + + reset(); + Assert.isTrue(FlxObject.separate(object1, object2)); + // object1 is separated, but maintains velocity + Assert.areEqual(10, object1.x); + Assert.areEqual(100, object1.velocity.x); + + // test with swapped a/b + reset(); + Assert.isTrue(FlxObject.separate(object2, object1)); + // object1 is separated, but maintains velocity + Assert.areEqual(10, object1.x); + Assert.areEqual(100, object1.velocity.x); + } + + @Ignore("Reverted #3418 as it breaks moving platforms") + @Test + function testSeparateOnBothAxisNewlyOverlapping():Void + { + final object1 = new FlxObject(0, 0, 10, 10); + final object2 = new FlxObject(11, 11, 10, 10); + object2.immovable = true; + Assert.isFalse(FlxObject.separate(object1, object2)); + + object1.setPosition(5, 5); + + // FlxObject.separate(object1, object2); + // Assert.areEqual(5, FlxObject.computeOverlapX(object1, object2)); + // Assert.areEqual(5, FlxObject.computeOverlapY(object1, object2)); + Assert.isTrue(FlxObject.separate(object1, object2)); + // X-axis resolves first and no collision + Assert.areEqual(5, object1.x); // Y-axis resolves second and is stopped by collision Assert.areEqual(0, object1.y); }