Search Unity

UPDATED: Angle calculation and coordinate systems

Discussion in 'Scripting' started by cz2isq, Aug 22, 2009.

  1. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Code (csharp):
    1. var _origin = Vector3(Screen.width-50, Screen.height-60, 0);
    2. var screenPos = camera.WorldToScreenPoint (target.transform.position);
    3. var targetDir = screenPos - _origin;
    4.  
    5. var angle = Mathf.Atan2(targetDir.y, targetDir.x) * Mathf.Rad2Deg;

    I am going to lose my S*** if I don't figure this out soon. Check out the attached graphic for a visual aid.

    So I have this pointer arrow as part of my GUI and I need to rotate that arrow a certain angle to point at a game object whose position I've calculated using camera.WorldToScreenPoint. Only problem is my angle calculation is obviously incorrect because it doesn't point directly at the GO. Here's is where I'm at with that angle calculation. I have an origin point which is the pivot that the arrow graphic is free to rotate around.

    Where does my logic fall apart in the code above??

    Surely there must be SOMEONE that knows what I am missing. Please help me because I am about to throw this computer out a window and move into the woods.

    CZ2ISQ

    Please...
     

    Attached Files:

  2. Digitalos

    Digitalos

    Joined:
    Jun 1, 2009
    Posts:
    112
    I had a look at this, but I have to admit I don't know. :< I will see how tricky this is to setup in my scene, and maybe try that code and see what's what, though it looks like you know this stuff better than I.

    I know how frustrating it is when something reasonably simple just totally fails. If I was to hazard a guess based on what you said, it sounds like something may be amiss with how it's getting the screen position for the object. Would you could try and do, to test if the returned position is correct, is have an OnGUI(){} function draw a box or something at that position. Then you can at least see if the arrow is pointing at the right spot, and identify which part is breaking down maybe?
     
  3. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Wouldn't theta in your case be x/y rather than y/x?

    Tan(theta) = O / A
    theta = ArcTan(O / A)
    theta = ArcTan(deltaX / deltaY)
     
  4. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Oops, yeah you are right, and I had tested it with x/y, but the behaviour is still not accurate. Is there a different coordinate system that I should be using?
     
  5. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Okay, fixed the angle calculation problem but now I have a new problem...

    The arrow will only rotate to the LEFT and not to the right. So if the game object that it is pointing at is to the right of the first person controller it will just point straight ahead instead of going into any of the right quadrants...

    Here is the new angle calculation code where centerObject is the first person controller:

    Code (csharp):
    1. var screenPos = camera.WorldToScreenPoint (target.transform.position);
    2. centerPos = centerObject.position;
    3. dx = centerPos.x - screenPos.x;
    4. dz = centerPos.z - screenPos.z;
    5.  
    6. rotator = Mathf.Atan2(dx,dz)*Mathf.Rad2Deg - 270 - centerObject.eulerAngles.y;

    Any ideas?
     
  6. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    I'm not sure why screen width is being invoked here. You're trying to get the angle between two objects, yes? In that case, just say:

    Code (csharp):
    1. var angle = Vector3.Angle(vector01, vector02);
    Here's my all-purpose rotate towards a vector function in C#:

    Code (csharp):
    1.  
    2. private IEnumerator RotateTo() {
    3.        
    4.         Quaternion fromQuat = transform.rotation;
    5.         Vector3 posDifference = destination - transform.position;
    6.         Quaternion toQuat = Quaternion.LookRotation(posDifference - Vector3.Project(posDifference, transform.up),transform.up);
    7.         float dist = Quaternion.Angle(transform.rotation,toQuat);
    8.        
    9.         for (float i = 0.0f; i < 1.0f; i += ((float)baseRotateSpeed * Time.deltaTime) / dist) {
    10.             transform.rotation = Quaternion.Lerp (fromQuat,toQuat, i); // rotates unit in the direction of movement
    11.             yield return 0; // wait for 1 frame
    12.         }
    13.     }
    14.  
    Here it is in JavaScript:

    Code (csharp):
    1.  
    2.     function RotateTo(movePos:Vector3) {
    3.         var fromQuat = transform.rotation;     
    4.         var posDifference = movePos - transform.position;
    5.  
    6.         var toQuat = Quaternion.LookRotation(posDifference - Vector3.Project(posDifference, transform.up),transform.up);
    7.         var dist = Quaternion.Angle(transform.rotation,toQuat);
    8.  
    9.         for (i = 0.0; i < 1.0; i += (maxRotateSpeed * Time.deltaTime) / dist) {
    10.             transform.rotation = Quaternion.Lerp (fromQuat,toQuat, i);
    11.             yield;
    12.         }
    13.  
    Just paste that sucker in and you're all set. It will smoothly rotate an object towards the "movePos". Because it's already got a coroutine running, don't put this in the update function, but rather call it with "RotateTo()" whenever you need the object to update it's rotation (usually when it reaches the end of its current rotation).
     
  7. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    I was using screen.width because the arrow is rotating around a pivot and I wanted that pivot point to be relative to screen width and height. Then I was calculating the angle between that pivot point and the screenPos for the game object.

    Thanks for your code, I will give it a shot!
     
  8. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    PS: that code is good for rotation in the x,z plane. I assume y is the vertical axis in your game around which you want things to rotate.
     
  9. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Perhaps the usage of quaternions is a little over my head but I'm having trouble applying your code.

    The arrow is just a 2D graphic that has to rotate to point to the screenPos of a game object which is calculated by using WorldToScreenPoint. It sits on the GUI layer and should just rotate clockwise or anti-clockwise to point to the GO screenPos. Have never used quaternions before.

    I don't know if you saw my post directly above yours but that's what I've been using as my angle calculation and it seems to work ok except for the problem of only rotating left and not right.
     
  10. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Tried the Vector3.Angle approach using screenPos and FPC position but the results were even worse than those from my first problem.
     
  11. fivearchers

    fivearchers

    Joined:
    Apr 17, 2009
    Posts:
    716
    whichWay=Vector3.Cross(transform.forward, myTarget.position-transform.position).y;

    Basically turn right on a positive value and turn left on a negative value (or a zero to cover everything...because a zero could be behind you).
     
  12. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Yeah, if you're working with screen coordinates then quaternions probably aren't the best solution. You'll just want to keep doing what you were doing before. Make sure you're not mixing apples and oranges when working with world space vs. screen space.

    Also, remember that when you're dealing with angles, that jump from 0-360 or 360-0 has to be accounted for in your calculations. I usually do that with a couple of if statements that check whether the angular distance is positive or negative, and if negative, convert it to the positive angle equivalent.

    Also, when you're dealing with rotating objects, it's important that the object you're rotating starts out with it's nose aligned with 0 degrees (positive z in Unity world space). Otherwise instead of rotating to align its nose, you'll be aligning one of its sides, or its back -- which will make your calculations SEEM incorrect when in fact they are perfectly fine.
     
  13. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Where would I use that in my code though?


    I see you are getting the cross product of the two vectors...but where can I use that in my angle calculation?
     
  14. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Yes I am planning to incorporate an angle check with if else statements once I get the angle calculation figure out.

    Good idea on checking the initial alignment. It seems that my arrow's initial state is with its nose pointed up at 12 o'clock. But zero degrees would be pointing to a 3 o'clock position?

    EDIT: I guess what I'm saying is, since 0 degrees is at 3 o'clock I should go back and save my graphic with the nose pointed at 3 o'clock, right?


    cz2isq
     
  15. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    The nose is set at the right position for my set up but the motion it still restricted to 2 of the four possible quadrants that it could point in.

    Does anybody know why??
     
  16. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    What are the differences between world space and screen space that I could be mixing up? Coordinate systems?
     
  17. fivearchers

    fivearchers

    Joined:
    Apr 17, 2009
    Posts:
    716
    I think I used the code in this project

    Somewhere here is the thread where I was trying to figure out the this same issue (at least I think it's the same issue).
     
  18. cz2isq

    cz2isq

    Joined:
    Jul 15, 2009
    Posts:
    49
    Thanks, I'll take a look.