Merge pull request #627 from FunkinCrew/pixel-perfect

more retro week 6 stuff
This commit is contained in:
Cameron Taylor 2024-06-25 13:48:29 -04:00 committed by GitHub
commit 9b64d08944
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 277 additions and 16 deletions

2
assets

@ -1 +1 @@
Subproject commit 4b95075255baeaba3585fabff7052c257856fafe
Subproject commit 225e248f148a92500a6fe90e4f10e4cd2acee782

View file

@ -0,0 +1,106 @@
package funkin.effects;
import flixel.util.FlxTimer;
import flixel.FlxCamera;
import openfl.filters.ColorMatrixFilter;
class RetroCameraFade
{
// im lazy, but we only use this for week 6
// and also sorta yoinked for djflixel, lol !
public static function fadeWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void
{
var steps:Int = 0;
var stepsTotal:Int = camSteps;
new FlxTimer().start(time / stepsTotal, _ -> {
var V:Float = (1 / stepsTotal) * steps;
if (steps == stepsTotal) V = 1;
var matrix = [
1, 0, 0, 0, V * 255,
0, 1, 0, 0, V * 255,
0, 0, 1, 0, V * 255,
0, 0, 0, 1, 0
];
camera.filters = [new ColorMatrixFilter(matrix)];
steps++;
}, stepsTotal + 1);
}
public static function fadeFromWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void
{
var steps:Int = camSteps;
var stepsTotal:Int = camSteps;
var matrixDerp = [
1, 0, 0, 0, 1.0 * 255,
0, 1, 0, 0, 1.0 * 255,
0, 0, 1, 0, 1.0 * 255,
0, 0, 0, 1, 0
];
camera.filters = [new ColorMatrixFilter(matrixDerp)];
new FlxTimer().start(time / stepsTotal, _ -> {
var V:Float = (1 / stepsTotal) * steps;
if (steps == stepsTotal) V = 1;
var matrix = [
1, 0, 0, 0, V * 255,
0, 1, 0, 0, V * 255,
0, 0, 1, 0, V * 255,
0, 0, 0, 1, 0
];
camera.filters = [new ColorMatrixFilter(matrix)];
steps--;
}, camSteps);
}
public static function fadeToBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void
{
var steps:Int = 0;
var stepsTotal:Int = camSteps;
new FlxTimer().start(time / stepsTotal, _ -> {
var V:Float = (1 / stepsTotal) * steps;
if (steps == stepsTotal) V = 1;
var matrix = [
1, 0, 0, 0, -V * 255,
0, 1, 0, 0, -V * 255,
0, 0, 1, 0, -V * 255,
0, 0, 0, 1, 0
];
camera.filters = [new ColorMatrixFilter(matrix)];
steps++;
}, camSteps);
}
public static function fadeBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void
{
var steps:Int = camSteps;
var stepsTotal:Int = camSteps;
var matrixDerp = [
1, 0, 0, 0, -1.0 * 255,
0, 1, 0, 0, -1.0 * 255,
0, 0, 1, 0, -1.0 * 255,
0, 0, 0, 1, 0
];
camera.filters = [new ColorMatrixFilter(matrixDerp)];
new FlxTimer().start(time / stepsTotal, _ -> {
var V:Float = (1 / stepsTotal) * steps;
if (steps == stepsTotal) V = 1;
var matrix = [
1, 0, 0, 0, -V * 255,
0, 1, 0, 0, -V * 255,
0, 0, 1, 0, -V * 255,
0, 0, 0, 1, 0
];
camera.filters = [new ColorMatrixFilter(matrix)];
steps--;
}, camSteps + 1);
}
}

View file

@ -7,6 +7,10 @@ import flixel.tweens.FlxTween;
import openfl.display3D.textures.TextureBase;
import funkin.graphics.framebuffer.FixedBitmapData;
import openfl.display.BitmapData;
import flixel.math.FlxRect;
import flixel.math.FlxPoint;
import flixel.graphics.frames.FlxFrame;
import flixel.FlxCamera;
/**
* An FlxSprite with additional functionality.
@ -269,6 +273,103 @@ class FunkinSprite extends FlxSprite
return result;
}
@:access(flixel.FlxCamera)
override function getBoundingBox(camera:FlxCamera):FlxRect
{
getScreenPosition(_point, camera);
_rect.set(_point.x, _point.y, width, height);
_rect = camera.transformRect(_rect);
if (isPixelPerfectRender(camera))
{
_rect.width = _rect.width / this.scale.x;
_rect.height = _rect.height / this.scale.y;
_rect.x = _rect.x / this.scale.x;
_rect.y = _rect.y / this.scale.y;
_rect.floor();
_rect.x = _rect.x * this.scale.x;
_rect.y = _rect.y * this.scale.y;
_rect.width = _rect.width * this.scale.x;
_rect.height = _rect.height * this.scale.y;
}
return _rect;
}
/**
* Returns the screen position of this object.
*
* @param result Optional arg for the returning point
* @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used.
* @return The screen position of this object.
*/
public override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint
{
if (result == null) result = FlxPoint.get();
if (camera == null) camera = FlxG.camera;
result.set(x, y);
if (pixelPerfectPosition)
{
_rect.width = _rect.width / this.scale.x;
_rect.height = _rect.height / this.scale.y;
_rect.x = _rect.x / this.scale.x;
_rect.y = _rect.y / this.scale.y;
_rect.round();
_rect.x = _rect.x * this.scale.x;
_rect.y = _rect.y * this.scale.y;
_rect.width = _rect.width * this.scale.x;
_rect.height = _rect.height * this.scale.y;
}
return result.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y);
}
override function drawSimple(camera:FlxCamera):Void
{
getScreenPosition(_point, camera).subtractPoint(offset);
if (isPixelPerfectRender(camera))
{
_point.x = _point.x / this.scale.x;
_point.y = _point.y / this.scale.y;
_point.round();
_point.x = _point.x * this.scale.x;
_point.y = _point.y * this.scale.y;
}
_point.copyToFlash(_flashPoint);
camera.copyPixels(_frame, framePixels, _flashRect, _flashPoint, colorTransform, blend, antialiasing);
}
override function drawComplex(camera:FlxCamera):Void
{
_frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY());
_matrix.translate(-origin.x, -origin.y);
_matrix.scale(scale.x, scale.y);
if (bakedRotationAngle <= 0)
{
updateTrig();
if (angle != 0) _matrix.rotateWithTrig(_cosAngle, _sinAngle);
}
getScreenPosition(_point, camera).subtractPoint(offset);
_point.add(origin.x, origin.y);
_matrix.translate(_point.x, _point.y);
if (isPixelPerfectRender(camera))
{
_matrix.tx = Math.round(_matrix.tx / this.scale.x) * this.scale.x;
_matrix.ty = Math.round(_matrix.ty / this.scale.y) * this.scale.y;
}
camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader);
}
public override function destroy():Void
{
frames = null;

View file

@ -9,6 +9,7 @@ import funkin.modding.module.ModuleHandler;
import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
import flixel.util.FlxTimer;
import funkin.util.EaseUtil;
import funkin.audio.FunkinSound;
class Countdown
@ -117,7 +118,7 @@ class Countdown
*
* If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event.
*/
public static function pauseCountdown()
public static function pauseCountdown():Void
{
if (countdownTimer != null && !countdownTimer.finished)
{
@ -130,7 +131,7 @@ class Countdown
*
* If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event.
*/
public static function resumeCountdown()
public static function resumeCountdown():Void
{
if (countdownTimer != null && !countdownTimer.finished)
{
@ -143,7 +144,7 @@ class Countdown
*
* If you want to call this from a module, it's better to use the event system and cancel the onCountdownStart event.
*/
public static function stopCountdown()
public static function stopCountdown():Void
{
if (countdownTimer != null)
{
@ -156,7 +157,7 @@ class Countdown
/**
* Stops the current countdown, then starts the song for you.
*/
public static function skipCountdown()
public static function skipCountdown():Void
{
stopCountdown();
// This will trigger PlayState.startSong()
@ -185,8 +186,11 @@ class Countdown
{
var spritePath:String = null;
var fadeEase = FlxEase.cubeInOut;
if (isPixelStyle)
{
fadeEase = EaseUtil.stepped(8);
switch (index)
{
case TWO:
@ -227,7 +231,7 @@ class Countdown
countdownSprite.screenCenter();
// Fade sprite in, then out, then destroy it.
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000,
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000,
{
ease: FlxEase.cubeInOut,
onComplete: function(twn:FlxTween) {
@ -235,6 +239,11 @@ class Countdown
}
});
FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000,
{
ease: fadeEase
});
PlayState.instance.add(countdownSprite);
}

View file

@ -16,6 +16,7 @@ import funkin.ui.MusicBeatSubState;
import funkin.ui.story.StoryMenuState;
import funkin.util.MathUtil;
import openfl.utils.Assets;
import funkin.effects.RetroCameraFade;
/**
* A substate which renders over the PlayState when the player dies.
@ -332,9 +333,12 @@ class GameOverSubState extends MusicBeatSubState
// After the animation finishes...
new FlxTimer().start(0.7, function(tmr:FlxTimer) {
// ...fade out the graphics. Then after that happens...
FlxG.camera.fade(FlxColor.BLACK, 2, false, function() {
var resetPlaying = function(pixel:Bool = false) {
// ...close the GameOverSubState.
FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true);
if (pixel) RetroCameraFade.fadeBlack(FlxG.camera, 10, 1);
else
FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true);
PlayState.instance.needsReset = true;
if (PlayState.instance.isMinimalMode || boyfriend == null) {}
@ -351,7 +355,22 @@ class GameOverSubState extends MusicBeatSubState
// Close the substate.
close();
});
};
if (musicSuffix == '-pixel')
{
RetroCameraFade.fadeToBlack(FlxG.camera, 10, 2);
new FlxTimer().start(2, _ -> {
FlxG.camera.filters = [];
resetPlaying(true);
});
}
else
{
FlxG.camera.fade(FlxColor.BLACK, 2, false, function() {
resetPlaying();
});
}
});
}
}

View file

@ -41,6 +41,8 @@ class MultiSparrowCharacter extends BaseCharacter
{
this.isPixel = true;
this.antialiasing = false;
pixelPerfectRender = true;
pixelPerfectPosition = true;
}
else
{

View file

@ -43,6 +43,8 @@ class PackerCharacter extends BaseCharacter
{
this.isPixel = true;
this.antialiasing = false;
pixelPerfectRender = true;
pixelPerfectPosition = true;
}
else
{

View file

@ -46,6 +46,8 @@ class SparrowCharacter extends BaseCharacter
{
this.isPixel = true;
this.antialiasing = false;
pixelPerfectRender = true;
pixelPerfectPosition = true;
}
else
{

View file

@ -7,8 +7,9 @@ import flixel.util.FlxDirection;
import funkin.graphics.FunkinSprite;
import funkin.play.PlayState;
import funkin.util.TimerUtil;
import funkin.util.EaseUtil;
class PopUpStuff extends FlxTypedGroup<FlxSprite>
class PopUpStuff extends FlxTypedGroup<FunkinSprite>
{
public var offsets:Array<Int> = [0, 0];
@ -17,7 +18,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
super();
}
public function displayRating(daRating:String)
public function displayRating(daRating:String):Void
{
var perfStart:Float = TimerUtil.start();
@ -40,10 +41,15 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
add(rating);
var fadeEase = null;
if (PlayState.instance.currentStageId.startsWith('school'))
{
rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7));
rating.antialiasing = false;
rating.pixelPerfectRender = true;
rating.pixelPerfectPosition = true;
fadeEase = EaseUtil.stepped(2);
}
else
{
@ -61,7 +67,8 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
remove(rating, true);
rating.destroy();
},
startDelay: Conductor.instance.beatLengthMs * 0.001
startDelay: Conductor.instance.beatLengthMs * 0.001,
ease: fadeEase
});
trace('displayRating took: ${TimerUtil.seconds(perfStart)}');
@ -92,10 +99,15 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
// add(comboSpr);
var fadeEase = null;
if (PlayState.instance.currentStageId.startsWith('school'))
{
comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 0.7));
comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 1));
comboSpr.antialiasing = false;
comboSpr.pixelPerfectRender = true;
comboSpr.pixelPerfectPosition = true;
fadeEase = EaseUtil.stepped(2);
}
else
{
@ -110,7 +122,8 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
remove(comboSpr, true);
comboSpr.destroy();
},
startDelay: Conductor.instance.beatLengthMs * 0.001
startDelay: Conductor.instance.beatLengthMs * 0.001,
ease: fadeEase
});
var seperatedScore:Array<Int> = [];
@ -133,8 +146,10 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
if (PlayState.instance.currentStageId.startsWith('school'))
{
numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7));
numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 1));
numScore.antialiasing = false;
numScore.pixelPerfectRender = true;
numScore.pixelPerfectPosition = true;
}
else
{
@ -156,7 +171,8 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
remove(numScore, true);
numScore.destroy();
},
startDelay: Conductor.instance.beatLengthMs * 0.002
startDelay: Conductor.instance.beatLengthMs * 0.002,
ease: fadeEase
});
daLoop++;

View file

@ -249,6 +249,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
// If pixel, disable antialiasing.
propSprite.antialiasing = !dataProp.isPixel;
// If pixel, we render it pixel perfect so there's less "mixels"
propSprite.pixelPerfectRender = dataProp.isPixel;
propSprite.pixelPerfectPosition = dataProp.isPixel;
propSprite.scrollFactor.x = dataProp.scroll[0];
propSprite.scrollFactor.y = dataProp.scroll[1];