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. Dismiss Notice

Checking if rotation is complete

Discussion in 'Scripting' started by Moderatgamer, Jan 31, 2018.

  1. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21
    So I have a simple path following script set up where the object will rotate to a transform and move towards it.
    My problem is that I don't want to object to start moving till it has fully rotated. I have a simple if statement to check this but it seems to only work on the first pass. Any help would be greatly appreciated.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class R2D2_logic : MonoBehaviour {
    6.  
    7.     public Transform[] path;
    8.     public float speed = 5.0f;
    9.     public float rotspeed = 0.1f;
    10.     public float rotspeedval = 0.1f;
    11.     public float reachDist = 1.0f;
    12.     public int currentPoint = 0;
    13.     public float rotation;
    14.     public int complete = 0;
    15.  
    16.  
    17.     Quaternion targetRotaion;
    18.  
    19.  
    20.  
    21.     // Update is called once per frame
    22.     void Update () {
    23.  
    24.  
    25.         float dist = Vector3.Distance(path[currentPoint].position, transform.position); // get distance between refernce and current pos
    26.  
    27.         Vector3 dir = path[currentPoint].position - transform.position;  // get target vector for pointing dirrection
    28.        
    29.         targetRotaion = Quaternion.LookRotation(dir); // gets quanterian vector dir
    30.      
    31.         transform.rotation = Quaternion.Lerp(transform.rotation, targetRotaion, Time.time * rotspeed);
    32.        
    33.        if (targetRotaion == transform.rotation)
    34.         {
    35.             transform.position = Vector3.MoveTowards(transform.position, path[currentPoint].position, Time.deltaTime * speed);
    36.             //complete = 0;
    37.  
    38.          
    39.         }
    40.  
    41.         if (dist <= reachDist)
    42.         {
    43.             currentPoint++;
    44.             //rotspeed = 0;
    45.         }
    46.         if (currentPoint >= path.Length)
    47.         {
    48.             currentPoint = 0;
    49.         }
    50.  
    51.  
    52.     }
    53.  
    54.     void OnDrawGizmos()
    55.     {
    56.         if (path.Length > 0) {
    57.             for (int i = 0; i < path.Length; i++)
    58.             {
    59.  
    60.                 if (path[i] != null)
    61.                 {
    62.                     Gizmos.DrawSphere(path[i].position, reachDist);
    63.                 }
    64.             }
    65.         }
    66.    
    67.     }
    68. }
     
  2. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    Actually, I can't see anything wrong with your code, however, it's organized kind of loosely. I would do something like:
    you only need to get the target rotation either at the start, or when you reachDist. So make it a method and call it in the start or when you reach distance.
    Then in update:
    //check distance and possibly call method to get new rotation and change currentPoint
    if(transform.rotation == targetRotation){
    //moveTowards
    }
    else{
    // lerp rotation
    }


    Like I say, I can't actually see anything wrong so maybe that wouldn't change anything.
     
    Moderatgamer likes this.
  3. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    661
    Usually lerp will never reach the end value. It rather goes to 99.999% towards it.
    For things like this you can deal with delta values. For positions that would be distance like:
    Code (CSharp):
    1. if((pos-target).magnitude < 0.001f)
    For rotation you could use angle:
    Code (CSharp):
    1. if (Quaternion.Angle(transform.rotation, targetRotaion) <= 0.01f)
    The alternative is to not use lerp but a constant step value like Vector3.SmoothDamp() does. But that does not exist for Quaternions and would be much more complicated anyway I guess. Maybe you could achieve that with euler angles.
     
    Last edited: Jan 31, 2018
    wraithseeker, resw, Mythran and 6 others like this.
  4. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21
    Thanks for the reply appreciate it, Yeah I think I will do that regardless of whether it fixes anything as it would be much cleaner, It was just all a bit over the place as I didn't actually know what I was doing when I went in to it so there was just code lying about everywhere. This was actually somewhat the cleaned up code removed a bunch of commented out failed code from there before pasting :p
     
  5. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21

    Thanks for that I was actually wondering if that was the case as I had to increase the speed of the rotation for the if statement to return true I think it was maybe overshooting with the speed increase. That's my guess anyway probably wrong. I think the problem is that the code in the if statement doesn't have to be fired between each step rather it just has to run once IDK I'm brand new to unity so I'm just pulling a strings :p I'll definitely use that magnitude code though should fix the speed problem.
     
  6. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21

    OK so I fixed the problem but know I have a new one :( the problem with the original code was that for the lerp I was using Time.time instead of Time.deltatime was causing the rotation to happen in an instant.

    My new problem is that the object should keep following the path as when it gets to the last point in the path the path sets back to 0. However now it gets back to the start and stops, it still rotates but doesn't actually move. Any help you could throw my way would be greatly appreciated. Also side note instead of taking your advice and cleaning up the code I actually made it worse. Also the reason I'm using a bool is that when I pasted the move code in to the if statement it was causing a bug where the character stuttered in its movement. I'll paste the new code for you guys to have a look at.

    Code (CSharp):
    1. public class R2D2_logic : MonoBehaviour {
    2.  
    3.     public Transform[] path;
    4.     public float speed = 5.0f;
    5.     public float rotspeed = 0.1f;
    6.     public float rotspeedval = 0.1f;
    7.     public float reachDist = 1.0f;
    8.     public float pause = 3f;
    9.     public int currentPoint = 0;
    10.     public float rotation;
    11.     public int complete = 0;
    12.     public bool turned = false;
    13.  
    14.  
    15.     Quaternion targetRotaion;
    16.  
    17.  
    18.  
    19.     // Update is called once per frame
    20.     void Update () {
    21.  
    22.  
    23.  
    24.         float dist = Vector3.Distance(path[currentPoint].position, transform.position); // get distance between refernce and current pos
    25.         //Debug.Log ("distance " + dist);
    26.  
    27.  
    28.         Vector3 dir = path[currentPoint].position - transform.position;  // get target vector for pointing dirrection
    29.         targetRotaion = Quaternion.LookRotation(dir); // gets quanterian vector dir
    30.         transform.rotation = Quaternion.Lerp(transform.rotation, targetRotaion, Time.deltaTime * rotspeed); // turns R2D2 in dirrection of next point
    31.  
    32.         //targetRotaion = Quaternion.LookRotation(dir);
    33.  
    34.  
    35.  
    36.         if (targetRotaion == transform.rotation)
    37.         {
    38.             turned = true;
    39.  
    40.         }else if (targetRotaion != transform.rotation){
    41.             turned = false;
    42.         }
    43.  
    44.         if (turned )
    45.         {
    46.             transform.position = Vector3.MoveTowards(transform.position, path[currentPoint].position, Time.deltaTime * speed);
    47.         }
    48.  
    49.         if (dist <= reachDist)
    50.         {
    51.             currentPoint++;
    52.             Debug.Log("position" + currentPoint);
    53.         }
    54.  
    55.         if (currentPoint >= path.Length)
    56.         {
    57.             currentPoint = 0;
    58.             Debug.Log("position" + currentPoint);
    59.         }
    60.  
    61.     }
    62.  
    63.     void OnDrawGizmos()
    64.     {
    65.        
    66.         if (path.Length > 0) {
    67.             for (int i = 0; i < path.Length; i++)
    68.             {
    69.                
    70.                 if (path[i] != null)
    71.                 {
    72.                    
    73.                     Gizmos.DrawSphere(path[i].position, reachDist);
    74.                 }
    75.             }
    76.         }
    77.    
    78.     }
    79. }
     
  7. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21
    I tried the magnitude thing but apparently you can't use it with Quaternion I'm thinking maybe I didn't fully get what you were trying to say. I have a new problem you'll see below apparently you can't multi quote on this forum or I would of added you to it. Any help would be greatly appreciated thanks.
     
  8. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    661
    The second line I posted is for Quaternions:
    Code (csharp):
    1. if (Quaternion.Angle(transform.rotation, targetRotaion) <= 0.01f)
     
  9. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    You just have to start adding debugs in there. Particularly in the "if (turned)" to see if that part of the program is running. Probably not, but then, why? Is the distance correct? You have to check each variable and find out why it isn't doing what you think it should do. I can't see it from looking at it. It looks like it would work.
     
  10. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21
    Guess I didn't read it over well enough :p I'll get that added right away. Thanks again.

    EDIT: Using the Angle script has sorted the new problem completely :)
     
    Last edited: Feb 1, 2018
  11. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21

    I did do some debugging and the loop is working correctly for some reason its just not moving once it gets back to the first transform reference. It's really strange as it was working fine until I added the rotation part of my code then it just started glitching out. The rotation seems to be causing me so much trouble.
     
  12. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    661
    Looking back at it you should solve it differently. As you constantly check your rotation with == instead of a delta as I suggested you might run into issues and floating point inaccuracies. just a difference of 0.0000001 could already cause this equation to be false. At least I wouldn't trust it.

    If you really want that step like behaviour, so that your enemy rotates toward the direction and then moves you should use a simple state machine.

    State 1 is Idle, nothing happens.
    State 2 is rotation, it only rotates toward its goal.
    Step 3 is moving, the unit only moves.

    So change your code like this:
    Code (CSharp):
    1. if (targetRotaion == transform.rotation)
    2.         State = EState.Moving;
    Then make one method for each state and call them in Update:
    Code (CSharp):
    1. switch (State)
    2. {
    3. case EState.Idle: DoIdle();break;
    4. case EState.Rotate: DoRotate();break;
    5. case EState.Move: DoMove();break;
    6. }
    7.  
     
  13. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    Yes, the reason your code is so hard to debug is that it isn't divided into logical units. It's haphazard, which almost always leads to unforseen events taking place.
     
  14. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    Honestly, I don't think he needs a state machine, his code just needs to be simplified and as long the order he does things is correct a state machine is not needed.

    Code (CSharp):
    1.  
    2.     void Update ()
    3.     {
    4.         Vector3 targetPosition = path[currentPoint].position;
    5.         Vector3 offset = targetPosition - transform.position;
    6.  
    7.         if(offset.sqrMagnitude<reachDist*reachDist)
    8.         {
    9.             currentPoint = (currentPoint+1) % path.length;
    10.             targetPosition = path[currentPoint].position;
    11.             offset = targetPosition - transform.position;
    12.         }
    13.  
    14.         targetRotation = Quaternion.LookRotation(offset);
    15.  
    16.         // using lerp this way is a possible cause to some of your problems
    17.         transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * rotspeed);
    18.         // so also included a movetowards so that the rotation has a minimum rotation speed
    19.         transform.rotation = Quaternion.MoveTowards(transform.rotation, targetRotation, Time.deltaTime * rotspeed);
    20.        
    21.         if(Quaternion.Dot(transform.rotation,targetRotation) >= 0.997f)
    22.         {
    23.             transform.position = Vector3.MoveTowards(transform.position, targetPosition, Time.deltaTime * speed);
    24.         }
    25.     }
    its really not that complicated, simply use two Vector3.SmoothDamp()s. One for the Quaternion's Forward, the other for the Quaternion's Up. Avoid euler angles as smooth damping by them would cause gimbal lock.
     
  15. Moderatgamer

    Moderatgamer

    Joined:
    Feb 11, 2014
    Posts:
    21
    Thanks for all the help guys I'll tidy my code up :)
     
  16. rosco_y

    rosco_y

    Joined:
    Aug 3, 2016
    Posts:
    31
    I've been wondering the same thing. I came up with:

    const float ROTATION_EPSILON = 0.1f;
    public bool DoneRotating()
    {
    Vector3 A = transform.rotation.eulerAngles;
    Vector3 B = _newRotation.eulerAngles;

    bool doneRotating = true; // set false if any checks fail

    doneRotating &= Mathf.Abs(A.x - B.x) <= ROTATION_EPSILON;
    doneRotating &= Mathf.Abs(A.y - B.y) <= ROTATION_EPSILON;
    doneRotating &= Mathf.Abs(A.z - B.z) <= ROTATION_EPSILON;

    _doneRotating = doneRotating;
    return doneRotating;
    }