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 How to fix moving in a circle

Discussion in 'Scripting' started by Marstonmoor, May 17, 2023.

  1. Marstonmoor

    Marstonmoor

    Joined:
    Oct 19, 2022
    Posts:
    5
    I've got code that should move an object in a circle and after it object is should stop for 5 seconds. The problem is that the time intervals are irregular. First lap is ok, but next "WaitForSeconds(5f);"
    are working after a pause/interval more than 10-15 seconds.
    Where did i make mistake?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Ostatniaszansa : MonoBehaviour
    6. {
    7.     public Transform target;
    8.     public float speed = 1.0f;
    9.     public float radius = 1.0f;
    10.     public float angle = 0;
    11.     private bool isWaiting = false;
    12.  
    13.     void Start()
    14.     {
    15.         StartCoroutine(WaitForWhile());
    16.     }
    17.  
    18.     void FixedUpdate()
    19.     {
    20.         if (!isWaiting)
    21.         {
    22.             Path();
    23.         }
    24.     }
    25.  
    26.     void Path()
    27.     {
    28.         float x = transform.position.x + Mathf.Cos(angle) * radius;
    29.         float y = transform.position.y;
    30.         float z = transform.position.z + Mathf.Sin(angle) * radius;
    31.         transform.position = new Vector3(x, y, z);
    32.         angle += speed * Time.deltaTime;
    33.  
    34.         if (angle >= 2 * Mathf.PI)
    35.         {
    36.             angle = 0;
    37.         }
    38.     }
    39.  
    40.     IEnumerator WaitForWhile()
    41.     {
    42.         while (true)
    43.         {
    44.             yield return new WaitUntil(() => angle == 0);
    45.             isWaiting = true;
    46.  
    47.             yield return new WaitForSeconds(5f);
    48.             isWaiting = false;
    49.         }
    50.     }
    51. }
    52.  
     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    This is mostly because you don't really prevent FixedUpdate from running, and so angle is hardly ever zero. But besides that FixedUpdate has its own rate of execution, and so its never in sync with the coroutine.

    Here's a code that does what you'd expect, although I don't really like this approach, it is mighty confusing and prone to errors.

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Ostatniaszansa : MonoBehaviour
    6. {
    7.     public Transform target;
    8.     public float speed = 1.0f;
    9.     public float radius = 1.0f;
    10.     public float angle = 0;
    11.  
    12.     private bool doneLoop = false;
    13.     private bool isWaiting = false;
    14.  
    15.     void Start()
    16.     {
    17.         StartCoroutine(WaitForWhile());
    18.     }
    19.  
    20.     void Update()
    21.     {
    22.         if (!doneLoop)
    23.         {
    24.             Path();
    25.         }
    26.     }
    27.  
    28.     void Path()
    29.     {
    30.         angle += speed * Time.deltaTime;
    31.         var v = radius * new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
    32.         target.transform.position = transform.position + new Vector3(v.x, 0f, v.y);
    33.  
    34.         if(angle >= 2f * Mathf.PI)
    35.         {
    36.           doneLoop = true;
    37.           angle = 0f;
    38.         }
    39.     }
    40.  
    41.     IEnumerator WaitForWhile()
    42.     {
    43.         while (true)
    44.         {
    45.             yield return new WaitUntil(() => doneLoop);
    46.             isWaiting = true;
    47.  
    48.             yield return new WaitForSeconds(1f);
    49.             isWaiting = false;
    50.             doneLoop = false;
    51.         }
    52.     }
    53. }
     
  3. Marstonmoor

    Marstonmoor

    Joined:
    Oct 19, 2022
    Posts:
    5
    It doesn't work. My object does not move at all. In the code above "private bool isWaiting = false" is not used (it doesn't actually do anything). Perhaps the error is elsewhere.
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    That script is tested. Attach another object to
    target
    field in the inspector. That was your intention right?
     
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Your code has several issues. First of all, are you sure that your radius is actually greater than 0? Keep in mind that the default value in the code doesn't matter at all. Your variable is public so the only value that matters is the value serialized in the inspector. What's actually the point of "target"? It's not used in your code.

    Apart from that you actually "add" your direction vector every frame to your "current" position. Since this is done without deltaTime the actual "radius" of the circle would be much larger and would be frame rate depended. As the framerate fluctuates the radius would change so your "circle" could actually drift / move around. Are you sure that's what you want? Usually you want to move in a circle around a certain fix point or relative to some other object.

    The script would be much more versatile when it simply moves the object in local space. So you can simply parent the object to an empty gameobject. That way the parent object would determine the rotation axis as well as the position.

    Also it doesn't make much sense to mix Update / FixedUpdate with a coroutine. You can actually do everything inside your coroutine.

    Code (CSharp):
    1.     using System.Collections;
    2.     using System.Collections.Generic;
    3.     using UnityEngine;
    4.    
    5.     public class MoveInCircle : MonoBehaviour
    6.     {
    7.         public float speed = 1.0f;
    8.         public float radius = 1.0f;
    9.         public float delay = 1.0f;
    10.         public float angle = 0;
    11.         void Start()
    12.         {
    13.             StartCoroutine(Move());
    14.         }
    15.         IEnumerator Move()
    16.         {
    17.             while (true)
    18.             {
    19.                 while (angle < 2 * Mathf.PI)
    20.                 {
    21.                     transform.localPosition = new Vector3(Mathf.Cos(angle)*radius, 0, Mathf.Sin(angle)*radius);
    22.                     angle += speed * Time.deltaTime;
    23.                     yield return null;
    24.                 }
    25.                 angle = 0;
    26.                 if (delay > 0f)
    27.                     yield return new WaitForSeconds(delay);
    28.             }
    29.         }
    30.     }
    That's all. This simply rotates the object this script is attached inside the parents coordinate space. Though as I already said, make sure you actually set meaningful values in the inspector. Also make sure the gameobjects in question do not have a zero scale.
     
  6. unUmGong

    unUmGong

    Joined:
    May 13, 2023
    Posts:
    11
    Ever heard of the saying that you can lead a horse to water but you can't make it drink. I think it's a saying relevant to everybody's day.
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    This is because it is not actually relevant for the actual logic, but I've kept it because then you have a signal whether the animation is waiting or not. It is used properly in that sense, but it's up to you to make it do anything.
     
  8. Marstonmoor

    Marstonmoor

    Joined:
    Oct 19, 2022
    Posts:
    5
    It works perfectly even without Time.deltaTime!

    But you're right, the object sometimes moved using a different radius,
    because I focused mainly on making the object move and stop at all.
    Although I intuitively felt that I probably need to use Time.deltaTime
    to solve this issue.

    Anyway, thank you all very much for your help. You've helped me a lot.