Skip to content

Commit

Permalink
add FlxTween.flicker (#3086)
Browse files Browse the repository at this point in the history
* add FlxTween.flicker

* fix code climate

* add comment

* honor startDelay add tween.time

* unit tests + fixes

* fix flash CI
  • Loading branch information
Geokureli authored Mar 15, 2024
1 parent e98d1d3 commit 5ede2bd
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 17 deletions.
124 changes: 107 additions & 17 deletions flixel/tweens/FlxTween.hx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import flixel.math.FlxMath;
import flixel.tweens.FlxEase.EaseFunction;
import flixel.tweens.misc.AngleTween;
import flixel.tweens.misc.ColorTween;
import flixel.tweens.misc.FlickerTween;
import flixel.tweens.misc.NumTween;
import flixel.tweens.misc.VarTween;
import flixel.tweens.motion.CircularMotion;
Expand Down Expand Up @@ -252,7 +253,39 @@ class FlxTween implements IFlxDestroyable
{
return globalManager.num(FromValue, ToValue, Duration, Options, TweenFunction);
}


/**
* Flickers the desired object
*
* @param basic The object to flicker
* @param duration Duration of the tween, in seconds
* @param period How often, in seconds, the visibility cycles
* @param options A structure with flicker and tween options
* @since 5.7.0
*/
public static function flicker(basic:FlxBasic, duration = 1.0, period = 0.08, ?options:FlickerTweenOptions)
{
return globalManager.flicker(basic, duration, period, options);
}

/**
* Whether the object is flickering via the global tween manager
* @since 5.7.0
*/
public static function isFlickering(basic:FlxBasic)
{
return globalManager.isFlickering(basic);
}

/**
* Cancels all flicker tweens on the object in the global tween manager
* @since 5.7.0
*/
public static function stopFlickering(basic:FlxBasic)
{
return globalManager.stopFlickering(basic);
}

/**
* A simple shake effect for FlxSprite. Shorthand for creating a ShakeTween, starting it and adding it to the TweenManager.
*
Expand Down Expand Up @@ -517,6 +550,12 @@ class FlxTween implements IFlxDestroyable
public var finished(default, null):Bool;
public var scale(default, null):Float = 0;
public var backward(default, null):Bool;

/**
* The total time passed since start
* @since 5.7.0
*/
public var time(get, never):Float;

/**
* How many times this tween has been executed / has finished so far - useful to
Expand Down Expand Up @@ -854,10 +893,15 @@ class FlxTween implements IFlxDestroyable
}
return loopDelay = dly;
}

inline function get_time():Float
{
return Math.max(_secondsSinceStart - _delayToUse, 0);
}

inline function get_percent():Float
{
return Math.max((_secondsSinceStart - _delayToUse), 0) / duration;
return time / duration;
}

function set_percent(value:Float):Float
Expand Down Expand Up @@ -999,6 +1043,40 @@ class FlxTweenManager extends FlxBasic
tween.tween(FromValue, ToValue, Duration, TweenFunction);
return add(tween);
}

/**
* Flickers the desired object
*
* @param basic The object to flicker
* @param duration Duration of the tween, in seconds
* @param period How often, in seconds, the visibility cycles
* @param options A structure with flicker and tween options
* @since 5.7.0
*/
public function flicker(basic:FlxBasic, duration = 1.0, period = 0.08, ?options:FlickerTweenOptions)
{
final tween = new FlickerTween(options, this);
tween.tween(basic, duration, period);
return add(tween);
}

/**
* Whether the object is flickering via this manager
* @since 5.7.0
*/
public function isFlickering(basic:FlxBasic)
{
return containsTweensOf(basic, ["flicker"]);
}

/**
* Cancels all flicker tweens on the object
* @since 5.7.0
*/
public function stopFlickering(basic:FlxBasic)
{
return cancelTweensOf(basic, ["flicker"]);
}

/**
* A simple shake effect for FlxSprite. Shorthand for creating a ShakeTween, starting it and adding it to the TweenManager.
Expand Down Expand Up @@ -1394,37 +1472,37 @@ class FlxTweenManager extends FlxBasic
*
* Note: loops backwards to allow removals.
*
* @param Object The object with tweens you are searching for.
* @param FieldPaths Optional list of the tween field paths to check. If null or empty, any tween of the specified
* object will match. Allows dot paths to check child properties.
* @param Function The function to call on each matching tween.
* @param object The object with tweens you are searching for.
* @param fieldPaths List of the tween field paths to check. If `null` or empty, any tween of
* the specified object will match. Allows dot paths to check child properties.
* @param func The function to call on each matching tween.
*
* @since 4.9.0
*/
function forEachTweensOf(Object:Dynamic, ?FieldPaths:Array<String>, Function:FlxTween->Void)
function forEachTweensOf(object:Dynamic, ?fieldPaths:Array<String>, func:FlxTween->Void)
{
if (Object == null)
if (object == null)
throw "Cannot cancel tween variables of an object that is null.";

if (FieldPaths == null || FieldPaths.length == 0)
if (fieldPaths == null || fieldPaths.length == 0)
{
var i = _tweens.length;
while (i-- > 0)
{
var tween = _tweens[i];
if (tween.isTweenOf(Object))
Function(tween);
if (tween.isTweenOf(object))
func(tween);
}
}
else
{
// check for dot paths and convert to object/field pairs
var propertyInfos = new Array<TweenProperty>();
for (fieldPath in FieldPaths)
for (fieldPath in fieldPaths)
{
var target = Object;
var path = fieldPath.split(".");
var field = path.pop();
var target = object;
final path = fieldPath.split(".");
final field = path.pop();
for (component in path)
{
target = Reflect.getProperty(target, component);
Expand All @@ -1439,18 +1517,30 @@ class FlxTweenManager extends FlxBasic
var i = _tweens.length;
while (i-- > 0)
{
var tween = _tweens[i];
final tween = _tweens[i];
for (info in propertyInfos)
{
if (tween.isTweenOf(info.object, info.field))
{
Function(tween);
func(tween);
break;
}
}
}
}
}

/**
* Crude helper to search for any tweens with the desired properties
*
* @since 5.7.0
*/
function containsTweensOf(object:Dynamic, ?fieldPaths:Array<String>):Bool
{
var found = false;
forEachTweensOf(object, fieldPaths, (_)->found = true);
return found;
}

/**
* Immediately updates all tweens that are not looping (type `FlxTween.LOOPING` or `FlxTween.PINGPONG`)
Expand Down
137 changes: 137 additions & 0 deletions flixel/tweens/misc/FlickerTween.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package flixel.tweens.misc;

import flixel.FlxBasic;
import flixel.tweens.FlxTween;

/**
* Special tween options for flicker tweens
* @since 5.7.0
*/
typedef FlickerTweenOptions = TweenOptions &
{
/**
* Whether the object will show after the tween, defaults to `true`
*/
?endVisibility:Bool,

/**
* The amount of time the object will show, compared to the total duration, The default is `0.5`,
* meaning equal times visible and invisible.
*/
?ratio:Float,

/**
* An optional custom flicker function, defaults to
* `function (tween) { return (tween.time / tween.period) % 1 > tween.ratio; }`
*/
?tweenFunction:(FlickerTween)->Bool
};

/**
* Flickers an object. See `FlxTween.flicker()`
* @since 5.7.0
*/
class FlickerTween extends FlxTween
{
/** The object being flickered */
public var basic(default, null):FlxBasic;

/** Controls how the object flickers over time */
public var tweenFunction(default, null):(FlickerTween)->Bool;

/** Whether the object will show after the tween, defaults to `true` */
public var endVisibility(default, null):Bool = true;

/** How often, in seconds, the visibility cycles */
public var period(default, null):Float = 0.08;

/**
* The ratio of time the object will show, default is `0.5`,
* meaning equal times visible and invisible.
*/
public var ratio(default, null):Float = 0.5;

function new(options:FlickerTweenOptions, ?manager:FlxTweenManager):Void
{
tweenFunction = defaultTweenFunction;
if (options != null)
{
if (options.endVisibility != null)
endVisibility = options.endVisibility;

if (options.ratio != null)
ratio = options.ratio;

if (options.tweenFunction != null)
tweenFunction = options.tweenFunction;
}

super(options, manager);
}

/**
* Clean up references
*/
override function destroy()
{
super.destroy();
basic = null;
}

/**
* Flickers the desired object
*
* @param basic The object to flicker
* @param duration Duration of the tween, in seconds
* @param period How often, in seconds, the visibility cycles
*/
public function tween(basic:FlxBasic, duration:Float, period:Float):FlickerTween
{
this.basic = basic;
this.duration = duration;
this.period = period;

if (period <= 0.0)
{
this.period = 1.0 / FlxG.updateFramerate;
FlxG.log.warn('Cannot flicker with a period of 0.0 or less, using 1.0 / FlxG.updateFramerate, instead');
}

start();
return this;
}

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

if (tweenFunction != null && _secondsSinceStart >= _delayToUse)
{
final visible = tweenFunction(this);
// do not call setter every frame
if (basic.visible != visible)
basic.visible = visible;
}
}

override function onEnd()
{
super.onEnd();

basic.visible = endVisibility;
}

override function isTweenOf(object:Dynamic, ?field:String):Bool
{
return basic == object && (field == null || field == "visible" || field == "flicker");
}

/**
* The default tween function of flicker tweens
* @param tween The tween handling the flickering
*/
public static function defaultTweenFunction(tween:FlickerTween)
{
return (tween.time / tween.period) % 1 > tween.ratio;
}
}
3 changes: 3 additions & 0 deletions tests/coverage/Project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
<haxelib name="flixel-ui" />
<haxelib name="nape-haxe4" />
<haxelib name="spinehaxe" />
<!-- Allows better completion while editing unit tests in VSC -->
<haxelib name="munit" />
<haxelib name="hamcrest" />

<!-- ______________________________ Haxedefines _____________________________ -->

Expand Down
Loading

0 comments on commit 5ede2bd

Please sign in to comment.