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

Allow linear drag and max speed, drag on skid #3110

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
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
87 changes: 71 additions & 16 deletions flixel/FlxObject.hx
Original file line number Diff line number Diff line change
Expand Up @@ -650,16 +650,23 @@ class FlxObject extends FlxBasic
*/
public var acceleration(default, null):FlxPoint;

public var maxSpeedMode = FlxMaxSpeedMode.NONE;

public var dragMode = FlxDragMode.NONE;
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 +826,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 +841,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 = 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);
}

/**
Expand All @@ -851,14 +862,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 +912,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);

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 +1556,44 @@ enum abstract CollisionDragType(Int)
/** Drags when colliding with heavier objects. Immovable objects have infinite mass. */
var HEAVIER = 3;
}

enum FlxMaxSpeedMode
{
/** The magnitude of velocity is capped */
LINEAR(value:Float);

/** Each axis is capped independantly */
XY(x:Float, y:Float);

/** 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;
}

enum FlxDragApplyMode
{
/** Drag is always applied to the object's velocity */
ALWAYS;

/** Drag is applied to objects in an "inertial" state (not accelerating) */
INERTIAL;

/**
* 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;
}
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
188 changes: 156 additions & 32 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:FlxMaxSpeedMode, drag:FlxDragMode)
{
switch(drag)
{
case NONE:

velocity.x += elapsed * acceleration.x;
velocity.y += elapsed * acceleration.y;

case XY(dragX, dragY, applyX, applyY):

velocity.x = computeSpeed1D(elapsed, velocity.x, acceleration.x, dragX, applyX);
velocity.y = computeSpeed1D(elapsed, velocity.y, acceleration.y, dragY, applyY != null ? applyY : applyX);

case UNIFORM(linearDrag, dragApply):

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:FlxMaxSpeedMode)
{
switch(max)
{
case NONE:
case XY(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 Down
Loading