Skip to content

Add FlxG.centerGraphic() and camera.centerGraphic() #3329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: 6.2.0
Choose a base branch
from
Open
47 changes: 47 additions & 0 deletions flixel/FlxCamera.hx
Original file line number Diff line number Diff line change
Expand Up @@ -1873,6 +1873,53 @@ 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 6.2.0
*/
public function centerGraphic<T:FlxSprite>(sprite:T, axes:FlxAxes = XY):T
{
final graphicBounds = sprite.getAccurateScreenBounds(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 6.2.0
*/
public function centerHitbox<T:FlxObject>(object:T, axes:FlxAxes = XY):T
{
if (axes.x)
object.x = scroll.x + (width - object.width) / 2;

if (axes.y)
object.y = scroll.y + (height - object.height) / 2;

return object;
}

/**
* The size and position of this camera's margins, via `viewMarginLeft`, `viewMarginTop`, `viewWidth`
* and `viewHeight`.
Expand Down
50 changes: 49 additions & 1 deletion flixel/FlxG.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -476,7 +477,54 @@ 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 6.2.0
*/
public static function centerGraphic<T:FlxSprite>(sprite:T, axes:FlxAxes = XY):T
{
final graphicBounds = sprite.getAccurateGraphicBounds();

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 6.2.0
*/
public static function centerHitbox<T:FlxObject>(object:T, axes:FlxAxes = XY):T
{
if (axes.x)
object.x = (FlxG.width - object.width) / 2;

if (axes.y)
object.y = (FlxG.height - object.height) / 2;

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.
Expand Down
37 changes: 21 additions & 16 deletions flixel/FlxObject.hx
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ class FlxObject extends FlxBasic
{
return (x + width > FlxG.worldBounds.x) && (x < FlxG.worldBounds.right) && (y + height > FlxG.worldBounds.y) && (y < FlxG.worldBounds.bottom);
}

/**
* Returns the screen position of this object.
*
Expand All @@ -1040,20 +1040,30 @@ class FlxObject extends FlxBasic
* @return The screen position of this object.
*/
public function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint
{
return getScreenPositionHelper(result, camera, true);
}

public function getAccurateScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint
{
return getScreenPositionHelper(result, camera, false);
}

public function getScreenPositionHelper(result:FlxPoint, camera:FlxCamera, honorPixelPerfect:Bool):FlxPoint
{
if (result == null)
result = FlxPoint.get();

if (camera == null)
camera = getDefaultCamera();

result.set(x, y);
if (pixelPerfectPosition)
if (honorPixelPerfect && pixelPerfectPosition)
result.floor();

return result.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y);
}

/**
* Returns the world position of this object.
*
Expand Down Expand Up @@ -1173,24 +1183,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.
Expand Down
49 changes: 35 additions & 14 deletions flixel/FlxSprite.hx
Original file line number Diff line number Diff line change
Expand Up @@ -1289,23 +1289,31 @@ class FlxSprite extends FlxObject
* @since 5.9.0
*/
public function getGraphicBounds(?rect:FlxRect):FlxRect
{
return getGraphicBoundsHelper(rect, true);
}

public function getAccurateGraphicBounds(?rect:FlxRect):FlxRect
{
return getGraphicBoundsHelper(rect, false);
}

public function getGraphicBoundsHelper(rect:FlxRect, honorPixelPerfect:Bool):FlxRect
{
if (rect == null)
rect = FlxRect.get();

rect.set(x, y);
if (pixelPerfectPosition)
if (honorPixelPerfect && pixelPerfectPosition)
rect.floor();

_scaledOrigin.set(origin.x * scale.x, origin.y * scale.y);
final absoluteScale = _point.set(Math.abs(scale.x), Math.abs(scale.y));
_scaledOrigin.set(origin.x * absoluteScale.x, origin.y * absoluteScale.y);
rect.x += origin.x - offset.x - _scaledOrigin.x;
rect.y += origin.y - offset.y - _scaledOrigin.y;
rect.setSize(frameWidth * scale.x, frameHeight * scale.y);
rect.setSize(frameWidth * absoluteScale.x, frameHeight * absoluteScale.y);

if (angle % 360 != 0)
rect.getRotatedBounds(angle, _scaledOrigin, rect);

return rect;
return rect.getRotatedBounds(angle, _scaledOrigin, rect);
}

/**
Expand Down Expand Up @@ -1359,7 +1367,7 @@ class FlxSprite extends FlxObject
* @return A globally aligned `FlxRect` that fully contains the input object's width and height.
* @since 4.11.0
*/
override function getRotatedBounds(?newRect:FlxRect)
override public function getRotatedBounds(?newRect:FlxRect):FlxRect
{
if (newRect == null)
newRect = FlxRect.get();
Expand All @@ -1377,6 +1385,16 @@ class FlxSprite extends FlxObject
* @since 4.11.0
*/
public function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect
{
return getScreenBoundsHelper(newRect, camera, true);
}

public function getAccurateScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect
{
return getScreenBoundsHelper(newRect, camera, false);
}

function getScreenBoundsHelper(newRect:FlxRect, camera:FlxCamera, honorPixelPerfect:Bool):FlxRect
{
if (newRect == null)
newRect = FlxRect.get();
Expand All @@ -1385,14 +1403,17 @@ class FlxSprite extends FlxObject
camera = getDefaultCamera();

newRect.setPosition(x, y);
if (pixelPerfectPosition)
if (honorPixelPerfect && pixelPerfectPosition)
newRect.floor();
_scaledOrigin.set(origin.x * scale.x, origin.y * scale.y);
newRect.x += -Std.int(camera.scroll.x * scrollFactor.x) - offset.x + origin.x - _scaledOrigin.x;
newRect.y += -Std.int(camera.scroll.y * scrollFactor.y) - offset.y + origin.y - _scaledOrigin.y;
if (isPixelPerfectRender(camera))

final absoluteScale = _point.set(Math.abs(scale.x), Math.abs(scale.y));
_scaledOrigin.set(origin.x * absoluteScale.x, origin.y * absoluteScale.y);
newRect.x += -camera.scroll.x * scrollFactor.x - offset.x + origin.x - _scaledOrigin.x;
newRect.y += -camera.scroll.y * scrollFactor.y - offset.y + origin.y - _scaledOrigin.y;
if (honorPixelPerfect && isPixelPerfectRender(camera))
newRect.floor();
newRect.setSize(frameWidth * Math.abs(scale.x), frameHeight * Math.abs(scale.y));

newRect.setSize(frameWidth * absoluteScale.x, frameHeight * absoluteScale.y);
return newRect.getRotatedBounds(angle, _scaledOrigin, newRect);
}

Expand Down
76 changes: 75 additions & 1 deletion tests/unit/src/flixel/FlxCameraTest.hx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package flixel;

import flixel.math.FlxPoint;
import flixel.util.FlxColor;
import haxe.PosInfos;
import massive.munit.Assert;

@:access(flixel.system.frontEnds.CameraFrontEnd)
Expand Down Expand Up @@ -89,7 +91,79 @@ class FlxCameraTest extends FlxTest
camera.follow(new FlxObject());
Assert.areEqual(defaultLerp, camera.followLerp);
}


@Test // #3329
function testCenterGraphic()
{
final cam = FlxG.camera;
cam.scroll.set(100.5, 100.5);
cam.zoom *= 2;

Assert.areEqual(cam.width, 640);
Assert.areEqual(cam.height, 480);

final sprite = new FlxSprite();
sprite.makeGraphic(10, 10);
sprite.scrollFactor.set(2, 2);
sprite.origin.set(10, 10);
sprite.offset.set(10, 10);
sprite.scale.set(2, 4);
sprite.angle = 180;
sprite.pixelPerfectPosition = true;
sprite.pixelPerfectRender = true;

sprite.setPosition(0, 0);
cam.centerGraphic(sprite, X);
assertCenter(sprite, 100.5 + 320 - 10 - (-110.5 + 10), 0);

sprite.setPosition(0, 0);
cam.centerGraphic(sprite, Y);
assertCenter(sprite, 0, 100.5 + 240 - 20 - (-110.5 + 10));

sprite.setPosition(0, 0);
cam.centerGraphic(sprite, XY);
assertCenter(sprite, 100.5 + 320 - 10 - (-110.5 + 10), 100.5 + 240 - 20 - (-110.5 + 10));

sprite.setPosition(1640, 1480);
cam.centerGraphic(sprite);
assertCenter(sprite, 100.5 + 320 - 10 - (-110.5 + 10), 100.5 + 240 - 20 - (-110.5 + 10));
}

@Test // #3329
function testCenterHitbox()
{
final cam = FlxG.camera;
cam.scroll.set(100.5, 100.5);
cam.zoom *= 2;

Assert.areEqual(cam.width, 640);
Assert.areEqual(cam.height, 480);

final object = new FlxObject(0, 0, 10, 10);

object.setPosition(0, 0);
cam.centerHitbox(object, X);
assertCenter(object, 100.5 + 320 - 5, 0);

object.setPosition(0, 0);
cam.centerHitbox(object, Y);
assertCenter(object, 0, 100.5 + 240 - 5);

object.setPosition(0, 0);
cam.centerHitbox(object, XY);
assertCenter(object, 100.5 + 320 - 5, 100.5 + 240 - 5);

object.setPosition(1640, 1480);
cam.centerHitbox(object);
assertCenter(object, 100.5 + 320 - 5, 100.5 + 240 - 5);
}

function assertCenter(object:FlxObject, expectedX:Float, expectedY:Float, ?info:PosInfos)
{
FlxAssert.areNear(object.x, expectedX, info);
FlxAssert.areNear(object.y, expectedY, info);
}

@Test
function testFadeInFadeOut()
{
Expand Down
Loading