Skip to content

FlxTween framerate option #3372

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

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions flixel/tweens/FlxTween.hx
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ class FlxTween implements IFlxDestroyable
public var active(default, set):Bool = false;
public var duration:Float = 0;
public var ease:EaseFunction;
public var framerate:Null<Float>;
Copy link
Member

Choose a reason for hiding this comment

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

Let's use a float where 0 means "an infinite rate "

Suggested change
public var framerate:Null<Float>;
public var framerate:Float;

public var onStart:TweenCallback;
public var onUpdate:TweenCallback;
public var onComplete:TweenCallback;
Expand Down Expand Up @@ -562,6 +563,7 @@ class FlxTween implements IFlxDestroyable
onUpdate = Options.onUpdate;
onComplete = Options.onComplete;
ease = Options.ease;
framerate = Options.framerate;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
framerate = Options.framerate;
framerate = Options.framerate != null ? Options.framerate : 0;

setDelays(Options.startDelay, Options.loopDelay);
this.manager = manager != null ? manager : globalManager;
}
Expand Down Expand Up @@ -619,13 +621,22 @@ class FlxTween implements IFlxDestroyable

function update(elapsed:Float):Void
{
_secondsSinceStart += elapsed;
var preTick:Float = _secondsSinceStart;
var postTick:Float = (_secondsSinceStart += elapsed);
Comment on lines +624 to +625
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
var preTick:Float = _secondsSinceStart;
var postTick:Float = (_secondsSinceStart += elapsed);
var preTick:Float = _secondsSinceStart;
_secondsSinceStart += elapsed
var postTick:Float = _secondsSinceStart;


var delay:Float = (executions > 0) ? loopDelay : startDelay;
if (_secondsSinceStart < delay)
{
return;
}
scale = Math.max((_secondsSinceStart - delay), 0) / duration;

if (framerate != null && framerate > 0)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (framerate != null && framerate > 0)
if (framerate > 0)

{
preTick = Math.fround(preTick * framerate) / framerate;
postTick = Math.fround(postTick * framerate) / framerate;
}

scale = Math.max((postTick - delay), 0) / duration;
if (ease != null)
{
scale = ease(scale);
Expand All @@ -647,7 +658,7 @@ class FlxTween implements IFlxDestroyable
}
else
{
if (onUpdate != null)
if (postTick > preTick && onUpdate != null)
onUpdate(this);
}
}
Expand Down Expand Up @@ -919,6 +930,12 @@ typedef TweenOptions =
*/
@:optional var ease:EaseFunction;

/**
* Optional set framerate for this tween to update at.
* This also affects how often `onUpdate` is called.
*/
@:optional var framerate:Float;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@:optional var framerate:Float;
@:optional var framerate:Null<Float>;


/**
* Optional start callback function.
*/
Expand Down
7 changes: 7 additions & 0 deletions flixel/tweens/misc/FlickerTween.hx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ typedef FlickerTweenOptions =
*/
@:optional var ease:EaseFunction;

/**
* Optional set framerate for this tween to update at.
* This also affects how often `onUpdate` is called.
* Not to be confused with `period` for flickering.
*/
@:optional var framerate:Float;
Copy link
Member

Choose a reason for hiding this comment

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

nit picky, but I'm trying to use Null whenever it's implied

Suggested change
@:optional var framerate:Float;
@:optional var framerate:Null<Float>;


/**
* Optional start callback function.
*/
Expand Down
20 changes: 20 additions & 0 deletions tests/unit/src/flixel/tweens/FlxTweenTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,26 @@ class FlxTweenTest extends FlxTest
Assert.isTrue(tween3Updated);
}

@Test
function testTweenFramerate()
{
// no given tween framerate
var tweenUpdatesDefault:Int = 0;
var tweenDefault:FlxTween = FlxTween.tween(this, {value: 1000}, 2, {startDelay: 0.54321, onUpdate: function(_) tweenUpdatesDefault++});
step(FlxG.updateFramerate * 3); // 3 full seconds
Assert.areEqual(FlxG.updateFramerate * 2, tweenUpdatesDefault);

// various tween framerate values
var testFramerates:Array<Float> = [5, 10, 10.1, 24, 29.9, 30, 30.1, 31, 59.9, 60, 100];
for (framerate in testFramerates)
{
var tweenUpdates:Int = 0;
var tween:FlxTween = FlxTween.tween(this, {value: 1000}, 2, {startDelay: 0.54321, framerate: framerate, onUpdate: function(_) tweenUpdates++});
step(FlxG.updateFramerate * 3); // 3 full seconds
Assert.areEqual(Std.int(Math.ceil(Math.min(framerate, FlxG.updateFramerate) * 2)), tweenUpdates);
}
}
Comment on lines +439 to +453
Copy link
Member

@Geokureli Geokureli Feb 21, 2025

Choose a reason for hiding this comment

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

it's a little difficult to understand what this is testing, and generally, we should assert against literal values rather than an equation. Also try to avoid "throwing the kitchen sink" at every test, make it very obvious what each test covers and why it's needed

Example (untested):

function assertTweenUpdates(duration:Float, ?fps:Float, ?options:TweenOptions, expected:Int)
{
	var updates = 0;
	
	if (options)
		options = {};
	
	if (fps != null)
		options. framerate = fps;
	
	options.onUpdate = function(_) updates++;
	options.onComplete = function(_) Assert.areEqual(expected, updates);
	FlxTween.tween({value: 0.0}, {value: 1}, 2.0, options);
}

assertTweenUpdates(2.0, null, null, 60);
assertTweenUpdates(2.0, 0, null, 60);
assertTweenUpdates(2.0, 5, null, 10);
assertTweenUpdates(2.0, 10, { startDelay: 0.5 }, 20);
assertTweenUpdates(2.0, 10.1, null, 21);
assertTweenUpdates(2.0, 29.9, null, 60);
assertTweenUpdates(2.0, 100, null, 60);
//... other edge cases

step(FlxG.updateFramerate * 3); // 3 full seconds


function makeTween(duration:Float, onComplete:TweenCallback, ?onUpdate:TweenCallback):FlxTween
{
var foo = {f: 0};
Expand Down