Gun above a car rotate correctly ?

Hi Guys.

I’m working on car with a gun.


my problem here is with the gun. gun object is rotating correctly on Y axis to aim at target. but gun object not moving correctly with the car body. If i used “LocalRotation” instead of “rotation”, it runs fine with the car body, but when rotate car to left or right gun rotation become like crazy !!? How to solve this ?

using UnityEngine;
using System.Collections;

public class RearWheelDrive : MonoBehaviour {

    private WheelCollider[] wheels;

    public float maxAngle = 30;
    public float maxTorque = 300;
    public GameObject wheelShape;

    public Transform target;
    public Transform gun_car;

    // here we find all the WheelColliders down in the hierarchy
    public void Start()    {
      
        wheels = GetComponentsInChildren<WheelCollider>();

        for (int i = 0; i < wheels.Length; ++i)
        {
            var wheel = wheels [i];

            // create wheel shapes only when needed
            if (wheelShape != null)
            {
                var ws = GameObject.Instantiate (wheelShape);
                ws.transform.parent = wheel.transform;
            }
        }
    }
      
    public void Update()    {

        // Gun rotation
        // If i change the gun_car.rotation to gun_car.localRotation it works. Gun object moving with the car body but rotate like crazy !!
        Vector3 dir = target.position - gun_car.transform.position;
        Quaternion lookr = Quaternion.LookRotation (dir);
        Vector3 rotationy = Quaternion.Lerp (gun_car.rotation, lookr, Time.deltaTime * 7).eulerAngles;
        gun_car.rotation = Quaternion.Euler (0f,rotationy.y,0f);


        float angle = maxAngle * Input.GetAxis("Horizontal");
        float torque = maxTorque * Input.GetAxis("Vertical");

        foreach (WheelCollider wheel in wheels)
        {
            // a simple car where front wheels steer while rear ones drive
            if (wheel.transform.localPosition.z > 0)
                wheel.steerAngle = angle;

            if (wheel.transform.localPosition.z < 0)
                wheel.motorTorque = torque;

            // update visual wheels if any
            if (wheelShape)
            {
                Quaternion q;
                Vector3 p;
                wheel.GetWorldPose (out p, out q);

                // assume that the only child of the wheelcollider is the wheel shape
                Transform shapeTransform = wheel.transform.GetChild (0);
                shapeTransform.position = p;
                shapeTransform.rotation = q;
            }

        }
    }
}

Short answer: because Euler angles are awful. See this article, and you should definitely read that entire article to understand why. The most relevant fact is that separating the x/y/z components of the Euler angle set makes the numbers virtually meaningless.

The best way to accomplish this would be to get a single value (degrees around the axis) representing the rotation to the target, and then you can use that value as your Y component in line 40. How to get this value?

  1. Convert your target’s position to the car’s local space (or more specifically, the local space of the gun’s parent object. You can do this via:
  2. Use Mathf.Atan2 to convert that to an angle in radians
  3. Convert radians to degrees
  4. Use in your Euler angle constructor in line 40.
    The code for these steps, in order, is:
Vector3 targetInLocalSpace = gun_car.transform.parent.InverseTransformPoint(target.position);
float angleInRadians = Mathf.Atan2(targetInLocalSpace.z, targetInLocalSpace.x);
float angleDegrees = angleInRadians * Mathf.Rad2Deg;
gun_car.localRotation = Quaternion.Euler (0f,angleDegrees,0f);

You may have to play with the angle - might need to be made negative, or offset by 90 degrees one way or the other, etc., depending on how your objects are configured. That’s easy to work out by trial and error.

2 Likes

Thank you so much StarManta.
No one explain it to me like this. Most people saying use Euler and it does it job, but in my case becomes useless. After this point, I don’t know what to do. I found my self trapped and my project stopped. I couldn’t find any video or tutorial explain your method like you did.
Its working now. I’m just missing one thing “Smooth Movement”. How to add speed to rotation ?

If you keep the previous angle of your gun, then you can use the Mathf.MoveTowardsAngle() function to only move a certain distance per frame towards the new desired angle.

Best way is probably to have a float that tells you how many degrees per second, then use that times Time.deltaTime (as the third argument to Mathf.MoveTowardsAngle()) in order to only move that many degrees per Update() call.

Thank you for your reply.
I used Mathf.MoveTowardsAngle on previous script and replaced “target” value with "angleInRadians " or "angleDegrees " but nothing change !!
https://docs.unity3d.com/ScriptReference/Mathf.MoveTowardsAngle.html

        Vector3 targetInLocalSpace = gun_car.transform.parent.InverseTransformPoint(target.position);
        float angleInRadians = Mathf.Atan2(targetInLocalSpace.x, targetInLocalSpace.z);
        float angleDegrees = angleInRadians * Mathf.Rad2Deg;
        gun_car.localRotation = Quaternion.Euler (0f,angleDegrees,0f);
       
        float anglev = Mathf.MoveTowardsAngle(gun_car.transform.eulerAngles.y, angleDegrees, 3 * Time.deltaTime);
        gun_car.transform.eulerAngles = new Vector3(0, anglev, 0);

I think I misunderstand you, could you please write the code.