Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question Moving and rotating towards a transform in Coroutine

Discussion in 'Scripting' started by Clicked_, May 11, 2024.

  1. Clicked_

    Clicked_

    Joined:
    Sep 11, 2016
    Posts:
    6
    I have a script that moves an item towards a transform in a coroutine, but I want the item to slowly increment on its local Vector3.forward while simultaneously rotating towards the item, until it reaches its destination. Right now using Vector3.MoveTowards it moves directly to the item in a straight line, and I wish to get more of a gradual curved effect wherever the transform point is placed.

    I've tried to lerp between the item's position and the desired position slowly incrementing. Can't seem to get this one to work.

    Code (CSharp):
    1.  public Vector3 GetNextBeltPosition() {
    2.      
    3.         var padding = 0.1f;
    4.         var position = itemQueueSpot.transform.position;
    5.         return new Vector3(position.x, position.y + padding, position.z);
    6.     }
    7.  
    8.     private IEnumerator MoveItem() {
    9.  
    10.         isSpaceTaken = true;
    11.  
    12.         if(beltItem.item != null && nextBeltInSequence != null && nextBeltInSequence.isSpaceTaken == false) {
    13.  
    14.             nextBeltInSequence.isSpaceTaken = true;
    15.          
    16.             var moveStep = beltManager.Speed * Time.deltaTime;
    17.             var rotationStep = beltManager.RotationSpeed * Time.deltaTime;
    18.  
    19.             Vector3 toPosition = nextBeltInSequence.GetNextBeltPosition();
    20.             //Vector3 forwardStep = beltItem.item.transform.position;
    21.  
    22.             while (Vector3.Distance(beltItem.item.transform.position, toPosition) > 0.01f) {
    23.  
    24.                 Vector3 targetDirection = toPosition - beltItem.transform.position;
    25.                 Vector3 newDirection = Vector3.RotateTowards(beltItem.transform.forward, targetDirection, rotationStep, 0);
    26.  
    27.                 beltItem.transform.rotation = Quaternion.LookRotation(newDirection);
    28.  
    29.                 //forwardStep = Vector3.Lerp(forwardStep, toPosition, moveStep);
    30.                 // beltItem.item.transform.position = Vector3.MoveTowards(beltItem.transform.position, forwardStep, moveStep);
    31.  
    32.                 beltItem.item.transform.position = Vector3.MoveTowards(beltItem.transform.position, toPosition, moveStep);
    33.  
    34.                 yield return null;
    35.             }
    36.  
    37.             isSpaceTaken = false;
    38.             nextBeltInSequence.beltItem = beltItem;
    39.             beltItem = null;
    40.         }
    41.     }
     
  2. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,094
    I'm guessing you want a homing missile behavior. Change line 32 to:
    Code (CSharp):
    1.         beltItem.item.transform.position+=beltItem.item.transform.forward*moveStep;
    2.  
    You may find it's smoother to use Quaternion.Slerp instead of RotateTowards.

    It seems a little odd that you're accessing the rotation through beltItem.transform.rotation and then accessing the position through beltItem.item.transform.position.

    It might be tidier to make beltItem reference the transform directly and then you can do something like:
    Code (CSharp):
    1.     beltItem.position+=beltItem.forward*moveStep;
    2.     beltItem.rotation=Quaternion.Slerp(beltItem.rotation,Quaternion.LookRotation(targetDirection),rotationStep);
     
  3. Clicked_

    Clicked_

    Joined:
    Sep 11, 2016
    Posts:
    6
    Yes, Kinda. I want behaviour where an object will move smoothly with "homing" behaviour between point A to point B, within a defined timeframe. If point B is closer, it will move slower to target, if it's further away it will move quicker, always ending up at the last point defined in the allotted timeframe.

    I guess the behaviour of MoveTowards but not directly in a straight line.

    That's a good point. Not sure why I was doing that. I've cleaned it up and added some improvements, but as it stands it's still too "floaty" towards the point, rather than tight movement and rotation directly to the point, finishing on its target rotation.
    Code (CSharp):
    1.     private IEnumerator MoveItem() {
    2.  
    3.         isSpaceTaken = true;
    4.  
    5.         var beltItemLocation = beltItem.transform;
    6.  
    7.         if(beltItem.item != null && nextBeltInSequence != null && nextBeltInSequence.isSpaceTaken == false) {
    8.  
    9.             nextBeltInSequence.isSpaceTaken = true;
    10.          
    11.             var moveStep = beltManager.Speed * Time.deltaTime;
    12.             var rotationStep = beltManager.RotationSpeed * Time.deltaTime;
    13.  
    14.             while (Vector3.Distance(beltItemLocation.position, nextPosition.position) > 0.25f) {
    15.  
    16.                 Vector3 targetDirection = nextPosition.position - beltItemLocation.position;
    17.                 //Quaternion targetRotation = nextBeltInSequence.itemQueueSpot.transform.rotation;
    18.  
    19.                 beltItemLocation.rotation = Quaternion.Slerp(beltItemLocation.rotation, Quaternion.LookRotation(targetDirection), rotationStep);
    20.                 //beltItemLocation.rotation = Quaternion.Slerp(beltItemLocation.rotation, targetRotation, rotationStep); // item ends up with target rotation but if it misses its mark the while codition is not met and it keeps going.
    21.  
    22.                 beltItemLocation.position += beltItemLocation.forward * moveStep;
    23.  
    24.                 yield return null;
    25.             }
    26.  
    27.             //beltItem.item.transform.rotation = nextPosition.rotation;  // Clean up rotation at the end. Works but its too 'jerky' if the object hasnt had time to rotate
    28.  
    29.             isSpaceTaken = false;
    30.             nextBeltInSequence.beltItem = beltItem;
    31.             beltItem = null;
    32.         }
    33.     }
     
  4. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,936
    To provide more realistic behavior of the missile, I would (assuming the missile is going from ground to air):
    1. Use SmoothDamp for the position and rotation
    2. Force the missile to move and face upwards only until a certain amount of time has elapsed since it was launched, then start turning and moving to the target.
    3. Set the pivot point of the missile to be the very top of the missile.

    Code (csharp):
    1.  
    2. private IEnumerator MoveItem ()
    3.     {
    4.         var beltItemLocation = beltItem.transform;
    5.  
    6.         moveStartTime = Time.time;
    7.  
    8.         while (Vector3.Distance (beltItemLocation.position, nextPosition.position) > 0.25f) {        
    9.  
    10.             Vector3 targetPosition = nextPosition.position;
    11.             Vector3 targetDirection = targetPosition - beltItemLocation.position;
    12.  
    13.             if (Time.time - moveStartTime < 0.50f) {
    14.  
    15.                 //Keep the missile moving straight up for a short duration of time
    16.                 targetPosition.x = beltItemLocation.position.x;
    17.                 targetPosition.z = beltItemLocation.position.z;
    18.  
    19.                 targetDirection = Vector3.up;
    20.             }
    21.  
    22.             beltItemLocation.rotation = SmoothDampQuaternion (beltItemLocation.rotation, Quaternion.LookRotation (targetDirection), ref smoothRotation, smoothRotationTime);    
    23.             beltItemLocation.position = Vector3.SmoothDamp (beltItemLocation.position, targetPosition, ref smoothPosition, smoothPositionTime);
    24.  
    25.             yield return null;
    26.         }
    27.     }
    28.  
    29. //Source: https://gist.github.com/maxattack/4c7b4de00f5c1b95a33b
    30. public static Quaternion SmoothDampQuaternion (Quaternion rot, Quaternion target, ref Quaternion deriv, float time)
    31.     {    
    32.         if (Time.deltaTime < Mathf.Epsilon) {
    33.             return rot;
    34.         }
    35.  
    36.         // account for double-cover
    37.         var Dot = Quaternion.Dot (rot, target);
    38.         var Multi = Dot > 0f ? 1f : -1f;
    39.         target.x *= Multi;
    40.         target.y *= Multi;
    41.         target.z *= Multi;
    42.         target.w *= Multi;
    43.         // smooth damp (nlerp approx)
    44.         var Result = new Vector4 (
    45.                          Mathf.SmoothDamp (rot.x, target.x, ref deriv.x, time),
    46.                          Mathf.SmoothDamp (rot.y, target.y, ref deriv.y, time),
    47.                          Mathf.SmoothDamp (rot.z, target.z, ref deriv.z, time),
    48.                          Mathf.SmoothDamp (rot.w, target.w, ref deriv.w, time)
    49.                      ).normalized;
    50.  
    51.         // ensure deriv is tangent
    52.         var derivError = Vector4.Project (new Vector4 (deriv.x, deriv.y, deriv.z, deriv.w), Result);
    53.         deriv.x -= derivError.x;
    54.         deriv.y -= derivError.y;
    55.         deriv.z -= derivError.z;
    56.         deriv.w -= derivError.w;    
    57.  
    58.         return new Quaternion (Result.x, Result.y, Result.z, Result.w);
    59.     }
    60.  
    You can play with smoothRotationTime and smoothPositionTime till it looks right.
    The smoothDamp position causes missile to slow down as it approaches the target. Although, I'm not sure why you want that behavior.
     
    Last edited: May 13, 2024