Skip to content
Open
9 changes: 9 additions & 0 deletions flixel/FlxCamera.hx
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,12 @@ class FlxCamera extends FlxBasic
*/
public function center<T:FlxSprite>(sprite:T, axes:FlxAxes = XY):T
{
// We need to disable these flags to get accurate graphic bounds
final pixelPerfectPosition = sprite.pixelPerfectPosition;
final pixelPerfectRender = sprite.pixelPerfectRender;
sprite.pixelPerfectPosition = false;
@:bypassAccessor sprite.pixelPerfectRender = false;
Copy link
Member

@Geokureli Geokureli Apr 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make a new helper that allows us to get the unrounded value, or a new arg in the existing one that allows us to ignore this field. this is not the first case where pxPerfPos has been a hassle

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about this, maybe this should NOT attempt to avoid pixelPerfectRender and position, perhaps the unit test was failing because it should have accounted for this? Can you elaborate on why it failed and why centering should not honor pixelperfectRendering?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests were actually fine, the problem was discovered in a test project, where grapthic is centered every frame. pixelPerfectPosition = true or pixelPerfectRender = true led to shaking, so my workaround was to temporarily disable them, as i don't see a reason to add another function to get graphic bounds.

For now i removed this workaround. Adding another argument to functions will work, but will cause problems with function override. Should this be a concern?

My test project:

package;

import flixel.FlxG;
import flixel.FlxSprite;
import flixel.util.FlxAxes;

class PlayState extends flixel.FlxState
{
	var graphic:FlxSprite;
	var centerMode = FlxAxes.XY;
	var rorate = false;

	override public function create()
	{
		super.create();

		FlxG.cameras.bgColor = flixel.util.FlxColor.GRAY;

		graphic = new FlxSprite().makeGraphic(20, 20);
		graphic.scrollFactor.set(2, 2);
		graphic.origin.set(20, 20);
		graphic.offset.set(200, 200);
		graphic.scale.set(2, 4);
		add(graphic);

		FlxG.watch.add(FlxG.camera, "scroll", "camera");
		FlxG.watch.add(this, "graphic", "graphic");
		FlxG.watch.add(graphic, "pixelPerfectPosition", "pixelPerfectPosition");
		FlxG.watch.add(graphic, "pixelPerfectRender", "pixelPerfectRender");
	}

	override public function update(elapsed:Float)
	{
		super.update(elapsed);

		var mult = 10.0;
		if (FlxG.keys.pressed.SHIFT)
			mult *= 10;

		if (FlxG.keys.pressed.A)
			FlxG.camera.scroll.x -= mult * elapsed;
		if (FlxG.keys.pressed.D)
			FlxG.camera.scroll.x += mult * elapsed;

		if (FlxG.keys.pressed.W)
			FlxG.camera.scroll.y -= mult * elapsed;
		if (FlxG.keys.pressed.S)
			FlxG.camera.scroll.y += mult * elapsed;

		mult /= 100;
		if (FlxG.keys.pressed.Q)
			FlxG.camera.zoom += mult * elapsed;
		if (FlxG.keys.pressed.E)
			FlxG.camera.zoom -= mult * elapsed;

		if (FlxG.keys.justPressed.Z)
			centerMode = FlxAxes.fromBools(!centerMode.x, centerMode.y);
		if (FlxG.keys.justPressed.X)
			centerMode = FlxAxes.fromBools(centerMode.x, !centerMode.y);

		if (FlxG.keys.justPressed.C)
			graphic.pixelPerfectPosition = !graphic.pixelPerfectPosition;
		if (FlxG.keys.justPressed.V)
			graphic.pixelPerfectRender = !graphic.pixelPerfectRender;

		if (FlxG.keys.justPressed.R)
			rorate = !rorate;
		if (rorate)
			graphic.angle += elapsed;

		FlxG.camera.centerGraphic(graphic, centerMode);
	}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an example to reproduce the shaking effect?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Shaking appears when camera is moved or sprite is rotated.
Side note, might also be the case for any transformation.


final graphicBounds = sprite.getScreenBounds(null, this);

if (axes.x)
Expand All @@ -1897,6 +1903,9 @@ class FlxCamera extends FlxBasic
sprite.y = (height - graphicBounds.height) / 2 + offset;
}

// Now we set flags back to their original state
sprite.pixelPerfectPosition = pixelPerfectPosition;
@:bypassAccessor sprite.pixelPerfectRender = pixelPerfectRender;
graphicBounds.put();
return sprite;
}
Expand Down
6 changes: 6 additions & 0 deletions flixel/FlxG.hx
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,10 @@ class FlxG
*/
public static function center<T:FlxSprite>(sprite:T, axes:FlxAxes = XY):T
{
// We need to disable this flag to get accurate graphic bounds
final pixelPerfectPosition = sprite.pixelPerfectPosition;
sprite.pixelPerfectPosition = false;

final graphicBounds = sprite.getGraphicBounds();

if (axes.x)
Expand All @@ -502,6 +506,8 @@ class FlxG
sprite.y = (FlxG.height - graphicBounds.height) / 2 + offset;
}

// Now we set flag back to its original state
sprite.pixelPerfectPosition = pixelPerfectPosition;
graphicBounds.put();
return sprite;
}
Expand Down
4 changes: 2 additions & 2 deletions flixel/FlxSprite.hx
Original file line number Diff line number Diff line change
Expand Up @@ -1368,8 +1368,8 @@ class FlxSprite extends FlxObject
if (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;
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 (isPixelPerfectRender(camera))
newRect.floor();
newRect.setSize(frameWidth * Math.abs(scale.x), frameHeight * Math.abs(scale.y));
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/src/flixel/FlxCameraTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ class FlxCameraTest extends FlxTest
sprite.offset.set(100, 100);
sprite.scale.set(2, 4);
sprite.angle = 180;
sprite.pixelPerfectPosition = true;
sprite.pixelPerfectRender = true;
final cam = FlxG.camera;
cam.scroll.set(100, 100);
cam.zoom *= 2;
Expand Down
1 change: 1 addition & 0 deletions tests/unit/src/flixel/FlxGTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class FlxGTest extends FlxTest
sprite.offset.set(100, 100);
sprite.scale.set(2, 4);
sprite.angle = 180;
sprite.pixelPerfectPosition = true;
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);
Expand Down