Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question World Rotation to Local Rotation

Discussion in 'Scripting' started by toborific, Nov 20, 2022.

  1. toborific

    toborific

    Joined:
    Sep 22, 2013
    Posts:
    2
    I have a turret that is mounted on a spaceship. It's a child object of the ship. The turret is allowed to point left, right, up, but not down, because that would be pointing through the spaceship.

    So, I need to limit the rotation of the turret so it won't point down. I started with this code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TurretScriptTest : MonoBehaviour
    6. {
    7.  
    8.  
    9.  
    10.     public float rotateSpeedBase = 1f;
    11.     public float rotateSpeedCurr = 1f;
    12.     public float yMin = 0;
    13.     public float yMax = 1;
    14.    
    15.     public Transform target;
    16.    
    17.     // Start is called before the first frame update
    18.     void Start()
    19.     {
    20.         rotateSpeedCurr = rotateSpeedBase;
    21.  
    22.     }
    23.  
    24.     // Update is called once per frame
    25.     void Update()
    26.     {
    27.         if (target && target.gameObject.activeSelf)
    28.         {
    29.             // Rotate
    30.             RotateWithLock();
    31.  
    32.         }
    33.    
    34.     }
    35.    
    36.     public virtual void RotateWithLock()
    37.     {
    38.  
    39.         if (target)
    40.         {
    41.  
    42.            
    43.             Vector3 targetDir = target.position - transform.position;
    44.            
    45.             float step = rotateSpeedCurr * Time.deltaTime;
    46.             Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
    47.             newDir.y = Mathf.Clamp(newDir.y, yMin, yMax);
    48.            
    49.             transform.rotation = Quaternion.LookRotation(newDir);
    50.            
    51.         }
    52.  
    53.     }
    54.  
    55.    
    56. }
    This works great except for one thing. The spaceship is also rotating all over the place. As soon as the parent spaceship goes off its original axis the above code is worthless. The turret will point through the spaceship or whatever else is in the way.

    I'm assuming that what I need to do is convert everything to local rotation and position so that "down" for the turret will always be its base, not "down" in world space. So I try this code:

    Code (CSharp):
    1. public virtual void RotateWithLock()
    2.     {
    3.  
    4.         if (target)
    5.         {
    6.  
    7.            
    8.             Vector3 targetDir = target.position - transform.position;
    9.             float step = rotateSpeedCurr * Time.deltaTime;
    10.             Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
    11.             Vector3 newDirLocal = transform.InverseTransformDirection(newDir);
    12.            
    13.             newDirLocal.y = Mathf.Clamp(newDirLocal.y, yMin, yMax);
    14.            
    15.             transform.localRotation = Quaternion.LookRotation(newDirLocal);
    16.            
    17.         }
    18.  
    19.     }
    Now the turret doesn't move at all. I can see that the rotation is changing slightly in the inspector, but not visibly in the game. When I multiply the rotation it does start to move, but still doesn't point the right direction. What am I doing wrong?
     
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Well, I don't think that localRotation is what you think it is. The localRotation of an object is relative to the parent space, not to the own coordinate space. So if you use a world space direction vector, you have to transfer it into the parent space. An alternative would be to simply transform it back to world space after you clamped it. Though it makes not much sense to express the own rotation inside its own coordinate space as it would always be constant.

    For turrets with certain limits it's usually easier to split the whole mechanic into seperate single axes and rotate them seperately. In many cases you usually have a seperate base that only rotates on the y axis and a separate gun barrel mounted on the base that only rotates on the local x axis. So the base would simply rotate to roughly face the target and the barrel would rotate up / down do the rest. You could clamp both individually
     
    PraetorBlue, Reedex and toborific like this.
  3. toborific

    toborific

    Joined:
    Sep 22, 2013
    Posts:
    2
    Thank you! That was the key. This code is doing what I want now.
    Code (CSharp):
    1.     public virtual void RotateWithLock()
    2.     {
    3.  
    4.         if (target)
    5.         {
    6.  
    7.             Vector3 targetDir = target.position - transform.position;
    8.             float step = rotateSpeedCurr * (Time.deltaTime+1);
    9.             Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
    10.             Vector3 newDirLocal = transform.parent.transform.InverseTransformDirection(newDir);
    11.             newDirLocal.y = Mathf.Clamp(newDirLocal.y, yMin, yMax);
    12.             Vector3 newDir2 = transform.parent.transform.TransformDirection(newDirLocal);
    13.            
    14.             transform.rotation = Quaternion.LookRotation(newDir2);
    15.            
    16.         }
    17.  
    18.     }
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Last edited: Nov 20, 2022
    Bunny83 and toborific like this.