My turret has an odd recoiling effect with rigidbody bullets

I’m not sure why this is. I have some video to describe the problem at this link:

There is also some code I will provide:

using UnityEngine;

public class Bullet : Spawnable {

    public float speed;
    Rigidbody rb;
   
    // Use this for initialization
    new void Start () {
        base.Start();
        rb = GetComponent<Rigidbody>();
       
    }

    void FixedUpdate()
    {
        rb.AddRelativeForce(Vector3.forward * speed);
    }

    void OnTriggerEnter(Collider co) //Bullets will all have "Bullet" tags
    {
        if(string.Equals("Bullet", co.gameObject.tag))
        {
            co.gameObject.SetActive(false); //Deactivate the other object
            print("Collision occurred!");
        }
    }

    public float VMagnitude
    {
        get
        {
            return speed;
        }
    }

}
using UnityEngine;

public class Turret : Spawnable
{
    public float turnSpeed = 1f, refireRate = 0.2f;
    public bool isTracking = false;
    Quaternion elevation, rotation, defaultElevation, defaultRotation;
    public Transform turretBase, turretGun, looker, bulletSpawn;
    Vector3 target;
    public Rigidbody targetRB;
    public Bullet bullet;
    public GameObject radarField;
    Timer refireTimer;

    new public void Start()
    {
        base.Start();
        defaultElevation = Quaternion.Euler(turretGun.localEulerAngles.x, turretGun.localEulerAngles.y, turretGun.localEulerAngles.z);
        defaultRotation = turretBase.rotation;
        refireTimer = gameObject.AddComponent<Timer>();
        refireTimer.Limit = refireRate;
    }
    void Compute()
    {
        target = (targetRB.velocity / bullet.VMagnitude) + targetRB.position;

        looker.LookAt(target);
        elevation = Quaternion.Euler(looker.eulerAngles.x, 0f,0f); //To be used locally
        rotation = Quaternion.Euler(turretBase.eulerAngles.x, looker.eulerAngles.y, turretBase.eulerAngles.z);
    }
    public void Traverse()
    {
        if (isTracking)
        {
            turretGun.localRotation = Quaternion.Slerp(turretGun.localRotation, elevation, turnSpeed);
            turretBase.rotation = Quaternion.Slerp(turretBase.rotation, rotation, turnSpeed);
        }
        else
        {
            turretGun.localRotation = Quaternion.Slerp(turretGun.localRotation, defaultElevation, turnSpeed);
            turretBase.rotation = Quaternion.Slerp(turretBase.rotation, defaultRotation, turnSpeed);
        }
    }

    public void SetTarget(GameObject t)
    {
        if (t != null)
        {
            targetRB = t.GetComponent<Rigidbody>();
            isTracking = true;
            refireTimer.Begin();
        }
        else
        {
            target = Vector3.forward;
            targetRB = null;
            isTracking = false;
        }
    }

    void Fire()
    {
        if (!refireTimer.IsRunning)
        {
            GameObject b = Instantiate(bullet.gameObject, bulletSpawn.position, bulletSpawn.rotation);
            b.transform.position = bulletSpawn.position;
            b.transform.rotation = bulletSpawn.rotation;

            refireTimer.Begin();
        }

    }

    public void Update()
    {
        if (isTracking)
        {
            Compute();
            Traverse();
            Fire();
        }
    }


}

I’ve not put many comments in these two files mainly because I’m the only dev and I can read my own code easily, but if you need any explanations I will happily provide them.

Not looked too much at it, but using Quaternion.Slerp in this way is wrong. I think you want Quaternion.RotateTowards instead.

It’s a bit of a stylistic choice I think, rather than being just “wrong”. Using Slerp like this gives it a snappier feel, while using RotateTowards would give it a robotic feel. You’re probably right in that a turret should probably be more robotic-feeling, though.

As for the core problem itself, you may be running into the problems with Euler angles. Put simply: Euler angles suck for anything except for plugging in human-readable degree values. In particular, anytime you split up the X/Y/Z components of Euler angles (as in lines 28-29), they become absolutely worthless.

As it happens I have recently done a writeup on how and why Euler angles fail for this sort of thing. At a glance, it appears that your turret rotation ought to be based on Atan2 of where its target is, in two planes: first the horizontal plane, then the vertical plane, where your “x” parameter for Atan2 should be the lateral (two-dimensional, using only X and Z) distance to your target.

Also, if you put a Rigidbody on the turret and on the bullet, and you’re spawning the bullet overlapping with the turret, they will automatically fly away from each other because of physics. Either make sure the bullet does not touch the turret when it spawns or put them on separate physics layers and turn off collision between those layers.

I think there’s a bug in your bullet “OnTriggerEnter” too. The collider parameter in OnTriggerEnter represents the trigger that you’ve entered, not yourself. If the bullet script is on a bullet, you don’t need to check if its own tag is bullet; you know that it is, because you put the script on the bullet. You should be checking if the trigger is whatever you want the bullet to hit; probably the thing that you’re shooting at. The way you have it now, it only triggers if a bullet hits a different bullet, which I’m guessing is not what you want.

Valid point. People kept suggesting that I use the method that I am, and I became thoroughly frustrated with attempting a trig solution over the weekend so in the end I settled for that.

In fact that may just be the problem. I noticed two things: when a bullet fires (and only when that happens) does the turret recoil round’ like it does. I think maybe the turret is aiming at the bullet because when it spawns it flies upward, not forward and the turret also aims upward.

I think that problem might be related to how I’m handling the trigger enter in my Radar.cs class (not posted here). I think spawning an object within the trigger is causing the turret to aim at it.

Until now I have had absolutey no trouble with the Quaternion.Euler() setting method. My camera aimed directly at the point of intercept many a time before I tried to get it to shoot at it.

Slerp is an interpolation method, and should be used as such. With a parameter of less than 1 it will never reach its final position, and with a value of 1 or more it will always move to the target rotation, so the interpolation is superfluous.

You can get the same behaviour from RotateTowards anyway by including the rotation delta in your calculation of maxDegreesDelta, effectively making a PD controller.

Trigonometry is cumbersome though, my advice would be to just use vectors and quaternions. For me at least, it is conceptually easier, and maybe surprisingly, they are often computationally cheaper as well.

To isolate the rotation of the base for example, get a vector in the direction of the target, and project it on the plane with a normal pointing up, and then get the quaternion rotation.

        Vector3 direction = target.position - transform.position;
        Vector3 directionPrj = Vector3.ProjectOnPlane(direction, Vector3.up);
        Quaternion targetRotation = Quaternion.LookRotation(directionPrj);
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, 250f * Time.deltaTime);

That sounds likely, I’ve been stumped by similar issues to that myself on occasion. Layer collision matrix to the rescue!