Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Two part spaceship turret not rotating on Z axis

Discussion in 'Scripting' started by Avo, Sep 21, 2014.

  1. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    I'm currently working on a script for two part turrets I can attach to the outside of spaceships in my game. The turret consists of two parts, a pan-able base that's supposed to rotate in the Z axis and a tilt-able head that's supposed to rotate on the Y axis. Unfortunately the base refuses to rotate on the Z axis, when I set it to move on the X or Y axis's however it rotates fine. I highlighted the lines dealing with setting the rotation of my dummy objects the physical turret attempts to steal the rotation from in red. Can anyone see the lapse in my logic that causes it to not rotate in Z? Thanks for your time!

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Turret : MonoBehaviour
    6. {
    7.     public int damage;
    8.     public string damageType;
    9.     public float reloadTime;
    10.     public float firePauseTime; //The pause time after the turret points at an enemy but before it fires.
    11.     public Transform[] muzzlePositions;
    12.     private int activeMuzzle = 0;
    13.     private float nextFireTime;
    14.     public float turnSpeed;
    15.     public float errorAmount;
    16.     public GameObject target;
    17.     public Transform pivotPan;
    18.     public Transform pivotTilt;
    19.     public GameObject projectile;
    20.     public Transform aimPan;  //A secondary dummy object used to discover our desired final rotation.
    21.     public Transform aimTilt; //A secondary dummy object used to discover our desired final rotation.
    22.  
    23.    
    24.     private Vector3 desiredRotation;
    25.     private RaycastHit hit;
    26.    
    27.     // This code is used from the Unitycookie tower defence tutorial 2
    28.     void Update ()
    29.     {
    30.         if(target != null)
    31.         {
    32.             //Check to make sure there's nothing between us and and the target. 384 are layers 8 & 9 our ship and obstacle layers.
    33.             //if(Physics.Linecast(muzzlePositions[activeMuzzle].position, target.transform.position, out hit,384) == target)
    34.             Physics.Linecast(muzzlePositions[activeMuzzle].position, target.transform.position, out hit,384);
    35.  
    36.             if(hit.collider.gameObject == target)
    37.             {
    38.                 aimPan.LookAt(target.transform);
    39.                 aimPan.eulerAngles = new Vector3(pivotPan.eulerAngles.x,pivotPan.eulerAngles.y,aimPan.eulerAngles.z);
    40.                 aimTilt.LookAt(target.transform);
    41.                 aimTilt.eulerAngles = new Vector3(aimTilt.eulerAngles.x,pivotTilt.eulerAngles.y,pivotTilt.eulerAngles.z);
    42.  
    43.                 pivotPan.rotation = Quaternion.Lerp(pivotPan.rotation,aimPan.rotation, Time.deltaTime*turnSpeed);
    44.                 pivotTilt.rotation = Quaternion.Lerp(pivotTilt.rotation,aimTilt.rotation, Time.deltaTime*turnSpeed);
    45.  
    46.                 if(Time.time >= nextFireTime)
    47.                 {
    48.                     FireProjectile();
    49.                 }
    50.             }
    51.                 print (hit.collider.gameObject.name);
    52.         }
    53.    
    54.     }
    55.  
    56.     void FireProjectile()
    57.     {
    58.         //Play Audio Here
    59.         nextFireTime = Time.time + reloadTime;
    60.  
    61.         //Swap between the barrels on our gun.
    62.         activeMuzzle = activeMuzzle + 1;
    63.         if (activeMuzzle == muzzlePositions.Length)
    64.         {
    65.             activeMuzzle = 0;
    66.         }
    67.  
    68.         //Create a missile Here
    69.  
    70.     }
    71.    
    72. }
    73.  
     
  2. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    Bump! Is there anything I can do to make this a little clearer or easier to digest?
     
  3. BmxGrilled

    BmxGrilled

    Joined:
    Jan 27, 2014
    Posts:
    238
    You could do this an easier way, if you link a reference to the transform the turrets are attached to, e.g. the ship.
    Your hierarchy would look like e.g. ship -> turret base -> turret head
    Then you can rotate relative to your ship, like so:

    Quaternion baseRot = Quaternion.Euler(0, 0, turretZangle);
    Vector3 turretDirection = baseRot * Vector3.forward;
    Quaternion newRot = Quaternion.LookRotation(shipTransform.rotation * turretDirection, shipTransform.up);

    So now all you have to do is pass the target Z angle, and set the turret base rotation to newRot.
    How this works:
    You first calculate the direction you want the turret to face (relative to the turret);
    Then you create a look rotation pointing in that direction relative to the ship;
    Finally the look rotation is relative to the ships UP direction, or you can swap that out for a up direction of your choosing, remember of course if you use your own up direction, to multiply it by your ships rotation, so that it is relative to the ship! :)

    Hope this helps! :)
     
  4. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    Sorry about my slow response, I still seem to be having some trouble implementing this. What exactly do you mean by the target z angle? Thank you for your help here, I've been pounding my head against this since Friday morning watching the code branch of into progressively more complicated attempts schemes to make the turret behave the way I want it. Your method seems like it will be a lot simpler and a lot faster than anything I've tried.

    Code (csharp):
    1.  
    2. Quaternion baseRot = Quaternion.Euler(0, 0, pivotPan.rotation.z);
    3. Vector3 turretDirection = baseRot * Vector3.forward;
    4. Quaternion newRot = Quaternion.LookRotation(shipTransform.rotation * turretDirection, shipTransform.up);
    5.  
    6. pivotPan.rotation = Quaternion.Lerp(pivotPan.rotation,baseRot, Time.deltaTime*turnSpeed);
    7. //pivotTilt.rotation = Quaternion.Lerp(pivotTilt.rotation,newRot, Time.deltaTime*turnSpeed);
     
  5. BmxGrilled

    BmxGrilled

    Joined:
    Jan 27, 2014
    Posts:
    238
    I actually found the code I posted you was wrong, but I'd hoped it would get you going in the right direction, the code can work but I'm just not getting the rotations in the right order. Maybe someone else could help! :)
    I wish I could help! :(
     
  6. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    You provided a different approach I can toy with and see if I can get it working. This has proven to be a lot more complicated than I first thought going into it, I really appreciate your help. If i get this working I'll have to post the code!
     
    BmxGrilled likes this.
  7. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I don't understand why LookRotation would be used here, but I do think Emma's approach of properly parenting all the parts is the right one.

    Then, you just need to set the localRotation of each part to whatever rotation you want (e.g. Quaternion.Euler(0, 0, someAngle)). That should be all there is to it.
     
  8. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    The main problem with lookrotation is it looks using 3 axis. With a turret, you need to translate this to two axis, your turret base and your turret head.

    I have the code to do it, but not here.

    You can use lookrotation, but you have to convert it to a flat local rotation before you assign it to your turret parts
     
  9. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    Joe Strout, that's exactly what I did in the first script, with the exception that I used Quaternion.Lerp to slowly move between the two points. When I set the aimPan/aimTilt's values to that of pivotPans/Pivot tilts I'm zeroing out the difference between them so the Lerp doesn't change them.

    JamesLeeNz, can you point me in a little more specific a direction than that? I did a little bit of googling but I didn't manage to find anything meaningful.
    Thank you again, I've been beating my head against this for way to long now.
     
  10. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    I feel your pain... I spent DAYS trying to figure it out... ill be nice. Here's the code that rotates a turret base/head independently.

    Works no matter what rotation of the object the turret is sitting on is. I use it in my vehicles, and aircraft in Inferno. Always interesting watching a jeep barrel roll through the air and still hitting a target accurately.

    Code (csharp):
    1.  
    2.  
    3. var localAimDirection = transform.InverseTransformDirection(target.position - turret.position);
    4. var flatLocalAimDirection = new Vector3(localAimDirection.x, 0, localAimDirection.z);
    5. turret.transform.localRotation = Quaternion.LookRotation(flatLocalAimDirection);
    6.  
    7. var turretLocalAimDirection = turret.transform.InverseTransformDirection(target.position - turretHead.position);
    8. turretHead.localRotation = Quaternion.LookRotation(turretLocalAimDirection);
    9.  
    10.  
    also, for the rotating part, I use RotateTowards. Gives a much easier to use/realistic manipulation of rotation for a turret
     
    Avo, BmxGrilled and JoeStrout like this.
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    No, not quite. You're using LookAt to try and rotate each of the parts. As @JamesLeeNZ explains above, that won't work here; you need to "flatten" the 3D orientation those return into a single-axis motion for each part (and the right single axis, of course).

    I suspect you're stuck in a cloud of confusion because you're trying to simultaneously figure out how to rotate the pan and tilt parts at all, and trying to get them to automatically track a target. These are separate issues. First get them to pan and tilt, under manual control or just steadily rotating or some such, until you convince yourself you've got the basic mechanics working. Only then should you tackle getting them to automatically point at a target, and make sure you do so by setting the same pan/tilt inputs you were setting manually.

    EDIT: Never mind, I see James has posted what looks like a very elegant solution!
     
    Avo likes this.
  12. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    Sorry about the week long delay between my last post. I wanted to toy with the script a little to see if I could figure it out. I finally have the base rotating around the right relative axis but it seems to flip over onto the side. I can't figure out why. Thank you again for your time, I'm not quite sure why this isn't clicking with me.

    JoeStrout,I suspect you're right. I've tried to simplify it down to just the base of the turret.
    Thanks for your script JamesLeenz. I can't seem to get it to rotate quite right but your scripts pushed me a lot closer.


    Code (csharp):
    1.  
    2. //The turret rotates on the wrong axis without the y position being set to 90
    3. localAimDirection =
    4. transform.InverseTransformDirection(target.transform.position - turretBase.transform.position);
    5. flatLocalAimDirection = new Vector3(localAimDirection.x,0,localAimDirection.z);
    6. turretBase.transform.localRotation = Quaternion.LookRotation (flatLocalAimDirection, Vector3.up);
    7.  
     
    Last edited: Sep 27, 2014
  13. BmxGrilled

    BmxGrilled

    Joined:
    Jan 27, 2014
    Posts:
    238
    flatLocalAimDirection is a normal vector pointing at the target from the position of the turret, not the euler angles! :eek:

    You could do Quaternion.Euler(0, 90, 0); to get a 90 degree rotation on Y axis.
    Then multiply that by the LookRotation thats been calculated. Hope this helps! :)
     
    Avo likes this.
  14. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    I'm not actually trying to rotate the object when I assign the Y of flatlocalrotation to 90. I'm changing the plane it rotates on, I posted a line of code I had been toying with. I'll edit it to hold the line I'm currently working with.
     
  15. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    I still can't seem to wrap my head around this. It's rotating the turret around it's base but it tilts the turret over on the side. I'm a little confused at what the localAimDirection does as well. Any clarification would really help! Thank you again for your time!

    Code (csharp):
    1.  
    2. //Get the difference in the objects rotations
    3.  
    4. //This line I'm confused about
    5.                 localAimDirection = transform.InverseTransformDirection(target.transform.position - transform.position);
    6.              
    7.                 flatLocalAimDirection = new Vector3(localAimDirection.x, 0, localAimDirection.z);
    8.  
    9.                 //Uses the world space up.
    10.                 targetLocalRotation = Quaternion.LookRotation(flatLocalAimDirection, Vector3.up);
    11.  
    12.                 //Instantly rotates our turretBase.
    13.                 turretBase.transform.localRotation = targetLocalRotation;
     
  16. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    in its non-rotated state, what's your turret base rotation? If its not 0,0,0 you should either reset the pivot rotation in your favourite modelling tool, or you should re-parent the mesh to a gameobject thats un-rotated.

    dealing with rotations that by default are 0,90,0 or similar is a pain the a-hole
     
  17. Avo

    Avo

    Joined:
    Dec 4, 2010
    Posts:
    236
    Sorry for my late response. That was it, I zeroed out all the rotations in my model so the artwork faced down the Z direction without the dummy parent objects having any rotation but it still didn't work. When I created a new turret using Unity primitives and set it up it worked perfectly .Thank you so much! I've been pounding my head against this for way to long, I couldn't have gotten it without so much help!

    I promised I'd paste the final code.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Turret : MonoBehaviour
    6. {
    7.     //public int damage;
    8.     //public string damageType;
    9.     //public float reloadTime;
    10.     //public float firePauseTime; //The pause time after the turret points at an enemy but before it fires.
    11.     public Transform[] muzzlePositions;
    12.     private int activeMuzzle = 0;
    13.     private float nextFireTime;
    14.     //public float turnSpeed;  //The maximum number of degrees our turret can turn per second.
    15.     //public float errorAmount;
    16.     public GameObject target;
    17.     //public GameObject projectile;
    18.     public Transform turretBase;  //A secondary dummy object used to discover our desired final rotation.
    19.     public Transform turretHead; //A secondary dummy object used to discover our desired final rotation.
    20.     public float reloadTime;
    21.  
    22.     private Vector3 desiredRotation;
    23.     private RaycastHit hit;
    24.  
    25.     private Vector3 localAimDirection;
    26.     private Vector3 flatLocalAimDirection;
    27.     private Vector3 turretLocalAimDirection;
    28.  
    29.     private Quaternion targetLocalRotation;
    30.  
    31.  
    32.     void start ()
    33.     {
    34.         targetLocalRotation = target.transform.localRotation;
    35.     }
    36.  
    37.     //Arcane Systems
    38.     void Update ()
    39.     {
    40.  
    41.         if(target != null)
    42.         {
    43.             //Check to make sure there's nothing between us and and the target. 384 are layers 8 & 9 our ship and obstacle layers.
    44.             Physics.Linecast(muzzlePositions[activeMuzzle].position, target.transform.position, out hit,384);
    45.          
    46.             if(hit.collider.gameObject == target)
    47.             {
    48.                 //Get the difference in the objects rotations
    49.                 localAimDirection = transform.InverseTransformDirection(target.transform.position - transform.position);
    50.              
    51.                 //flatLocalAimDirection = new Vector3(localAimDirection.x, 0, localAimDirection.z);
    52.                 flatLocalAimDirection = new Vector3( localAimDirection.x,0,localAimDirection.z);
    53.  
    54.                 //Uses the world space up.
    55.                 targetLocalRotation = Quaternion.LookRotation(flatLocalAimDirection, Vector3.up);
    56.  
    57.                 //Instantly rotates our turretBase.
    58.                 turretBase.transform.localRotation = targetLocalRotation;
    59.  
    60.                 turretLocalAimDirection = transform.InverseTransformDirection(target.transform.position - turretHead.position);
    61.                 turretHead.localRotation = Quaternion.LookRotation(turretLocalAimDirection);
    62.              
    63.                 if(Time.time >= nextFireTime)
    64.                 {
    65.                     FireProjectile();
    66.                 }
    67.             }
    68.         }
    69.      
    70.     }
    71.  
    72.     void FireProjectile()
    73.     {
    74.         //Play Audio Here
    75.         nextFireTime = Time.time + reloadTime;
    76.      
    77.         //Swap between the barrels on our gun.
    78.         activeMuzzle = activeMuzzle + 1;
    79.         if (activeMuzzle == muzzlePositions.Length)
    80.         {
    81.             activeMuzzle = 0;
    82.         }
    83.      
    84.         //Create a missile Here
    85.      
    86.     }
    87. }
     
    korath86 likes this.
  18. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    I had similar problems earlier, which is why any model/primitive I use now must have a zero rotation. So.much.easier.
     
  19. korath86

    korath86

    Joined:
    Mar 13, 2017
    Posts:
    1
    Hi was wondering how would one "clamp" (not sure that's the correct term) the rotation between two values? for example the turret only rotating a max of 90 degreees