Search Unity

transform.LookAt or Quaternion.LookRotation on 1 axis only?

Discussion in 'Scripting' started by gevarre, Dec 7, 2009.

  1. gevarre

    gevarre

    Joined:
    Jan 19, 2009
    Posts:
    132
    Is it at all possible to use transform.LookAt or Quaternion.LookRotation to rotate an object to look at another object on one axis only?

    I've done searches of the forums and read over 100 posts related to this, but none of the solutions really works right. Specifically, all I'm trying to do is rotate an object smoothly about it's Y-Axis over time to point at another object.

    Oh, and by the way, many thanks to andeeee who has time and time again posted explanations on variations of this topic. They have come close, but still aren't working quite right. Even so, reading through his posts have taught me oodles of other things that I didn't know that will help me later:)
     
  2. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    You're most welcome. In fact, here I come again...

    One of the variants of transform.LookAt takes a point in space as the target. Just set the Y coordinate of the target point to zero:-
    Code (csharp):
    1. var targetObj: Transform;
    2.    ...
    3. var point: Vector3 = targetObj.position;
    4. point.y = 0.0;
    5. transform.LookAt(point);
     
  3. gevarre

    gevarre

    Joined:
    Jan 19, 2009
    Posts:
    132
    Cool. That helps a lot:)

    One follow-up question:
    How do I now get that to rotate to the position over time instead of just immediately snapping to that angle?
     
  4. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    In that case, you do need Quaternion.LookRotation. Store the object's current rotation and then use LookRotation to make a "target" rotation. You can then interpolate between the two smoothly using Quaternion.Slerp.
     
    RixiRooTM and twobob like this.
  5. rocket5tim

    rocket5tim

    Joined:
    May 19, 2009
    Posts:
    227
    Here's an example that uses Slerp. This will make the transform rotate around Z. Useful if you're a 2D side-scroller moving on the X and Y plane.

    Code (csharp):
    1.     var newRotation = Quaternion.LookRotation(transform.position - target.position, Vector3.forward);
    2.     newRotation.x = 0.0;
    3.     newRotation.y = 0.0;
    4.     transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 8);
     
    Tatarr, dyupa, siordache94 and 4 others like this.
  6. [nj86]

    [nj86]

    Joined:
    Dec 20, 2009
    Posts:
    12
    I'm glad I happened to find this thread instead of having to start a new one (first post here, btw!).

    I'm fairly new to Unity, and I'm trying to figure out how to make Diablo-like movement system. It doesn't have to be anything too complicated: just click somewhere, and the character goes there.

    So far I've managed to pull off this tech-demo (lol) with the help of this thread: http://white-isle.net/utest/instanssi/

    Don't worry about the cubes being spawned, I was just playing around.. Anyhow, now that I somehow managed to get the blue "character" cube to face the direction the mouse is being clicked at, I need the cube to actually move to that point till it reaches it.

    I suppose the target coordinates can be found from the raytraced RaycastHit-object (RaycastHit.point?), but with what kind of function I'd move the cube to its destination?

    I guess it's something really easy, I just haven't figured it out yet :O (transform.Translate....?)
     
  7. [nj86]

    [nj86]

    Joined:
    Dec 20, 2009
    Posts:
    12
    Don't mind me, I'm a real idiot... I found a solution already, a solution called "Vector3.Lerp" :p
     
  8. SpiritWebb

    SpiritWebb

    Joined:
    Nov 10, 2009
    Posts:
    38
    I am glad I found this thread...and I am trying to figure out how to do this:

    What I am wanting to do is create the point/click movement style, like that of Archlord or WoW. Basically, I point to a spot on the screen and the character turns and goes to that point where you clicked, then stops.

    I am wanting to remove the WASD key movements and just go with the mouse point/click.

    I don't know how to set this up, where to begin, nothing...any help would be greatly appreciated. Thanks in advance.
     
  9. EnglishMuffin101

    EnglishMuffin101

    Joined:
    Dec 14, 2011
    Posts:
    3
    Sorry to resurrect this thread, but it seems you guys know what you're doing. I'm trying to make the top of a stationary turret turn in a 2D Platformer by using LookAt but instead of aiming the turret at the character, it aims the top of the turret at him. I want it to move smoothly as well but meanstreak's code isn't working. My turret just turns all over the place. This is my turret script with meanstreak's code instead of LookAt:
    Code (csharp):
    1. var ShootSpeed : int = 1;
    2. var fire : boolean = false;
    3. var IdleRotateSpeed : int = 2;
    4. var Bullet : GameObject;
    5. var Idle : boolean = true;
    6. var target : Transform;
    7. var BulletSpeed : int = 1000;
    8.  
    9. function OnTriggerEnter (other:Collider) {
    10. if(other.gameObject.CompareTag("Player"))
    11. {
    12.     Idle = false;
    13.     fire = true;
    14.     target = other.gameObject.GetComponent(Transform);
    15. }
    16. }
    17.  
    18. function OnTriggerStay (other:Collider) {
    19. if(other.gameObject.CompareTag("Player")) {
    20.     var newRotation = Quaternion.LookRotation(transform.position - target.position, Vector3.forward);
    21.     newRotation.x = 0.0;
    22.     newRotation.z = 0.0;
    23.     transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 8);
    24.     if(fire) {
    25.    
    26.         Fire();
    27.         }
    28.     }
    29. }
    30.  
    31. function OnTriggerExit (other:Collider) {
    32. if(other.gameObject.CompareTag("Player"))
    33. {
    34. Idle = true;
    35. fire = false;
    36. }
    37. }
    38.  
    39. function Update () {
    40. if(Idle) {
    41. idle();
    42. }
    43. }
    44.  
    45. function Fire () {
    46. fire = false;
    47. var shot = Instantiate (Bullet, transform.position, transform.rotation);
    48. shot.rigidbody.AddForce (transform.forward * BulletSpeed);
    49. yield WaitForSeconds(ShootSpeed);
    50. fire = true;
    51.  
    52. }
    53.  
    54. function idle () {
    55.  
    56. print ("Doing nothing");
    57.  
    58. }
    Any help is appreciated thanks. And by the way, I'm not using all of the variables yet, I'm trying to get this to work first.
     
  10. EnglishMuffin101

    EnglishMuffin101

    Joined:
    Dec 14, 2011
    Posts:
    3
    Sorry, i forgot to say, my turret is two parted, with a top and a base, the base is stationary and the top should rotate.
     
  11. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    Ive seen this thread before and its suggestion for setting the values you dont want to rotate to zeros. It never worked for me. I would end up as you say, with the rotation pretty much going all over the show.

    The best approach is just to rotate around the axis you want to change... ie.

    transform.Rotate (1, 0, 0);

    reverse...
    transform.Rotate(-1, 0, 0);
     
  12. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    also, you are probably going to want to update your addforce to add the extra parameter for force type. For a bullet, use velocityChange
     
  13. EnglishMuffin101

    EnglishMuffin101

    Joined:
    Dec 14, 2011
    Posts:
    3
    I'm not really sure how you would set that up... I'm slightly new to the whole javascripting... Can you give an example of how that would work?
     
  14. yuriythebest

    yuriythebest

    Joined:
    Nov 21, 2009
    Posts:
    999
    THANK YOU!!! this post from 2009 solved hours of "bad rotation"
     
  15. helioraanswer

    helioraanswer

    Joined:
    Apr 7, 2009
    Posts:
    412
    Just an update.

    If you want a simple 2 object turret that cannot aim up/down use the folowing code:

    var newRotation = Quaternion.LookRotation(transform.position - target.position);
    newRotation.z = 0.0;
    newRotation.x = 0.0;
    transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 8);

    Vector3.forward needed to be removed for that.

    I will use this for enemy units in a 3rd person action adventure with platforms which have different heights.
    Normally my units would simple lean forward or backward with their whole body if you were on a different height.
    Now that problem won't be there.

    Thanks to above posters, this has been a thorn in my eye for quite some time.
     
    Lord-Megabite likes this.
  16. George Foot

    George Foot

    Joined:
    Feb 22, 2012
    Posts:
    399
    Don't blat over the X and Z components of a quaternion - instead, take the target vector, blat its Y component, and then pass it into LookRotation.

    Code (csharp):
    1.  
    2. var flatVectorToTarget = transform.position - target.position;
    3. flatVectorToTarget.y = 0;
    4. var newRotation = Quaternion.LookRotation(flatVectorToTarget);
    5. ... Slerp as before ...
    6.  
     
    spaceemotion, Ben-BearFish and MaDDoX like this.
  17. Mish

    Mish

    Joined:
    Apr 23, 2010
    Posts:
    96
    Could anyone provide a thorough explanation of what happens when you set quaternion components to 0 vs when you set the target vectors values to 0?
     
  18. RealSoftGames

    RealSoftGames

    Joined:
    Jun 8, 2014
    Posts:
    218
    ok here is a simple fix based on andeeeee Method, how ever, that method did not calculate for your Ai being on uneven terrain (Hills etc). by changing point.y = 0.0f; to point.y = transform.position.y; makes the (Ai) look forward no matter what, use IK perhaps to make the top half of the body or head look towards the player. good luck all =)

    and thanks to andeeeee for helping me battle this one my self for a while =)


    Code (CSharp):
    1. public Transform target;
    2.  
    3.  
    4.         var point = target.position;
    5.         point.y = transform.position.y;
    6.         transform.LookAt(point);
     
  19. CoCoNutti

    CoCoNutti

    Joined:
    Nov 30, 2009
    Posts:
    454
    This thread is great, so thanks to posters for saving me hours
     
  20. Shade_

    Shade_

    Joined:
    Dec 29, 2013
    Posts:
    86
    K I know this is old, but I just had to say that this saved my life. And I wanted to give back to the community. This is the code I used to disable 1 axis using the LookAt function. I recommend the moderators pin this since I'm sure many games are going to have turrets in them :)

    Code (csharp):
    1.   public GameObject target_con;
    2.  
    3.    Vector3 target_point;
    4.  
    5.    // Update is called once per frame
    6.    void Update ()
    7.    {
    8.      target_point = target_con.transform.position;
    9.      target_point.y = 0;
    10.  
    11.      //transform.LookAt(target_con.transform.position,transform.up);
    12.      transform.LookAt(target_point);
    13.    }
    This disables the object from being able to look up and down but will still follow the target left and right.
    In the inspector you drag your target into the 'target_con' variable.

    Hope it helps!
     
    dyupa and gohst like this.
  21. lance12367

    lance12367

    Joined:
    Dec 30, 2015
    Posts:
    2
  22. JadedStone

    JadedStone

    Joined:
    Sep 11, 2017
    Posts:
    4
    I am doing basically this same thing using Quaternion.LookRotation and Slerp. What I am running into as a problem is that my object is rotating correctly on the Y, however it ends up faceing the Z of my object toward my intended target/object. I want the X value to be what faces my target not the Z. This is driving me insane, all due to how Unity looks at X Y and Z. Not sure why they didnt have Z as up and down rather than Y to begin with it is just odd to me. Just wish there were an easy way to tell the Slerp to use X as it's nose to hunt down my target lol
     
  23. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    692
    Unity thinks of +Z as "forward." It is an arbitrary decision, and different engines will choose differently.

    The simplest solution here is to introduce an intermediate Empty object between the original parent and the model you are trying to turn.

    * MyTankModel
    * TurretRotationEmptyObject
    * TurretModelWithXBarrel


    Orient it such that the +X of your turret is pointed in the same direction as the +Z of the Empty. Now you can use the Unity functions like RotateTowards or Lookat or LookRotation on the Empty, and the child object will rotate along as you like.
     
  24. JadedStone

    JadedStone

    Joined:
    Sep 11, 2017
    Posts:
    4
    Sadly I tried this as well, it didnt work. The empty object seemed to take on it's own life. I moved my script to the empty hopeing it would rotate for me instead of the actual object thus allowing me to "fake" rotate the X to where I needed it.
     
  25. JadedStone

    JadedStone

    Joined:
    Sep 11, 2017
    Posts:
    4
    Halley thank you you inspired me to give it another try. I had to destroy that original gameobject cause somehow it was corrupt and nothing would make it work properly. When I recreated the new empty object and a whole new script and started from there. Once I gave the gameobject a -90 for Y and left the Model Object at 0 this oriented me correctly and suddenly I was able to get it to move. Where previously with the broken empty it would turn only one time to a degree that basically had it pointing in the dead center of the screen and would never move again. Who would have thought it was a buggy object that cause the script to stop working.