Skip to content

Commit

Permalink
add option for linear drag and maxSped
Browse files Browse the repository at this point in the history
  • Loading branch information
Geokureli committed Apr 16, 2024
1 parent 79cb061 commit e05004d
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 50 deletions.
77 changes: 61 additions & 16 deletions flixel/FlxObject.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

/**
Expand All @@ -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);
Expand Down Expand Up @@ -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();
}

/**
Expand Down Expand Up @@ -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;
}
12 changes: 11 additions & 1 deletion flixel/math/FlxPoint.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
190 changes: 157 additions & 33 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,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.
Expand All @@ -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));
}
}

0 comments on commit e05004d

Please sign in to comment.