Magic-4e
Magic-4e

Reputation: 71

How do I build up towards a velocity by using add force?

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

Answers (1)

Magic-4e
Magic-4e

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

Related Questions