From e05004da7d695411478803ce9a8b3416c0cef352 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Tue, 16 Apr 2024 14:24:41 -0500 Subject: [PATCH] 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)); } }