Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Hodoer · May 21, 2020 at 12:06 PM · waypoint system

Making object wait when moving between waypoints

Hi,

I'm trying to make an object wait at its current location for a second or two before moving on the its next location. Applying a coroutine didn't achieve the desired effect, maybe I'm just using it wrong. Please advise. Thanks.

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class EnemyProperties : MonoBehaviour
 {
     [Header("Movement Properties")]
     public Transform[] waypoints;
     public float speed;
     public int curWaypoint;
     public bool patrol = true;
     public Vector3 target;
     public Vector3 moveDirection;
     public Vector3 velocity;
     public float entryWait = 3f;
     public float positionWait = 1f;
 
     void Awake()
     {
         StartCoroutine(EntryWait());
     }
 
     void Update()
     {
         if (curWaypoint < waypoints.Length)
         {
             target = waypoints[curWaypoint].position;
             moveDirection = target - transform.position;
             velocity = GetComponent<Rigidbody>().velocity;
 
             if (moveDirection.magnitude < 1)
             {
                 curWaypoint = Random.Range(0, 14);
             }
             else
             {
                 velocity = moveDirection.normalized * speed;
             }
         }
         else
         {
             if (patrol)
             {
                 curWaypoint = 0;
             }
             else
             {
                 velocity = Vector3.zero;
             }
         }
 
         GetComponent<Rigidbody>().velocity = velocity;
     }
 
     private IEnumerator EntryWait()
     {
         curWaypoint = 7;
         yield return new WaitForSeconds(entryWait);
     }
     private IEnumerator PositionWait()
     {
         yield return new WaitForSeconds(positionWait);
     }
 }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by ADiSiN · May 21, 2020 at 05:55 PM

Hi!

So, it's important to understand what Coroutines do and what they don't.


You should always remember that they are not creating delay for your Update() function - look at this script:

 void Update()
 {
     StartCoroutine(WaitBeforeDo());
 
     Debug.Log("I am doing something");
 }
 
 IEnumerator WaitBeforeDo()
 {
     Debug.Log("Wait before do");
 
     yield return new WaitForSeconds(5f);
 }

Theoretically we want to create delay before debugging "I am doing something", but...oh, look at the output in the console:alt text As we can see we not only continously calling the "I am doing something", but also "Wait before do", because it gets called each frame as well.

Therefore you shouldn't think of coroutine as time freezer for your main executive function, but better as an function with option to switch variables not at the same frame, but with given delay.


Now look at this code:

 bool isDoingSomething = true;
 float timer = 0f;
 
 void Update()
 {
     if (isDoingSomething)
     {
         Debug.Log("I am doing something");
         timer += 1 * Time.deltaTime;
 
         if (timer > 2f)
         {
             isDoingSomething = false;
             StartCoroutine(WaitBeforeDo());
         }
     }
 
 }
 
 IEnumerator WaitBeforeDo()
 {
     // Action BEFORE delay
     Debug.Log("Wait before do");
 
     // Delay itself
     yield return new WaitForSeconds(3f);
 
     // Action AFTER delay
     isDoingSomething = true;
     timer = 0f;
 }

And let's take a look at the console: alt text Great, now it's working.

The "I am doing something" gets called multiple times, but when we receive "Wait before do" it freezes for couple of seconds and then starts again, but only because our Coroutine swaps isDoingSomething boolean after delay, not because of the delay itself.


As you can see we now have much more variables in our code itself, so the first thing for Coroutines to be useful is proper design of your executive functions - you should have variables that will be adjusted by the Coroutine before/after given delay or otherwise you call functions with delay, but nothing changes after the delay.


So, the overall look that you would like to achieve could look like this:

 bool isMoving = true;
 
     void Update()
     {
         if (isMoving)
         {
             // Move code;
 
             if(/* We reach destination or any conditions that you want to achieve before waiting*/)
             {
                 isMoving = false;   // Stops move code execution;
                 StartCoroutine(PositionWait());     // Calls the Coroutine
             }
         }
     }
 
     IEnumerator PositionWait()
     {
         yield return new WaitForSeconds(positionWait);  // The Coroutine will wait for given seconds
         isMoving = true;       // Only after given seconds gone it will switch boolean to true so the Update() functions will starts execute code inside if (isMoving) statement
     }



I hope it helps.


ex1.png (7.8 kB)
ex-2.png (7.8 kB)
Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Hodoer · May 22, 2020 at 06:24 AM 0
Share

Hi,

Thanks for the help. I sort of understand that (a little bit) so I've re-written my code as such

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class EnemyAI_V1 : $$anonymous$$onoBehaviour
 {
     [Header("$$anonymous$$ovement Properties")]
     public Transform[] waypoints;
     public float speed;
     public int curWaypoint;
     public Vector3 target;
     public Vector3 moveDirection;
     public Vector3 velocity;
     public float entryWait = 3f;
     public float positionWait = 1f;
     public bool is$$anonymous$$oving = true;
     public bool patrol = true;
 
     void Awake()
     {
         curWaypoint = 7;
         $$anonymous$$oveToTarget();
     }
     
     void Update()
     {
         if (is$$anonymous$$oving)
         {
             $$anonymous$$oveToTarget();
         }
         else
         {
             velocity = Vector3.zero;
         }
         if (moveDirection.magnitude < 1)
         {
             is$$anonymous$$oving = false;
             StartCoroutine(PositionWait());
         }
         GetComponent<Rigidbody>().velocity = velocity;
     }
 
     void $$anonymous$$oveToTarget()
     {
         target = waypoints[curWaypoint].position;
         moveDirection = target - transform.position;
         velocity = GetComponent<Rigidbody>().velocity;
         velocity = moveDirection.normalized * speed;
     }
 
     private IEnumerator PositionWait()
     {
         yield return new WaitForSeconds(positionWait);
         curWaypoint = Random.Range(0, 8);
         is$$anonymous$$oving = true;
     } 
 }
 

I got the object to stop for the desired duration, however the object wriggles as it's moving to its next target. From the inspector panel I can see that the script is still choosing a random target while it's moving off. Is there a way for the Random.Range function to fire off only once? Or do I have to change the variable in 'if (moveDirection.magnitude < 1)' to something else? Please advise. Thank you.

avatar image Hodoer · May 22, 2020 at 10:25 AM 0
Share

Ok, I think I sort of solved the issue by implementing the random.range inside the 'if (moveDirection.magnitude < 1)' statement ins$$anonymous$$d of the IEnumerator function. Thanks for the help.

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class EnemyAI_V1 : $$anonymous$$onoBehaviour
 {
     [Header("$$anonymous$$ovement Properties")]
     public Transform[] waypoints;
     public float speed;
     public int curWaypoint;
     public Vector3 target;
     public Vector3 moveDirection;
     public Vector3 velocity;
     public float entryWait = 3f;
     public float positionWait = 1f;
     public bool is$$anonymous$$oving = true;
     public bool patrol = true;
 
     void Awake()
     {
         curWaypoint = 7;
         $$anonymous$$oveToTarget();
     }
     
     void Update()
     {
         if (is$$anonymous$$oving)
         {
             $$anonymous$$oveToTarget();
         }
         else
         {
             velocity = Vector3.zero;
         }
         if (moveDirection.magnitude < 1)
         {
             is$$anonymous$$oving = false;
             curWaypoint = Random.Range(0, 14);
             StartCoroutine(PositionWait());
         }
         GetComponent<Rigidbody>().velocity = velocity;
     }
 
     void $$anonymous$$oveToTarget()
     {
         target = waypoints[curWaypoint].position;
         moveDirection = target - transform.position;
         velocity = GetComponent<Rigidbody>().velocity;
         velocity = moveDirection.normalized * speed;
     }
 
     private IEnumerator PositionWait()
     {
         yield return new WaitForSeconds(positionWait);
         is$$anonymous$$oving = true;
     } 
 }
 
avatar image ADiSiN Hodoer · May 22, 2020 at 12:55 PM 0
Share

Great that it worked out for you!

The implementations can be different, for example, there could potentially be situation where you are already called Coroutine PositionWait(), but moveDirection.magnitude < 1 remained true therefore your Coroutine gonna be called multiple times.


As an option to avoid that you can do it this way:

 void Update()
 {
     if (is$$anonymous$$oving)
     {
         $$anonymous$$oveToTarget();
 
         if (moveDirection.magnitude < 1)
         {
             is$$anonymous$$oving = false;
             curWaypoint = Random.Range(0, 14);
             StartCoroutine(PositionWait());
         }
     }
     else
     {
         velocity = Vector3.zero;
     }
     GetComponent<Rigidbody>().velocity = velocity;
 }

Or this way:

 void Update()
 {
     if (is$$anonymous$$oving)
     {
         $$anonymous$$oveToTarget();
     }
     else
     {
         velocity = Vector3.zero;
     }
 
     GetComponent<Rigidbody>().velocity = velocity;
 }
 
 void $$anonymous$$oveToTarget()
 {
     target = waypoints[curWaypoint].position;
     moveDirection = target - transform.position;
     velocity = GetComponent<Rigidbody>().velocity;
     velocity = moveDirection.normalized * speed;
 
     if (moveDirection.magnitude < 1)
     {
         is$$anonymous$$oving = false;
         curWaypoint = Random.Range(0, 14);
         StartCoroutine(PositionWait());
     }
 }

In both options you are checking magnitude only when is$$anonymous$$oving true, but since you disable it after, then this piece of code for sure will not be executed until Coroutine PositionWait() will swap is$$anonymous$$oving to true.


Also, just for information since sometimes people don't know - the Random.Range is EXCLUSIVE when integer and INCLUSIVE when float with max value.

So, your Random.Range(0,14) never gonna take 14, but 13 as maximum.

Here is more info about that one: https://docs.unity3d.com/ScriptReference/Random.Range.html

avatar image Hodoer ADiSiN · May 22, 2020 at 01:17 PM 0
Share

Thank you for the advice. Your code looks cleaner than $$anonymous$$e, I'll implement it right now. Thanks again.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

126 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Can different variables referencing the same object return different instance IDs? 0 Answers

I need help with a waypoint system. 1 Answer

Vector2.Lerp not working properly. Horizontal jittering when not moving horizontally at all! 1 Answer

How do I make my enemy stop at specific waypoints? 0 Answers

PacMan IA of ghost using nodes/waypoints 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges