Transform.Rotate affects Vector3.forward erroneously?

Hi,

Me and 2 classmates are creating a small racing game in the style of FZero. Now we have a basic movement script for driving the vehicle, but we are having one tricky problem.

While the acceleration and deceleration are going fine, implemented with rigidbody.addForce, the rotations are giving us more trouble. After we apply a rotation to the vehicle, it seems that Vector3.forward is not always pointing forward anymore. The consequence is that our vehicle does not move straight forward anymore.

We use a rigidbody and a spherecollider in Unity with gravity on. I’m not sure if this is the cause. Can it be that because of the friction with the floor plane, the rotation is not fully executed or something?

Here is the code in c# of our drive script:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody))]
public class Drive : MonoBehaviour {

    public float m_acceleration = 0.0f;
    public float m_top_speed = 0.0f;
    public float m_brake_decel = 0.0f;
    public float m_steering_speed = 0.0f;

    private float m_steering_treshold;

	// Use this for initialization
	void Start () {
	
	}

    void Awake()
    {
        m_steering_treshold = 0.1f;
    }
	
	// Update is called once per frame
	void FixedUpdate () {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            Accelerate();
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {
            Decelerate();
        }
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            TurnLeft();
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            TurnRight();
        }
	}

    float kphTomps(float kph)
    {
        return kph * 3.6f;
    }

    void Accelerate()
    {
        if (rigidbody.velocity.magnitude < m_top_speed)
        {
            rigidbody.AddRelativeForce(Vector3.forward * rigidbody.mass * m_acceleration);
        }
    }

    void Decelerate()
    {
        if (rigidbody.velocity.magnitude > (-m_top_speed / 10.0f))
        {
            rigidbody.AddRelativeForce(Vector3.back * rigidbody.mass * m_brake_decel);
        }
    }

    void TurnLeft()
    {
        if (rigidbody.velocity.magnitude > m_steering_treshold)
        {            transform.Rotate(Vector3.Normalize(Vector3.down * m_steering_speed * Time.deltaTime), Space.Self);
            // Centripetal force = (m * v^2) / r
            rigidbody.AddRelativeForce(Vector3.left * 
                (rigidbody.mass * (rigidbody.velocity.magnitude * rigidbody.velocity.magnitude) / m_steering_speed));
        }
    }

    void TurnRight()
    {
        if (rigidbody.velocity.magnitude > m_steering_treshold)
        {
transform.Rotate(Vector3.Normalize(Vector3.up * m_steering_speed * Time.deltaTime), Space.Self);
            // Centripetal force = (m * v^2) / r
            rigidbody.AddRelativeForce(Vector3.right * 
                (rigidbody.mass * (rigidbody.velocity.magnitude * rigidbody.velocity.magnitude) / m_steering_speed));
        }
    }
}

If someone has experienced the same problem, how did you solve it? Do we need to disable the gravity and use our own code for this or are there solutions out there that I missed?

Thanks in Advance,
Squibel

Edit: (PS) The values we are using currently for the public members are:
Acceleration = 10
Top_speed = 120
Brake_decel = 15
Steering_speed = 80

Hi, welcome to the forum!

If you are using this on a sphere, then you need to take its rolling into account with the physics. AddRelativeForce and transform.Rotate operate in the object’s local coordinate space. So, if the sphere has rolled, its transform.forward might be pointing downward or whatever. If you then apply a force in this direction, the sphere won’t move as you would expect.

The exact way around this will depend on what type of game you are making. If the sphere rolls over a flat surface, you may be able to use world coordinates (eg, rigidbody.AddForce rather than AddRelativeForce). Other alternatives include moving the sphere according to the direction of the camera or relative to the surface it is resting on.

Hi,

Thank you for your quick reply.

I am using the forces on a sphere, but the object this code is used on has “freeze rotation” turned on. Therefore the force added should not rotate the object around if I am right.
I have been testing, and when I turn off gravity and let the object float without it touching anything, the code works. So, my first thought is that the cause lies somewhere in the interaction with the floor plane.

I have also noticed, in other cases, that using transform.rotate is not always very reliable. Which I find quite weird actually. I would think that transform.rotate is absolute and there is no margin for errors…

Can you think of any other causes for this problem?

Try turning first and then going forward. You also may want to take any AngularVelocity out of the system at this step by setting it to zero. That will stop sidesliding from mass and momentum.

HTH

I have been searching for myself a bit longer and have found 2 possible solutions for my problem:

  • use transform.rotation instead of transform.rotate()
  • create a force opposite to gravity to make my hoovercraft float instead of using a bigger collider than the actual model…

Thanks you two for your advice, my problem has been solved now!