From d8f984ef6c9798fd1259375c5be97c2b5de182b3 Mon Sep 17 00:00:00 2001 From: Sun-dried oranges <1113812931@qq.com> Date: Fri, 7 Aug 2026 08:32:25 +0800 Subject: [PATCH 1/2] Add FunkinBackdrop class for customizable backdrop --- source/funkin/backend/FunkinBackdrop.hx | 152 ++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 source/funkin/backend/FunkinBackdrop.hx diff --git a/source/funkin/backend/FunkinBackdrop.hx b/source/funkin/backend/FunkinBackdrop.hx new file mode 100644 index 000000000..20b29bd3e --- /dev/null +++ b/source/funkin/backend/FunkinBackdrop.hx @@ -0,0 +1,152 @@ +package funkin.backend; + +import flixel.FlxCamera; +import flixel.FlxG; +import flixel.addons.display.FlxBackdrop; +import flixel.math.FlxMath; +import flixel.math.FlxRect; +import flixel.util.FlxAxes; +import flixel.util.FlxColor; +import funkin.backend.system.Flags; + +class FunkinBackdrop extends FlxBackdrop +{ + public var zoomFactor:Float = 1; + public var zoomFactorEnabled:Bool = true; + public var angleFactor:Float = 1; + public var angleFactorEnabled:Bool = true; + + private var _rect2:FlxRect; + + public function new(?graphic:Dynamic, repeatAxes = XY, spacingX:Int = 0, spacingY:Int = 0) + { + super(graphic, repeatAxes, spacingX, spacingY); + _rect2 = FlxRect.get(); + antialiasing = true; + } + + private inline function __shouldDoZoomFactor():Bool + { + return zoomFactorEnabled && zoomFactor != 1 && camera != null; + } + + private inline function __getZoomScaleX(camera:FlxCamera):Float + { + return (camera.scaleX > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.scaleX, 1, zoomFactor)); + } + + private inline function __getZoomScaleY(camera:FlxCamera):Float + { + return (camera.scaleY > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.scaleY, 1, zoomFactor)); + } + + private inline function __getZoomAnchorX(camera:FlxCamera):Float + { + if (Flags.USE_LEGACY_ZOOM_FACTOR) + return camera.width * 0.5 - origin.x; + + return camera.width * 0.5 + camera.scroll.x * scrollFactor.x - origin.x; + } + + private inline function __getZoomAnchorY(camera:FlxCamera):Float + { + if (Flags.USE_LEGACY_ZOOM_FACTOR) + return camera.height * 0.5 - origin.y; + + return camera.height * 0.5 + camera.scroll.y * scrollFactor.y - origin.y; + } + + private inline function __shouldDoAngleFactor():Bool + { + return angleFactorEnabled && angleFactor != 1 && camera != null; + } + + private inline function __getAngleAnchorX(camera:FlxCamera):Float + { + if (Flags.USE_LEGACY_ZOOM_FACTOR) + return camera.width * 0.5 - origin.x; + + return camera.width * 0.5 + camera.scroll.x * scrollFactor.x - origin.x; + } + + private inline function __getAngleAnchorY(camera:FlxCamera):Float + { + if (Flags.USE_LEGACY_ZOOM_FACTOR) + return camera.height * 0.5 - origin.y; + + return camera.height * 0.5 + camera.scroll.y * scrollFactor.y - origin.y; + } + + private inline function __prepareAngleFactor(camera:FlxCamera):Float + { + return FlxMath.lerp(-camera.angle, 0, angleFactor); + } + + override public function draw():Void + { + if (!__shouldDoZoomFactor() && !__shouldDoAngleFactor()) + { + super.draw(); + return; + } + + var camera:FlxCamera = this.camera; + if (camera == null) + camera = FlxG.camera; + + var oldX:Float = x; + var oldY:Float = y; + var oldScaleX:Float = scale.x; + var oldScaleY:Float = scale.y; + var oldAngle:Float = angle; + + if (__shouldDoZoomFactor()) + { + var zoomScaleX:Float = __getZoomScaleX(camera); + var zoomScaleY:Float = __getZoomScaleY(camera); + + var anchorX:Float = __getZoomAnchorX(camera); + var anchorY:Float = __getZoomAnchorY(camera); + + x = (x - anchorX) * zoomScaleX + anchorX; + y = (y - anchorY) * zoomScaleY + anchorY; + + scale.set(scale.x * zoomScaleX, scale.y * zoomScaleY); + } + + if (__shouldDoAngleFactor()) + { + var anchorX:Float = __getAngleAnchorX(camera); + var anchorY:Float = __getAngleAnchorY(camera); + var prepAngle:Float = __prepareAngleFactor(camera); + + var rad:Float = prepAngle * (Math.PI / 180); + var cos:Float = Math.cos(rad); + var sin:Float = Math.sin(rad); + + var dx:Float = x - anchorX; + var dy:Float = y - anchorY; + + x = dx * cos - dy * sin + anchorX; + y = dx * sin + dy * cos + anchorY; + angle += prepAngle; + } + + super.draw(); + + x = oldX; + y = oldY; + scale.set(oldScaleX, oldScaleY); + angle = oldAngle; + } + + override public function destroy():Void + { + if (_rect2 != null) + { + _rect2.put(); + _rect2 = null; + } + super.destroy(); + } +} From 8df9b59688a61d77df957122a06d6ae744307a40 Mon Sep 17 00:00:00 2001 From: Sun-dried oranges <1113812931@qq.com> Date: Fri, 7 Aug 2026 09:05:16 +0800 Subject: [PATCH 2/2] Add FunkinBackdrop to script mappings --- source/funkin/backend/scripting/Script.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx index 037609d7d..6e936388b 100644 --- a/source/funkin/backend/scripting/Script.hx +++ b/source/funkin/backend/scripting/Script.hx @@ -105,6 +105,7 @@ class Script extends FlxBasic implements IFlxDestroyable { "FunkinText" => funkin.backend.FunkinText, "FlxAnimate" => animate.FlxAnimate, "FunkinSprite" => funkin.backend.FunkinSprite, + "FunkinBackdrop" => funkin.backend.FunkinBackdrop, "Alphabet" => funkin.menus.ui.Alphabet, "Flags" => funkin.backend.system.Flags,