Creating a multiple part turret what locks onto certain axis.

Ok so, I have a game Im trying to work on, the turrets I am using are two parts ( shown in http://puu.sh/6Xg7o.jpg ) and I need to be able to make a " realistic " rotation so that the top part can only rotate up and down while the bottom part only rotates sideways. I know that is quite easy to do, however I also need to make it dynamic so that the turret can be rotated any way, it will be attached to a spaceship which can move upside down or in any direction so I can not just use a " Hacky " method to set the x y z to certain values.

Does anyone have any sources / idea’s of how I would achieve this? I have been trying to do this for quite a while today but angles are not really my thing.

Help would be amazing.

Here is the code I am using right now, setting the local version of the rotation messes up when I rotate the turret.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class EntityTurret : Entity
{

    public GameObject target;

    public GameObject baseObj;
    public GameObject midObj;
    public GameObject topObj;

    void Start()
    {

    }

    void Update()
    {
        transformTowardsTarget();
    }

    int getDir(Vector3 f, Vector3 tD, Vector3 u)
    {
        Vector3 perp = Vector3.Cross(f, tD);
        float dir = Vector3.Dot(perp, u);
        if (dir > 0.0f)
        {
            return 1;
        }
        else if (dir < 0.0f)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }

    int getDirVertical(Vector3 f, Vector3 tD, Vector3 u)
    {
        Vector3 perp = Vector3.Cross(u, tD);
        float dir = Vector3.Dot(perp, f);
        if (dir > 0.0f)
        {
            return 1;
        }
        else if (dir < 0.0f)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }

    public static float SignedAngle(Vector3 v1, Vector3 v2, Vector3 n)
    {
        return (float)Mathf.Atan2(Vector3.Dot(n, Vector3.Cross(v1, v2)), Vector3.Dot(v1, v2)) * 57.29578f;
    }
    
    float getAngle(Vector3 p1, Vector3 p2)
    {
        Vector3 targetDir = (p1 - p2).normalized;
        float angle = Vector3.Angle(targetDir, transform.forward);
        int dir = getDir(transform.forward, targetDir, transform.up);
        switch(dir)
        {
            case -1:
                return 360 - angle;
            case 1:
                return angle;
            default:
                return 0;
        }
    }

    float getAngleUp(Vector3 p1, Vector3 p2)
    {
        Vector3 targetDir = (p1 - p2).normalized;
        float angle = Vector3.Angle(targetDir, transform.forward);
        int dir = getDir(transform.forward, targetDir, transform.right);
        switch (dir)
        {
            case -1:
                return 360 - angle;
            case 1:
                return angle;
            default:
                return 0;
        }
    }
    
    void transformTowardsTarget()
    {
        Transform t = target.transform;
        Vector3 p = t.position;
        Vector3 p2 = transform.position;

        Vector3 targetDir = (p - p2).normalized;
        Vector3 forward = transform.forward;

        Vector3 finalRot = new Vector3();

        Debug.DrawLine(p2, p, Color.blue);

        float yAng = getAngle(p, p2);
        float xAng = getAngleUp(p, p2);

        Debug.Log(yAng + "," + xAng);

        finalRot.y = yAng;
        finalRot.z = transform.localRotation.eulerAngles.z;
        finalRot.x = transform.localRotation.eulerAngles.x;

        midObj.transform.localRotation = Quaternion.Euler(finalRot);

        // ALL TEST CODE BELOW ( For reference )

        //midObj.transform.LookAt(new Vector3(p.x, transform.position.y, p.z));
        //midObj.transform.Rotate(0, 90, 0);

        //topObj.transform.LookAt(new Vector3(p.x, p.y, p.z));
        //topObj.transform.Rotate(0, 270, 0);


        //float angle = Vector3.Angle((p - transform.position), transform.forward);


        //Debug.DrawLine(topObj.transform.position, tO.transform.forward, Color.blue);

        // Mid Rotation
        /*
        Vector3 flatVectorToTarget = transform.position - t.position;

        flatVectorToTarget.y = 0;

        Quaternion newRotation = Quaternion.LookRotation(flatVectorToTarget);

        midObj.transform.rotation = newRotation;
        midObj.transform.Rotate(0, 90, 0); // rotate offset to make turret properly face target.

        // Top Rotation

        flatVectorToTarget = transform.position - t.position;

        newRotation = Quaternion.LookRotation(flatVectorToTarget);

        Debug.DrawLine(topObj.transform.position, t.position,Color.red);
        Debug.DrawLine(topObj.transform.position, tO.transform.position, Color.green);

        topObj.transform.rotation = newRotation;
        topObj.transform.Rotate(0, 90, 0); // rotate offset to make turret properly face target.
        */
        /*
        
        Vector3 point = t.position;
        point.y = transform.position.y;
        midObj.transform.LookAt(point);
        */
        //point = t.position;


        //topObj.transform.LookAt(point);

    }

    public override void onEntityInitialized()
    {

    }

    public override void entityTick()
    {
        
    }

}

If you use transform.localRotation instead of transform.rotation, your “hacky way” should work just fine (provided you’re careful about local gimbal lock).

Unitycookie.com has a multi-part tower defense tutorial that should have some useful information that could be applied to your situation.
CG Cookie | Learn Blender, Online Tutorials and Help - CG Cookie | Learn Blender, Online Tutorials and Feedback

If the turret will always be aligned with the world axes, you will find some starter code here:

http://answers.unity3d.com/questions/388185/make-the-turret-automatically-rotate-to-look-at-wh.html

If the turret has arbitrary rotation, you will find a single, short script down in the comments to my answer here. You will need to open the additional comments of my answer to find the script:

http://answers.unity3d.com/questions/562443/turret-slerp-rotation-and-clamping.html

Note there are a few other post with alternate solutions on UA for turret rotation.

using UnityEngine;
using System.Collections;
public class AISmoothLookAT : MonoBehaviour {
public Transform target;
public Transform looker;
public Transform Xsmoothlooker;
public Transform Ysmoothlooker;
public float Xspeed = 2f;
public float Yspeed = 2f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
looker.LookAt (target);
float YRotation = looker.eulerAngles.y;
float XRotation = looker.eulerAngles.x;
Xsmoothlooker.rotation = Quaternion.Slerp(Xsmoothlooker.rotation , Quaternion.Euler(0 , YRotation, 0), Time.deltaTime * Xspeed);
Ysmoothlooker.rotation = Quaternion.Slerp(Ysmoothlooker.rotation , Quaternion.Euler(XRotation , YRotation, 0), Time.deltaTime * Yspeed);
}
}