From e05004da7d695411478803ce9a8b3416c0cef352 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Tue, 16 Apr 2024 14:24:41 -0500 Subject: [PATCH 1/6] add option for linear drag and maxSped --- flixel/FlxObject.hx | 77 +++++++++++---- flixel/math/FlxPoint.hx | 12 ++- flixel/math/FlxVelocity.hx | 190 ++++++++++++++++++++++++++++++------- 3 files changed, 229 insertions(+), 50 deletions(-) diff --git a/flixel/FlxObject.hx b/flixel/FlxObject.hx index fa268062cb..ca51e07715 100644 --- a/flixel/FlxObject.hx +++ b/flixel/FlxObject.hx @@ -650,16 +650,24 @@ class FlxObject extends FlxBasic */ public var acceleration(default, null):FlxPoint; + public var maxSpeedMode = FlxMovementType.NONE; + + public var dragMode = FlxMovementType.NONE; + public var dragApplyMode:FlxDragApplyMode = INERTIAL; + public var angularDragApplyMode:FlxDragApplyMode = INERTIAL; + /** * This isn't drag exactly, more like deceleration that is only applied * when `acceleration` is not affecting the sprite. */ + @:deprecated("drag is deprecated, use dragMode instead") public var drag(default, null):FlxPoint; /** * If you are using `acceleration`, you can use `maxVelocity` with it * to cap the speed automatically (very useful!). */ + @:deprecated("maxVelocity is deprecated, use maxSpeedMode instead") public var maxVelocity(default, null):FlxPoint; /** @@ -819,6 +827,7 @@ class FlxObject extends FlxBasic * Internal function for initialization of some object's variables. */ @:noCompletion + @:haxe.warning("-WDeprecated") function initVars():Void { flixelType = OBJECT; @@ -833,12 +842,15 @@ class FlxObject extends FlxBasic * Internal function for initialization of some variables that are used in `updateMotion()`. */ @:noCompletion + @:haxe.warning("-WDeprecated") inline function initMotionVars():Void { velocity = FlxPoint.get(); acceleration = FlxPoint.get(); - drag = FlxPoint.get(); - maxVelocity = FlxPoint.get(10000, 10000); + function setDrag(p:FlxPoint) dragMode = BILINEAR(p.x, p.y); + function setMaxSpeed(p:FlxPoint) maxSpeedMode = BILINEAR(p.x, p.y); + drag = new FlxCallbackPoint(setDrag, setDrag, setDrag); + maxVelocity = new FlxCallbackPoint(setMaxSpeed, setMaxSpeed, setMaxSpeed); } /** @@ -851,14 +863,15 @@ class FlxObject extends FlxBasic * Override this function to `null` out variables manually or call `destroy()` on class members if necessary. * Don't forget to call `super.destroy()`! */ + @:haxe.warning("-WDeprecated") override public function destroy():Void { super.destroy(); velocity = FlxDestroyUtil.put(velocity); acceleration = FlxDestroyUtil.put(acceleration); - drag = FlxDestroyUtil.put(drag); - maxVelocity = FlxDestroyUtil.put(maxVelocity); + drag = FlxDestroyUtil.destroy(drag); + maxVelocity = FlxDestroyUtil.destroy(maxVelocity); scrollFactor = FlxDestroyUtil.put(scrollFactor); last = FlxDestroyUtil.put(last); _point = FlxDestroyUtil.put(_point); @@ -900,18 +913,20 @@ class FlxObject extends FlxBasic angularVelocity += velocityDelta; angle += angularVelocity * elapsed; angularVelocity += velocityDelta; - - velocityDelta = 0.5 * (FlxVelocity.computeVelocity(velocity.x, acceleration.x, drag.x, maxVelocity.x, elapsed) - velocity.x); - velocity.x += velocityDelta; - var delta = velocity.x * elapsed; - velocity.x += velocityDelta; - x += delta; - - velocityDelta = 0.5 * (FlxVelocity.computeVelocity(velocity.y, acceleration.y, drag.y, maxVelocity.y, elapsed) - velocity.y); - velocity.y += velocityDelta; - delta = velocity.y * elapsed; - velocity.y += velocityDelta; - y += delta; + + final newVelocity = FlxPoint.get().copyFrom(velocity); + FlxVelocity.computeSpeed2D(elapsed, newVelocity, acceleration, maxSpeedMode, dragMode, dragApplyMode); + + final velocityDeltaX = 0.5 * (newVelocity.x - velocity.x); + final velocityDeltaY = 0.5 * (newVelocity.y - velocity.y); + velocity.x += velocityDeltaX; + velocity.y += velocityDeltaY; + x += velocity.x * elapsed; + y += velocity.y * elapsed; + velocity.x += velocityDeltaX; + velocity.y += velocityDeltaY; + + newVelocity.put(); } /** @@ -1542,3 +1557,33 @@ enum abstract CollisionDragType(Int) /** Drags when colliding with heavier objects. Immovable objects have infinite mass. */ var HEAVIER = 3; } + +enum FlxMovementType +{ + /** Along one axis, usually in the direction of movement */ + LINEAR(value:Float); + + /** Along two independant axes */ + BILINEAR(x:Float, y:Float); + + NONE; +} + +enum FlxDragApplyMode +{ + /** + * Drag is always applied to the object's velocity. + */ + ALWAYS; + + /** + * Drag is applied to objects in an "inertial" state, or, when the object has no acceleration. + */ + INERTIAL; + + /** + * Drag is applied when there is no acceleration on the object or if the object is accelerating + * in the opposite direction it is moving. + */ + SKID; +} \ No newline at end of file diff --git a/flixel/math/FlxPoint.hx b/flixel/math/FlxPoint.hx index 1943a95de3..4d0555ef92 100644 --- a/flixel/math/FlxPoint.hx +++ b/flixel/math/FlxPoint.hx @@ -794,7 +794,17 @@ import openfl.geom.Point; p.putWeak(); return dotProductWeak(normalized); } - + + /** + * Check if the angle between 2 vectors are less than 90 degrees + * + * @param p point to check + */ + public inline function areSameFacing(p:FlxPoint):Bool + { + return dotProduct(p) > 0; + } + /** * Check the perpendicularity of two points. * diff --git a/flixel/math/FlxVelocity.hx b/flixel/math/FlxVelocity.hx index b5b331a4de..60124b1f31 100644 --- a/flixel/math/FlxVelocity.hx +++ b/flixel/math/FlxVelocity.hx @@ -1,5 +1,6 @@ package flixel.math; +import flixel.FlxObject; import flixel.FlxSprite; #if FLX_TOUCH import flixel.input.touch.FlxTouch; @@ -222,49 +223,172 @@ class FlxVelocity /** * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. * - * @param Velocity Any component of velocity (e.g. 20). - * @param Acceleration Rate at which the velocity is changing. - * @param Drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. - * @param Max An absolute value cap for the velocity (0 for no cap). - * @param Elapsed The amount of time passed in to the latest update cycle - * @return The altered Velocity value. + * @param speed Any component of velocity (e.g. 20). + * @param acceleration Rate at which the velocity is changing. + * @param drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. + * @param max An absolute value cap for the velocity (0 for no cap). + * @param elapsed The amount of time passed in to the latest update cycle + * @return The altered Velocity value. */ - public static function computeVelocity(Velocity:Float, Acceleration:Float, Drag:Float, Max:Float, Elapsed:Float):Float + public static function computeVelocity(speed:Float, acceleration:Float, drag:Float, max:Float, elapsed:Float):Float { - if (Acceleration != 0) - { - Velocity += Acceleration * Elapsed; - } - else if (Drag != 0) + speed = computeSpeed1D(elapsed, speed, acceleration, drag, FlxDragApplyMode.INERTIAL); + + return capSpeed1D(speed, max); + } + + public static function capSpeed1D(speed:Float, max:Float):Float + { + if (speed != 0 && max > 0) { - var drag:Float = Drag * Elapsed; - if (Velocity - drag > 0) - { - Velocity -= drag; - } - else if (Velocity + drag < 0) + if (speed > max) { - Velocity += drag; + speed = max; } - else + else if (speed < -max) { - Velocity = 0; + speed = -max; } } - if ((Velocity != 0) && (Max != 0)) + + return speed; + } + + /** + * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. + * + * @param elapsed The amount of time passed in to the latest update cycle + * @param speed Any component of velocity (e.g. 20). + * @param acceleration Rate at which the velocity is changing. + * @param drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. + * @return The altered Velocity value. + */ + public static function computeSpeed1D(elapsed:Float, speed:Float, acceleration:Float, + drag:Float, dragApply:FlxDragApplyMode):Float + { + final applyDrag = drag > 0 && switch(dragApply) { - if (Velocity > Max) - { - Velocity = Max; - } - else if (Velocity < -Max) - { - Velocity = -Max; - } + case ALWAYS: + true; + case INERTIAL: + acceleration == 0; + case SKID: + // Apply drag if accelerating the opposite direction of current movement + acceleration == 0 || ((acceleration < 0) == (speed < 0)); + } - return Velocity; + + if (acceleration != 0) + { + speed += acceleration * elapsed; + } + + if (applyDrag) + { + speed = applyDrag1D(elapsed, speed, drag); + } + + return speed; } - + + public static function applyDrag1D(elapsed:Float, speed:Float, drag:Float):Float + { + final frameDrag = drag * elapsed; + if (speed - frameDrag > 0) + { + speed -= frameDrag; + } + else if (speed + frameDrag < 0) + { + speed += frameDrag; + } + else + { + speed = 0; + } + + return speed; + } + + /** + * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. + * + * @param elapsed The amount of time passed in to the latest update cycle + * @param velocity Any component of velocity (e.g. 20). + * @param acceleration Rate at which the velocity is changing. + * @param drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. + * @return The altered Velocity value. + */ + public static function computeSpeed2D(elapsed:Float, velocity:FlxPoint, acceleration:FlxPoint, + max:FlxMovementType, drag:FlxMovementType, dragApply:FlxDragApplyMode) + { + switch(drag) + { + case NONE: + + velocity.x += elapsed * acceleration.x; + velocity.y += elapsed * acceleration.y; + + case BILINEAR(dragX, dragY): + + velocity.x = computeSpeed1D(elapsed, velocity.x, acceleration.x, dragX, dragApply); + velocity.y = computeSpeed1D(elapsed, velocity.y, acceleration.y, dragY, dragApply); + + case LINEAR(linearDrag): + + final applyDrag = linearDrag > 0 && switch(dragApply) + { + case ALWAYS: + true; + case INERTIAL: + acceleration.isZero(); + case SKID: + // Apply drag unless if accelerating in the direction of movement (under ±90 degrees) + acceleration.isZero() || !acceleration.areSameFacing(velocity); + + } + + if (!acceleration.isZero()) + { + velocity.x += acceleration.x * elapsed; + velocity.y += acceleration.y * elapsed; + } + + if (applyDrag) + { + final speed = velocity.length; + final scale = applyDrag1D(elapsed, speed, linearDrag) / speed; + velocity.x *= scale; + velocity.y *= scale; + } + } + + return capSpeed2D(velocity, max); + } + + public static function capSpeed2D(velocity:FlxPoint, max:FlxMovementType) + { + switch(max) + { + case NONE: + case BILINEAR(maxX, maxY): + + velocity.x = capSpeed1D(velocity.x, maxX); + velocity.y = capSpeed1D(velocity.y, maxY); + + case LINEAR(max): + + final speed = velocity.length; + if (speed > max) + { + velocity.x *= max / speed; + velocity.y *= max / speed; + } + } + + return velocity; + } + /** * Sets the x/y acceleration on the source FlxSprite so it will accelerate in the direction of the specified angle. * You must give a maximum speed value (in pixels per second), beyond which the FlxSprite won't go any faster. @@ -284,6 +408,6 @@ class FlxVelocity source.velocity.set(0, 0); source.acceleration.set(cosA * acceleration, sinA * acceleration); - source.maxVelocity.set(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); + source.maxSpeedMode = BILINEAR(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); } } From c352939f2305b8a92f00c6c60e6cc7962144c565 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Tue, 16 Apr 2024 14:34:35 -0500 Subject: [PATCH 2/6] use XY instead of BILINEAR --- flixel/FlxObject.hx | 6 +++--- flixel/math/FlxVelocity.hx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/flixel/FlxObject.hx b/flixel/FlxObject.hx index ca51e07715..d31cd5d577 100644 --- a/flixel/FlxObject.hx +++ b/flixel/FlxObject.hx @@ -847,8 +847,8 @@ class FlxObject extends FlxBasic { velocity = FlxPoint.get(); acceleration = FlxPoint.get(); - function setDrag(p:FlxPoint) dragMode = BILINEAR(p.x, p.y); - function setMaxSpeed(p:FlxPoint) maxSpeedMode = BILINEAR(p.x, p.y); + function setDrag(p:FlxPoint) dragMode = XY(p.x, p.y); + function setMaxSpeed(p:FlxPoint) maxSpeedMode = XY(p.x, p.y); drag = new FlxCallbackPoint(setDrag, setDrag, setDrag); maxVelocity = new FlxCallbackPoint(setMaxSpeed, setMaxSpeed, setMaxSpeed); } @@ -1564,7 +1564,7 @@ enum FlxMovementType LINEAR(value:Float); /** Along two independant axes */ - BILINEAR(x:Float, y:Float); + XY(x:Float, y:Float); NONE; } diff --git a/flixel/math/FlxVelocity.hx b/flixel/math/FlxVelocity.hx index 60124b1f31..760d0b788e 100644 --- a/flixel/math/FlxVelocity.hx +++ b/flixel/math/FlxVelocity.hx @@ -329,7 +329,7 @@ class FlxVelocity velocity.x += elapsed * acceleration.x; velocity.y += elapsed * acceleration.y; - case BILINEAR(dragX, dragY): + case XY(dragX, dragY): velocity.x = computeSpeed1D(elapsed, velocity.x, acceleration.x, dragX, dragApply); velocity.y = computeSpeed1D(elapsed, velocity.y, acceleration.y, dragY, dragApply); @@ -371,7 +371,7 @@ class FlxVelocity switch(max) { case NONE: - case BILINEAR(maxX, maxY): + case XY(maxX, maxY): velocity.x = capSpeed1D(velocity.x, maxX); velocity.y = capSpeed1D(velocity.y, maxY); @@ -408,6 +408,6 @@ class FlxVelocity source.velocity.set(0, 0); source.acceleration.set(cosA * acceleration, sinA * acceleration); - source.maxSpeedMode = BILINEAR(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); + source.maxSpeedMode = XY(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); } } From 74d23d480ec08aa8cb2dad9fd53bbcfd1b19d20e Mon Sep 17 00:00:00 2001 From: George FunBook Date: Tue, 16 Apr 2024 14:46:13 -0500 Subject: [PATCH 3/6] docs --- flixel/FlxObject.hx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/flixel/FlxObject.hx b/flixel/FlxObject.hx index d31cd5d577..4f8ac7e5f7 100644 --- a/flixel/FlxObject.hx +++ b/flixel/FlxObject.hx @@ -1566,24 +1566,23 @@ enum FlxMovementType /** Along two independant axes */ XY(x:Float, y:Float); + /** Shortcut for 0 */ NONE; } enum FlxDragApplyMode { - /** - * Drag is always applied to the object's velocity. - */ + /** Drag is always applied to the object's velocity */ ALWAYS; - /** - * Drag is applied to objects in an "inertial" state, or, when the object has no acceleration. - */ + /** Drag is applied to objects in an "inertial" state (not accelerating) */ INERTIAL; /** - * Drag is applied when there is no acceleration on the object or if the object is accelerating - * in the opposite direction it is moving. + * Drag is to objects not accelerating in the direction they are moving, including + * objects in an inertial state. For a `dragmode` of `XY` this applies to both + * axes separately, for `LINEAR`, drag is applied if the object is not accelerating + * less than 90 degrees from the object's current movement direction */ SKID; } \ No newline at end of file From 14e46346251cc9f4f65e1f7ce56621f5c1cffef7 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Tue, 16 Apr 2024 15:20:42 -0500 Subject: [PATCH 4/6] don't deprecate drag and maxVelocity --- flixel/FlxObject.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixel/FlxObject.hx b/flixel/FlxObject.hx index 4f8ac7e5f7..245907ec03 100644 --- a/flixel/FlxObject.hx +++ b/flixel/FlxObject.hx @@ -660,14 +660,14 @@ class FlxObject extends FlxBasic * This isn't drag exactly, more like deceleration that is only applied * when `acceleration` is not affecting the sprite. */ - @:deprecated("drag is deprecated, use dragMode instead") + // @:deprecated("drag is deprecated, use dragMode instead") public var drag(default, null):FlxPoint; /** * If you are using `acceleration`, you can use `maxVelocity` with it * to cap the speed automatically (very useful!). */ - @:deprecated("maxVelocity is deprecated, use maxSpeedMode instead") + // @:deprecated("maxVelocity is deprecated, use maxSpeedMode instead") public var maxVelocity(default, null):FlxPoint; /** From a51d653d8c2413836af8b55d4ceeb1cd5f1ad8b1 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Tue, 16 Apr 2024 21:54:48 -0500 Subject: [PATCH 5/6] fox unit tests --- flixel/math/FlxVelocity.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixel/math/FlxVelocity.hx b/flixel/math/FlxVelocity.hx index 760d0b788e..77e2accd89 100644 --- a/flixel/math/FlxVelocity.hx +++ b/flixel/math/FlxVelocity.hx @@ -408,6 +408,6 @@ class FlxVelocity source.velocity.set(0, 0); source.acceleration.set(cosA * acceleration, sinA * acceleration); - source.maxSpeedMode = XY(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); + source.maxVeloctity.set(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); } } From 71065fda5c9c5d1751f93189a9c434232c6683a9 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Sun, 21 Apr 2024 16:03:19 -0400 Subject: [PATCH 6/6] separate maxSpeed and drag types --- flixel/FlxObject.hx | 29 ++++++++++++++++++++--------- flixel/math/FlxVelocity.hx | 14 +++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/flixel/FlxObject.hx b/flixel/FlxObject.hx index 245907ec03..05394e45d9 100644 --- a/flixel/FlxObject.hx +++ b/flixel/FlxObject.hx @@ -650,10 +650,9 @@ class FlxObject extends FlxBasic */ public var acceleration(default, null):FlxPoint; - public var maxSpeedMode = FlxMovementType.NONE; + public var maxSpeedMode = FlxMaxSpeedMode.NONE; - public var dragMode = FlxMovementType.NONE; - public var dragApplyMode:FlxDragApplyMode = INERTIAL; + public var dragMode = FlxDragMode.NONE; public var angularDragApplyMode:FlxDragApplyMode = INERTIAL; /** @@ -847,7 +846,7 @@ class FlxObject extends FlxBasic { velocity = FlxPoint.get(); acceleration = FlxPoint.get(); - function setDrag(p:FlxPoint) dragMode = XY(p.x, p.y); + function setDrag(p:FlxPoint) dragMode = XY(p.x, p.y, INERTIAL, INERTIAL); function setMaxSpeed(p:FlxPoint) maxSpeedMode = XY(p.x, p.y); drag = new FlxCallbackPoint(setDrag, setDrag, setDrag); maxVelocity = new FlxCallbackPoint(setMaxSpeed, setMaxSpeed, setMaxSpeed); @@ -915,7 +914,7 @@ class FlxObject extends FlxBasic angularVelocity += velocityDelta; final newVelocity = FlxPoint.get().copyFrom(velocity); - FlxVelocity.computeSpeed2D(elapsed, newVelocity, acceleration, maxSpeedMode, dragMode, dragApplyMode); + FlxVelocity.computeSpeed2D(elapsed, newVelocity, acceleration, maxSpeedMode, dragMode); final velocityDeltaX = 0.5 * (newVelocity.x - velocity.x); final velocityDeltaY = 0.5 * (newVelocity.y - velocity.y); @@ -1558,15 +1557,27 @@ enum abstract CollisionDragType(Int) var HEAVIER = 3; } -enum FlxMovementType +enum FlxMaxSpeedMode { - /** Along one axis, usually in the direction of movement */ + /** The magnitude of velocity is capped */ LINEAR(value:Float); - /** Along two independant axes */ + /** Each axis is capped independantly */ XY(x:Float, y:Float); - /** Shortcut for 0 */ + /** No max speed */ + NONE; +} + +enum FlxDragMode +{ + /** Along one axis, usually in the direction of movement */ + UNIFORM(value:Float, applyMode:FlxDragApplyMode); + + /** Drag is applied to each axes separately */ + XY(x:Float, y:Float, xApplyMode:FlxDragApplyMode, ?yApplyMode:FlxDragApplyMode); + + /** No drag */ NONE; } diff --git a/flixel/math/FlxVelocity.hx b/flixel/math/FlxVelocity.hx index 77e2accd89..e11edab252 100644 --- a/flixel/math/FlxVelocity.hx +++ b/flixel/math/FlxVelocity.hx @@ -320,7 +320,7 @@ class FlxVelocity * @return The altered Velocity value. */ public static function computeSpeed2D(elapsed:Float, velocity:FlxPoint, acceleration:FlxPoint, - max:FlxMovementType, drag:FlxMovementType, dragApply:FlxDragApplyMode) + max:FlxMaxSpeedMode, drag:FlxDragMode) { switch(drag) { @@ -329,12 +329,12 @@ class FlxVelocity velocity.x += elapsed * acceleration.x; velocity.y += elapsed * acceleration.y; - case XY(dragX, dragY): + case XY(dragX, dragY, applyX, applyY): - velocity.x = computeSpeed1D(elapsed, velocity.x, acceleration.x, dragX, dragApply); - velocity.y = computeSpeed1D(elapsed, velocity.y, acceleration.y, dragY, dragApply); + velocity.x = computeSpeed1D(elapsed, velocity.x, acceleration.x, dragX, applyX); + velocity.y = computeSpeed1D(elapsed, velocity.y, acceleration.y, dragY, applyY != null ? applyY : applyX); - case LINEAR(linearDrag): + case UNIFORM(linearDrag, dragApply): final applyDrag = linearDrag > 0 && switch(dragApply) { @@ -366,7 +366,7 @@ class FlxVelocity return capSpeed2D(velocity, max); } - public static function capSpeed2D(velocity:FlxPoint, max:FlxMovementType) + public static function capSpeed2D(velocity:FlxPoint, max:FlxMaxSpeedMode) { switch(max) { @@ -408,6 +408,6 @@ class FlxVelocity source.velocity.set(0, 0); source.acceleration.set(cosA * acceleration, sinA * acceleration); - source.maxVeloctity.set(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); + source.maxVelocity.set(Math.abs(cosA * maxSpeed), Math.abs(sinA * maxSpeed)); } }