diff --git a/flixel/FlxCamera.hx b/flixel/FlxCamera.hx index fb195ea2d..76dd620c3 100644 --- a/flixel/FlxCamera.hx +++ b/flixel/FlxCamera.hx @@ -1873,6 +1873,62 @@ class FlxCamera extends FlxBasic setScale(scaleX, scaleY); } + /** + * Centers `FlxSprite` by graphic size in this camera view, either by the x axis, y axis, or both. + * + * @param sprite The sprite to center. + * @param axes On what axes to center the sprite (e.g. `X`, `Y`, `XY`) - default is both. + * @return Centered sprite for chaining. + * @since TBA + */ + public function center(sprite:T, axes:FlxAxes = XY):T + { + final graphicBounds = sprite.getScreenBounds(null, this); + + if (axes.x) + { + final offset = sprite.x - graphicBounds.x; + sprite.x = (width - graphicBounds.width) / 2 + offset; + } + + if (axes.y) + { + final offset = sprite.y - graphicBounds.y; + sprite.y = (height - graphicBounds.height) / 2 + offset; + } + + graphicBounds.put(); + return sprite; + } + + /** + * Centers `FlxObject` by hitbox size in this camera view, either by the x axis, y axis, or both. + * + * @param object The object to center. + * @param axes On what axes to center the object (e.g. `X`, `Y`, `XY`) - default is both. + * @return Centered object for chaining. + * @since TBA + */ + public function centerHitbox(object:T, axes:FlxAxes = XY):T + { + final hitbox = object.getHitbox(); + + if (axes.x) + { + final offset = object.x - hitbox.x; + object.x = scroll.x + (width - hitbox.width) / 2 + offset; + } + + if (axes.y) + { + final offset = object.y - hitbox.y; + object.y = scroll.y + (height - hitbox.height) / 2 + offset; + } + + hitbox.put(); + return object; + } + /** * The size and position of this camera's margins, via `viewMarginLeft`, `viewMarginTop`, `viewWidth` * and `viewHeight`. diff --git a/flixel/FlxG.hx b/flixel/FlxG.hx index d65d882d0..443e4e0cd 100644 --- a/flixel/FlxG.hx +++ b/flixel/FlxG.hx @@ -20,6 +20,7 @@ import flixel.system.frontEnds.VCRFrontEnd; import flixel.system.frontEnds.WatchFrontEnd; import flixel.system.scaleModes.BaseScaleMode; import flixel.system.scaleModes.RatioScaleMode; +import flixel.util.FlxAxes; import flixel.util.FlxCollision; import flixel.util.FlxSave; import flixel.util.typeLimit.NextState; @@ -476,7 +477,63 @@ class FlxG { return overlap(objectOrGroup1, objectOrGroup2, notifyCallback, FlxObject.separate); } - + + /** + * Centers `FlxSprite` by graphic size in game space, either by the x axis, y axis, or both. + * + * @param sprite The sprite to center. + * @param axes On what axes to center the sprite (e.g. `X`, `Y`, `XY`) - default is both. + * @return Centered sprite for chaining. + * @since TBA + */ + public static function center(sprite:T, axes:FlxAxes = XY):T + { + final graphicBounds = sprite.getGraphicBounds(); + + if (axes.x) + { + final offset = sprite.x - graphicBounds.x; + sprite.x = (FlxG.width - graphicBounds.width) / 2 + offset; + } + + if (axes.y) + { + final offset = sprite.y - graphicBounds.y; + sprite.y = (FlxG.height - graphicBounds.height) / 2 + offset; + } + + graphicBounds.put(); + return sprite; + } + + /** + * Centers `FlxObject` by hitbox size in game space, either by the x axis, y axis, or both. + * + * @param object The object to center. + * @param axes On what axes to center the object (e.g. `X`, `Y`, `XY`) - default is both. + * @return Centered object for chaining. + * @since TBA + */ + public static function centerHitbox(object:T, axes:FlxAxes = XY):T + { + final hitbox = object.getHitbox(); + + if (axes.x) + { + final offset = object.x - hitbox.x; + object.x = (FlxG.width - hitbox.width) / 2 + offset; + } + + if (axes.y) + { + final offset = object.y - hitbox.y; + object.y = (FlxG.height - hitbox.height) / 2 + offset; + } + + hitbox.put(); + return object; + } + /** * Regular `DisplayObject`s are normally displayed over the Flixel cursor and the Flixel debugger if simply * added to `stage`. This function simplifies things by adding a `DisplayObject` directly below mouse level. diff --git a/flixel/FlxObject.hx b/flixel/FlxObject.hx index 3c9b1d4f3..a43f4eae5 100644 --- a/flixel/FlxObject.hx +++ b/flixel/FlxObject.hx @@ -1168,24 +1168,19 @@ class FlxObject extends FlxBasic kill(); } #end - + /** * Centers this `FlxObject` on the screen, either by the x axis, y axis, or both. - * + * * @param axes On what axes to center the object (e.g. `X`, `Y`, `XY`) - default is both. * @return This FlxObject for chaining */ + @:deprecated("screenCenter is deprecated, use FlxG.centerHitbox instead") public inline function screenCenter(axes:FlxAxes = XY):FlxObject { - if (axes.x) - x = (FlxG.width - width) / 2; - - if (axes.y) - y = (FlxG.height - height) / 2; - - return this; + return FlxG.centerHitbox(this, axes); } - + /** * Helper function to set the coordinates of this object. * Handy since it only requires one line of code. diff --git a/tests/unit/src/flixel/FlxCameraTest.hx b/tests/unit/src/flixel/FlxCameraTest.hx index 85459f609..f7db289b4 100644 --- a/tests/unit/src/flixel/FlxCameraTest.hx +++ b/tests/unit/src/flixel/FlxCameraTest.hx @@ -1,5 +1,6 @@ package flixel; +import flixel.math.FlxPoint; import flixel.util.FlxColor; import massive.munit.Assert; @@ -89,7 +90,88 @@ class FlxCameraTest extends FlxTest camera.follow(new FlxObject()); Assert.areEqual(defaultLerp, camera.followLerp); } - + + @Test + function testCenter() + { + final sprite = new FlxSprite(); + sprite.makeGraphic(100, 100); + sprite.origin.set(100, 100); + sprite.offset.set(100, 100); + sprite.scale.set(2, 4); + sprite.angle = 180; + final cam = FlxG.camera; + cam.scroll.set(100, 100); + cam.zoom *= 2; + final graphicBounds = sprite.getScreenBounds(null, cam); + final offset = FlxPoint.get(sprite.x - graphicBounds.x, sprite.y - graphicBounds.y); + final center = FlxPoint.get((cam.width - graphicBounds.width) / 2 + offset.x, (cam.height - graphicBounds.height) / 2 + offset.y); + final offCenter = center.copyTo().add(1000, 1000); + + sprite.setPosition(offCenter.x, offCenter.y); + cam.center(sprite, X); + FlxAssert.areNear(sprite.x, center.x); + FlxAssert.areNear(sprite.y, offCenter.y); + + sprite.setPosition(offCenter.x, offCenter.y); + cam.center(sprite, Y); + FlxAssert.areNear(sprite.x, offCenter.x); + FlxAssert.areNear(sprite.y, center.y); + + sprite.setPosition(offCenter.x, offCenter.y); + cam.center(sprite, XY); + FlxAssert.areNear(sprite.x, center.x); + FlxAssert.areNear(sprite.y, center.y); + + sprite.setPosition(offCenter.x, offCenter.y); + cam.center(sprite); + FlxAssert.areNear(sprite.x, center.x); + FlxAssert.areNear(sprite.y, center.y); + + offset.put(); + graphicBounds.put(); + offCenter.put(); + center.put(); + } + + @Test + function testCenterHitbox() + { + final object = new FlxObject(0, 0, 10, 10); + final cam = FlxG.camera; + cam.scroll.set(100, 100); + cam.zoom *= 2; + final hitbox = object.getHitbox(); + final offset = FlxPoint.get(object.x - hitbox.x, object.y - hitbox.y); + final center = FlxPoint.get(cam.scroll.x + (cam.width - hitbox.width) / 2 + offset.x, cam.scroll.y + (cam.height - hitbox.height) / 2 + offset.y); + final offCenter = center.copyTo().add(1000, 1000); + + object.setPosition(offCenter.x, offCenter.y); + cam.centerHitbox(object, X); + Assert.areEqual(object.x, center.x); + Assert.areEqual(object.y, offCenter.y); + + object.setPosition(offCenter.x, offCenter.y); + cam.centerHitbox(object, Y); + Assert.areEqual(object.x, offCenter.x); + Assert.areEqual(object.y, center.y); + + object.setPosition(offCenter.x, offCenter.y); + cam.centerHitbox(object, XY); + Assert.areEqual(object.x, center.x); + Assert.areEqual(object.y, center.y); + + object.setPosition(offCenter.x, offCenter.y); + cam.centerHitbox(object); + Assert.areEqual(object.x, center.x); + Assert.areEqual(object.y, center.y); + + offset.put(); + hitbox.put(); + offCenter.put(); + center.put(); + } + @Test function testFadeInFadeOut() { diff --git a/tests/unit/src/flixel/FlxGTest.hx b/tests/unit/src/flixel/FlxGTest.hx index 58d3daac7..2872866bf 100644 --- a/tests/unit/src/flixel/FlxGTest.hx +++ b/tests/unit/src/flixel/FlxGTest.hx @@ -1,5 +1,6 @@ package flixel; +import flixel.math.FlxPoint; import massive.munit.Assert; @:access(flixel.FlxG) @@ -104,4 +105,79 @@ class FlxGTest extends FlxTest { Assert.areEqual(480, FlxG.height); } + + @Test + function testCenter() + { + final sprite = new FlxSprite(); + sprite.makeGraphic(100, 100); + sprite.origin.set(100, 100); + sprite.offset.set(100, 100); + sprite.scale.set(2, 4); + sprite.angle = 180; + final graphicBounds = sprite.getGraphicBounds(); + final offset = FlxPoint.get(sprite.x - graphicBounds.x, sprite.y - graphicBounds.y); + final center = FlxPoint.get((FlxG.width - graphicBounds.width) / 2 + offset.x, (FlxG.height - graphicBounds.height) / 2 + offset.y); + final offCenter = center.copyTo().add(1000, 1000); + + sprite.setPosition(offCenter.x, offCenter.y); + FlxG.center(sprite, X); + FlxAssert.areNear(sprite.x, center.x); + FlxAssert.areNear(sprite.y, offCenter.y); + + sprite.setPosition(offCenter.x, offCenter.y); + FlxG.center(sprite, Y); + FlxAssert.areNear(sprite.x, offCenter.x); + FlxAssert.areNear(sprite.y, center.y); + + sprite.setPosition(offCenter.x, offCenter.y); + FlxG.center(sprite, XY); + FlxAssert.areNear(sprite.x, center.x); + FlxAssert.areNear(sprite.y, center.y); + + sprite.setPosition(offCenter.x, offCenter.y); + FlxG.center(sprite); + FlxAssert.areNear(sprite.x, center.x); + FlxAssert.areNear(sprite.y, center.y); + + offset.put(); + graphicBounds.put(); + offCenter.put(); + center.put(); + } + + @Test + function testCenterHitbox() + { + final object = new FlxObject(0, 0, 10, 10); + final hitbox = object.getHitbox(); + final offset = FlxPoint.get(object.x - hitbox.x, object.y - hitbox.y); + final center = FlxPoint.get((FlxG.width - hitbox.width) / 2 + offset.x, (FlxG.height - hitbox.height) / 2 + offset.y); + final offCenter = center.copyTo().add(1000, 1000); + + object.setPosition(offCenter.x, offCenter.y); + FlxG.centerHitbox(object, X); + Assert.areEqual(object.x, center.x); + Assert.areEqual(object.y, offCenter.y); + + object.setPosition(offCenter.x, offCenter.y); + FlxG.centerHitbox(object, Y); + Assert.areEqual(object.x, offCenter.x); + Assert.areEqual(object.y, center.y); + + object.setPosition(offCenter.x, offCenter.y); + FlxG.centerHitbox(object, XY); + Assert.areEqual(object.x, center.x); + Assert.areEqual(object.y, center.y); + + object.setPosition(offCenter.x, offCenter.y); + FlxG.centerHitbox(object); + Assert.areEqual(object.x, center.x); + Assert.areEqual(object.y, center.y); + + offset.put(); + hitbox.put(); + offCenter.put(); + center.put(); + } } diff --git a/tests/unit/src/flixel/FlxObjectTest.hx b/tests/unit/src/flixel/FlxObjectTest.hx index dc8953022..99e998712 100644 --- a/tests/unit/src/flixel/FlxObjectTest.hx +++ b/tests/unit/src/flixel/FlxObjectTest.hx @@ -341,6 +341,7 @@ class FlxObjectTest extends FlxTest } @Test + @:haxe.warning("-WDeprecated") // TODO: remove this test? function testScreenCenter() { var center = FlxPoint.get((FlxG.width - object1.width) / 2, (FlxG.height - object1.height) / 2); @@ -350,17 +351,17 @@ class FlxObjectTest extends FlxTest object1.screenCenter(X); Assert.areEqual(object1.x, center.x); Assert.areEqual(object1.y, offCenter.y); - + object1.setPosition(offCenter.x, offCenter.y); object1.screenCenter(Y); Assert.areEqual(object1.x, offCenter.x); Assert.areEqual(object1.y, center.y); - + object1.setPosition(offCenter.x, offCenter.y); object1.screenCenter(XY); Assert.areEqual(object1.x, center.x); Assert.areEqual(object1.y, center.y); - + object1.setPosition(offCenter.x, offCenter.y); object1.screenCenter(); Assert.areEqual(object1.x, center.x); @@ -369,7 +370,7 @@ class FlxObjectTest extends FlxTest offCenter.put(); center.put(); } - + @Test function testgetRotatedBounds() {