Skip to content
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

add linearDrag, maxLinearVelocity and drag modes #2704

Draft
wants to merge 4 commits 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
146 changes: 134 additions & 12 deletions flixel/FlxObject.hx
Original file line number Diff line number Diff line change
Expand Up @@ -644,17 +644,53 @@ class FlxObject extends FlxBasic
public var acceleration(default, null):FlxPoint;

/**
* This isn't drag exactly, more like deceleration that is only applied
* when `acceleration` is not affecting the sprite.
* Determines when `drag.x/y` is applied to `velocity.x/y`.
* @since 5.1.0
*/
public var dragMode(default, null):FlxDragMode2D;

/**
* Determines when `angularDrag` is applied to `angularVelocity`.
* @since 5.1.0
*/
public var angularDragMode:FlxDragMode = INERTIAL;

/**
* Determines when `linearDrag` is applied to `velocity`.
* @since 5.1.0
*/
public var linearDragMode:FlxDragMode = ALWAYS;

/**
* Not conventional drag, more like conditional deceleration. When applied, velocity will
* reduce (or move closer to 0) by this amount (in pixels per second).
* Use `linearDrag` to apply drag on velocity's overall magnitude.
*
* Note: You can use `dragMode.x` and `dragMode.y` to determine when drag is applied.
*/
public var drag(default, null):FlxPoint;

/**
* If you are using `acceleration`, you can use `maxVelocity` with it
* to cap the speed automatically (very useful!).
* The maximum velocity of this object in both X and Y axes.
* Use `maxLinearVelocity` to limit velocity's overall magnitude.
*/
public var maxVelocity(default, null):FlxPoint;

/**
* This isn't drag exactly, more like deceleration that is only applied
* when `acceleration` is not affecting the sprite.
* Use `drag` to apply drag on each axis.
* @since 5.1.0
*/
public var linearDrag(default, null):Float = 0;

/**
* The maximum velocity of this object, in terms of overall magnitude.
* Use `maxVelocity` to limit velocity on each axis.
* @since 5.1.0
*/
public var maxLinearVelocity(default, null):Float = 10000;

/**
* Important variable for collision processing.
* By default this value is set automatically during at the start of `update()`.
Expand Down Expand Up @@ -828,7 +864,12 @@ class FlxObject extends FlxBasic
velocity = FlxPoint.get();
acceleration = FlxPoint.get();
drag = FlxPoint.get();
dragMode = new FlxDragMode2D(INERTIAL, INERTIAL);
linearDragMode = ALWAYS;
angularDragMode = INERTIAL;
maxVelocity = FlxPoint.get(10000, 10000);
maxLinearVelocity = 10000;
linearDrag = 0;
}

/**
Expand Down Expand Up @@ -886,22 +927,69 @@ class FlxObject extends FlxBasic
@:noCompletion
function updateMotion(elapsed:Float):Void
{
var velocityDelta = 0.5 * (FlxVelocity.computeVelocity(angularVelocity, angularAcceleration, angularDrag, maxAngular, elapsed) - angularVelocity);
final newAngularVelocity = FlxVelocity.computeVelocity(angularVelocity, angularAcceleration, angularDrag, angularDragMode, maxAngular, elapsed);

final newVelocity = FlxPoint.get();
newVelocity.x = FlxVelocity.computeVelocity(velocity.x, acceleration.x, drag.x, dragMode.x, maxVelocity.x, elapsed);
newVelocity.y = FlxVelocity.computeVelocity(velocity.y, acceleration.y, drag.y, dragMode.y, maxVelocity.y, elapsed);

computeLinearVelocity(newVelocity, acceleration, linearDrag, linearDragMode, maxLinearVelocity, elapsed);

var velocityDelta = 0.5 * (newAngularVelocity - angularVelocity);
angularVelocity += velocityDelta;
angle += angularVelocity * elapsed;
angularVelocity += velocityDelta;

velocityDelta = 0.5 * (FlxVelocity.computeVelocity(velocity.x, acceleration.x, drag.x, maxVelocity.x, elapsed) - velocity.x);
velocityDelta = 0.5 * (newVelocity.x - velocity.x);
velocity.x += velocityDelta;
var delta = velocity.x * elapsed;
x += velocity.x * elapsed;
velocity.x += velocityDelta;
x += delta;

velocityDelta = 0.5 * (FlxVelocity.computeVelocity(velocity.y, acceleration.y, drag.y, maxVelocity.y, elapsed) - velocity.y);
velocityDelta = 0.5 * (newVelocity.y - velocity.y);
velocity.y += velocityDelta;
delta = velocity.y * elapsed;
y += velocity.y * elapsed;
velocity.y += velocityDelta;
y += delta;
}

function computeLinearVelocity(velocity:FlxPoint, acceleration:FlxPoint, drag:Float, dragMode:FlxDragMode, max:Float, elapsed:Float)
{
// apply linearDrag
if (drag > 0 && !velocity.isZero())
{
// determine whether to apply drag
final applyDrag = switch(dragMode)
{
case ALWAYS:
true;
case INERTIAL:
acceleration.isZero();
case SKID:
acceleration.isZero() || acceleration.dotProduct(velocity) < 0;
}

if (applyDrag)
{
final frameDrag = elapsed * drag;
final len = velocity.length;
if (len < frameDrag)
velocity.set(0, 0);
else
velocity.length = len - frameDrag;
}
}

// limit velocity by maxLinearVelocity
if (!velocity.isZero() && max > 0)
{
// if velocity exceeds maxLinearVelocity
final vLengthSquared = velocity.lengthSquared;
if (vLengthSquared > max * max)
{
// reduce velocity length to limit
final scale = max / Math.sqrt(vLengthSquared);
velocity.scale(scale, scale);
}
}
}

/**
Expand Down Expand Up @@ -1512,6 +1600,40 @@ class FlxObject extends FlxBasic
}
}

class FlxDragMode2D
{
public var x:FlxDragMode;
public var y:FlxDragMode;

public inline function new (x = INERTIAL, y = INERTIAL)
{
this.x = x;
this.y = y;
}
}

/**
* Determines when drag is applied to an object on a single x or y axis.
*/
enum FlxDragMode
{
/**
* 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;
}

/**
* Determines when to apply collision drag to one object that collided with another.
*/
Expand Down
58 changes: 36 additions & 22 deletions flixel/math/FlxVelocity.hx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package flixel.math;

import flixel.FlxObject;
import flixel.FlxSprite;
#if FLX_TOUCH
import flixel.input.touch.FlxTouch;
Expand Down Expand Up @@ -222,47 +223,60 @@ 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 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 reduces, according to `dragMode`.
* @param dragMode Determines when drag is applied.
* @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(velocity:Float, acceleration:Float, drag:Float, dragMode:FlxDragMode, max:Float, elapsed:Float):Float
{
if (Acceleration != 0)
if (acceleration != 0)
{
Velocity += Acceleration * Elapsed;
velocity += acceleration * elapsed;
}
else if (Drag != 0)

final applyDrag = switch(dragMode)
{
var drag:Float = Drag * Elapsed;
if (Velocity - drag > 0)
case ALWAYS:
true;
case INERTIAL:
acceleration == 0;
case SKID:
acceleration == 0 || ((acceleration < 0) == (velocity < 0));
}

if (drag != 0 && applyDrag)
{
final frameDrag = drag * elapsed;
if (velocity - frameDrag > 0)
{
Velocity -= drag;
velocity -= frameDrag;
}
else if (Velocity + drag < 0)
else if (velocity + frameDrag < 0)
{
Velocity += drag;
velocity += frameDrag;
}
else
{
Velocity = 0;
velocity = 0;
}
}
if ((Velocity != 0) && (Max != 0))

if ((velocity != 0) && (max != 0))
{
if (Velocity > Max)
if (velocity > max)
{
Velocity = Max;
velocity = max;
}
else if (Velocity < -Max)
else if (velocity < -max)
{
Velocity = -Max;
velocity = -max;
}
}
return Velocity;
return velocity;
}

/**
Expand Down