Reputation: 71
I have bean trying to create a script to move an object with Rigidbody.AddForce
.
My goal is to reach a specified velocity by just adding force.
I have some if statements that allow three scenarios:
However I wasn't able to create the desired outcome.
Currently as soon as it comes close to the desired velocity it jumps between above and below the desired velocity rapidly.
I think this is because my calculation to predict the next velocity is not accurate.
I was trying to tweak it in multiple ways but I simply can't figure out how to get it right.
It may be possible that there is a different cause to my problem, but if so, I can't find it.
Anyway I put my code below.
using System.Collections.Generic;
using UnityEngine;
public partial class PlayerController : MonoBehaviour
{
//*Public
//Targeted Objects
/// <summary>The camera stand/parent/folder that is used for left and right
/// rotations</summary>
public GameObject cameraPivotRL;
/// <summary>The camera stand/parent/folder that is used for gravity related
/// rotations</summary>
public GameObject cameraGravityPivot;
//Target scripts
/// <summary>A script that reads inputs</summary>
public PlayerInputs playerInputs;
//User settings
//speed settings
/// <summary>The amount the player accelerates</summary>
public float accelerationForce;
/// <summary>The amount the player decelerates</summary>
public float decelerationForce;
/// <summary>The players desired velocity</summary>
public float desiredVelocity;
/// <summary>How fast the velocity will reach the desired velocity after
/// accelerating or decelerating</summary>
public float approximationSpeed;
//*Private
//Inputs
/// <summary>Directional inputs used for Moving the character</summary>
private Vector2 moveInput;
/// <summary>Magnitude/lengt of the moveInput Vector</summary>
private float moveInputMagni;
//Camera pivot R/L Vectors (I use the camera stand so it's not influenced by
//up/down rotations)
/// <summary>The forward vector of the camera</summary>
private Vector3 camF;
/// <summary>The right vector of the camera</summary>
private Vector3 camR;
//Gravity pivot Vector
/// <summary>The vector opposed to the gravity direction</summary>
private Vector3 gravityPivot;
//Physics
/// <summary>The Unity component which stores and processes the players
/// physics</summary>
private Rigidbody body;
/// <summary>The players current velocity</summary>
private float bodyVelocityMagnitude;
/// <summary>The portion of the players velocity that is going in the desired
/// direction</summary>
private float veloInDesiredDirectionMagni;
/// <summary>The velocity the player is predicted to have after applying the
/// acceleration force</summary>
private float predictedVelocity;
//Debug bools
private bool isAccelerating = false;
private float isAcceleratingForce;
private bool isDecelerating = false;
private float isDeceleratingForce;
private bool isMaximizing = false;
private float isMaximizingForce;
/// <summary>The force that will be applied</summary>
private float desiredForceMagni;
/// <summary>The amount of velocity that will be added after force is
/// applied</summary>
private float addedVeloMagni;
/// <summary>The velocity from the previous step</summary>
private float velocityMagniMem;
/// <summary>The difference betwean the current velocity and the previous
/// velocity</summary>
private float velocityChange;
/// <summary>a simple bool to keep track of when this code is ran</summary>
private bool stepCheck = false;
//#### Unity Functions ####
//## Awake is called when this object is spawned
private void Awake()
{
//Get rigidbody
body = transform.GetComponent<Rigidbody>();
//Get input script
playerInputs = GetComponent<PlayerInputs>();
}
//## Fixed update is called 50 times per second
private void FixedUpdate()
{
//Reset debug bools
isAccelerating = false;
isDecelerating = false;
isMaximizing = false;
//Draw current velocity
Debug.DrawRay(transform.position + new Vector3(0, 0.1f, 0),
body.velocity * 0.1f, Color.white);
//Measure the current velocity
bodyVelocityMagnitude = body.velocity.magnitude;
//Get move inputs
moveInput = playerInputs.moveInput;
//* Main move script block
if (moveInput.magnitude > 0)
{
//Get camera axis
camF = cameraPivotRL.transform.forward;
camR = cameraPivotRL.transform.right;
//Get the current gravity direction
gravityPivot = cameraGravityPivot.transform.up;
//Turn inputs into a direction relative to the camera
var desiredDirection = (camF * moveInput.y) + (camR * moveInput.x);
//Determine the dirctional force that will be applied
var desiredForce = desiredDirection * accelerationForce;
//Calculate how much velocity will be added after the force is applied
var addedVelo = desiredForce.normalized * (desiredForce.magnitude
- (body.velocity.magnitude * body.drag)) / body.mass
* Time.fixedDeltaTime;
//Calculate how much of the current velocity is going in the desired
//direction
var veloInDesiredDirection = body.velocity - (body.velocity
- desiredDirection.normalized * Vector3.Dot(body.velocity,
desiredDirection.normalized));
//Make the desired velocity shrink/grow based on the move inputs
//strengt
var desiredVelocityTemp = desiredVelocity * moveInput.magnitude;
//Predict future velocity and add/subtract force accordingly
//will velocity in the desired direction be greater or ecual to
//desired velocity after aplying force?
if ((addedVelo + veloInDesiredDirection).magnitude
>= desiredVelocityTemp && veloInDesiredDirection.magnitude
>= desiredVelocityTemp && Vector3.Dot(body.velocity,
desiredDirection.normalized) > 0)
{
//will velocity in the desired direction still be greater than
//desired velocity after aplying negative force?
if (((addedVelo * -1) + veloInDesiredDirection).magnitude
> desiredVelocityTemp)
{
//apply force in oposite direction of desired direction
body.AddForce(desiredForce * -1, ForceMode.Acceleration);
//a peace of code to measure how much the velocity changed
//after 1 step
if (stepCheck && velocityMagniMem > 0)
{
velocityChange = body.velocity.magnitude
- velocityMagniMem;
stepCheck = false;
}
else
{
velocityMagniMem = body.velocity.magnitude;
stepCheck = true;
}
//Debug
//draw ray that represents the negative force that is applied
//to the player
Debug.DrawRay(transform.position, desiredForce * -1 * 0.005f,
Color.cyan);
//this code is running
isDecelerating = true;
//measure force
isDeceleratingForce = desiredForce.magnitude * -1;
}
else
{
//calculate force requered to reach and maintain velocity
var requiForce = (desiredVelocityTemp * 1.4056579f
+ 20.6009236f) * (((desiredVelocityTemp
- (desiredDirection.normalized * Vector3.Dot(
body.velocity, desiredDirection.normalized)).magnitude)
* approximationSpeed) + 1);
//apply force needed to reach and maintain desired velocity
body.AddForce(desiredForce.normalized * requiForce,
ForceMode.Acceleration);
//Debug
//draw ray that represents the negative force that is applied
//to the player
Debug.DrawRay(transform.position, desiredForce.normalized
* requiForce * 0.005f, Color.green);
//this code is running
isMaximizing = true;
//measure force
isMaximizingForce = requiForce;
}
}
else
{
//apply force in desired direction
body.AddForce(desiredForce, ForceMode.Acceleration);
//a peace of code to measure how much the velocity changed after
//1 step
if (stepCheck && velocityMagniMem > 0)
{
velocityChange = body.velocity.magnitude - velocityMagniMem;
stepCheck = false;
}
else
{
velocityMagniMem = body.velocity.magnitude;
stepCheck = true;
}
//Debug
//draw ray that represents the negative force that is applied to
//the player
Debug.DrawRay(transform.position, desiredForce * 0.005f,
Color.red);
//this code is running
isAccelerating = true;
//measure force
isAcceleratingForce = desiredForce.magnitude;
}
//Code to turn player model
//if velocity is detected use velocity to turn player model
if(body.velocity.magnitude > 0.01f)
{
transform.rotation = Quaternion.LookRotation(camF * Vector3.Dot(
camF, body.velocity.normalized) + camR * Vector3.Dot(camR,
body.velocity.normalized), gravityPivot);
}
//if not use inputs
else if(moveInput.magnitude > 0)
{
transform.rotation = Quaternion.LookRotation((camF * moveInput.y)
+ (camR * moveInput.x), gravityPivot);
}
//Debug variables
//the strengt of the applied force
desiredForceMagni = desiredForce.magnitude;
//the amount of velocity that is predicted to be added after aplying
//force
addedVeloMagni = addedVelo.magnitude;
//the current velocity strengt that is in the same direction as the
//desired direction
veloInDesiredDirectionMagni = veloInDesiredDirection.magnitude;
//the velocity streng that is predected player is predicted to have in
//the desired direction after force is applied
predictedVelocity = (addedVelo + veloInDesiredDirection).magnitude;
//draw predicted velocity
Debug.DrawRay(transform.position + new Vector3(0, 0.2f, 0),
addedVelo.normalized * predictedVelocity * 0.1f,
Color.magenta);
//draw the velocity in the desired direction
Debug.DrawRay(transform.position + new Vector3(0, 0.1f, 0),
veloInDesiredDirection * 0.1f, Color.yellow);
//draw a line betwean the normal velocity and the velocity in the
//desired direction
Debug.DrawLine(transform.position + new Vector3(0, 0.1f, 0)
+ (veloInDesiredDirection * 0.1f), transform.position
+ new Vector3(0, 0.1f, 0) + (body.velocity * 0.1f),
Color.gray);
}
}
}
Upvotes: 3
Views: 1611
Reputation: 71
I have found what I was looking for. So here for everyone else struggling with this:
//Set a target velocity
Vector3 targetVelocity = moveDirection * maxSpeed;
//Find the change of velocity needed to reach target
Vector3 velocityChange = targetVelocity - rb.velocity;
//Convert to acceleration, which is change of velocity over time
Vector3 acceleration = velocityChange / Time.fixedDeltaTime;
//Clamp it to your maximum acceleration magnitude
acceleration = Vector3.ClampMagnitude(acceleration, maxAcceleration);
//Then AddForce
rb.AddForce(acceleration, ForceMode.Acceleration);
I hope this will help someone else too.
Upvotes: 4