Search code examples
c#unity-game-enginegame-physics

What formula would I use to relate velocity to rotation I Unity?


I'm messing around in Unity trying to create a script for a physics based arrow. I have it arcing properly, but it's still straight up throughout it's flight. I'm trying to use Quaternion.LookRotation to change it's rotation based on the velocity (R = 0 at V = Vinitial, R = 90 at V = 0, and R = 180 at V = -Vinitial) but I haven't been able to puzzle out what formula I would use. Any help would be appreciated.

EDIT

With help from the responses, I was able to get it working as I intended. Incase anyone stumbles across this looking for help, my end code looked like this:

private void Update()
{
    if (ForceSet)
    {
        ForceSet = false;
        initialVelocity = rb.velocity;
        EndAngleOffset = .33f * rb.velocity.y;
    }
    if (stuckTarget == null)
    {
        Vector3 Forward;
        Vector3 Upward;
        if (rb.velocity.y > 0)
        {
            Forward = new Vector3(rb.velocity.x, -((-initialVelocity.y + rb.velocity.y - EndAngleOffset) * (-initialVelocity.y + rb.velocity.y - EndAngleOffset)), rb.velocity.z);
            Upward = new Vector3(0, 1, 0);
        }
        else
        {
            Forward = new Vector3(rb.velocity.x, ((initialVelocity.y + rb.velocity.y + EndAngleOffset) * (initialVelocity.y + rb.velocity.y + EndAngleOffset)), rb.velocity.z);
            Upward = new Vector3(0, -1, 0);
        }
        rb.rotation = Quaternion.LookRotation(Forward, Upward);

}

(ForceSet is set equal to true the when applying the launching force to the 'Arrow')


Solution

  • If you have access to the arrows Rigidbody component, you can access it's velocity property and set its rotation to be equal to its velocity.

    I recommend when making modifications to a Rigidbody's transform that you use the Rigidbody methods. This ensures that values are properly updated with Unity's physics system.

    using UnityEngine;
    
    public class ArrowBehaviour : MonoBehaviour
    {
        private Rigidbody rigidbody;
    
        private void Awake()
        {
            rigidbody = GetComponent<Rigidbody>();
        }
    
        private void Update()
        {
            rigidbody.rotation = Quaternion.LookRotation(rigidbody.velocity, Vector3.up);
        }
    }
    
    

    Depending on the circumstances, you might want to change Update to FixedUpdate.

    EDIT:

    There was a bug in the above code I fixed; that's what you get when you write it at 2am. I'd give it another go and see if that works for you.

    In an ideal world, you would pre-compute the path of the arrow so that you could perform any procedural animation to the arrow you would like.

    You could also calculate an X rotation to use instead, which will fake the effect.

    using UnityEngine;
    
    public class ArrowBehaviour : MonoBehaviour
    {
        [SerializeField] private float lookMagnitude = 45.0f;
        [SerializeField] private float velocitySensitivity;
    
        private Rigidbody rigidbody;
    
        private void Awake()
        {
            rigidbody = GetComponent<Rigidbody>();
        }
    
        private void Update()
        {
            // Determine how much we want to fake the rotation by.
            var lookStrength = Mathf.InverseLerp(-velocitySensitivity, velocitySensitivity, rigidbody.velocity)
    
            var targetEulerAngles = new Vector3(
             Mathf.Lerp(-lookMagnitude, lookMagnitude, lookStrength),
             transform.localEulerAngles.y,
             transform.localEulerAngles.z
            );
    
            // Rotate the transform
            transform.localEulerAngles = targetEulerAngles;
    
            // Alternatively, interpolate to the target angle.
            /* transform.localEulerAngles = Vector3.Lerp(
             *  transform.localEulerAngles,
             *  targetEulerAngles,
             *  Time.deltaTime * rotationSpeed
             * );
             */
        }
    }