• 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
Question by SeanBamberger · May 08, 2016 at 08:33 AM · coroutineyieldwaitforsecondsboolcoroutine errors

Coroutine sequence not running properly

Hi $$anonymous$$,

I have a basic Enemy AI script for moving and firing using vector3 coordinates and bools. I'm trying to mangle coroutine sequences so they run in the right order and after a lot of consulting these forums (thanks all!)

I've managed to get the movement working in sequence (and the sequence does loop successfully), however the fire coroutine is not instantiating a new bullet after first initiation (the second one will just return nothing, etc). I'm thinking it's because my sequence isn't resetting the bool to make sure the bullet only fires once. Can't for the life of me figure out why though!

Any help would be much appreciated. I've tried to annotate it where possible (as I am a C# newbie so I need to remind myself constantly!), so apologies if this code is a bit scrappy.

 using UnityEngine;
 using System.Collections;
 
 public class EnemyAI001 : MonoBehaviour
 {
 
     //Me and you
     public GameObject iEnemy;
     public GameObject youPlayer;
     //So I can face you
     public Vector3 playernose;
     //How fast I travel to my destination
     public float approachSpeed;
     //How fast I travel on my pre-scripted path after arriving from my origin
     public float movementSpeed;
     //My Gun
     public GameObject weapon;
     //My Bullet
     public Rigidbody enemyBullet;
     //Control the amount I shoot
     public bool bulletAlive = false;
     //Control how fast I shoot
     public float bulletSpeed = 1f;
 
     //Vectors for travel, and bools to dictate which sequence to run
     //Origin Vector3 and Bools
     Vector3 foclc = new Vector3(0, 0, 150);
     bool originBool = true;
     bool originBoolCapsule()
     {
         return originBool && !firstBool && !secondBool && !finalBool;
     }
 
     //Destination Vector3 and Bools
     Vector3 x0y5z30 = new Vector3(0, 5, 30);
     bool firstBool = false;
     bool firstBoolCapsule()
     {
         return !originBool && firstBool && !secondBool && !finalBool;
     }
 
     //Second Destination Vector3 and Bools
     Vector3 x20y10z15 = new Vector3(20, 10, 15);
     bool secondBool = false;
     bool secondBoolCapsule()
     {
         return !originBool && !firstBool && secondBool && !finalBool;
     }
 
     //Final Destination Vector3 and Bools
     Vector3 xm20y0z45 = new Vector3(-20, 0, 45);
     bool finalBool = false;
     bool finalBoolCapsule()
     {
         return !originBool && !firstBool && !secondBool && finalBool;
     }
 
     // Assign my identity on Start
     void Start ()
     {
         iEnemy = this.gameObject;
     }
 
     public void Update()
     {
         //Keep Looking at Player 
         playernose = new Vector3(youPlayer.transform.position.x, youPlayer.transform.position.y, youPlayer.transform.position.z);
         iEnemy.transform.LookAt(playernose);
 
         //If I've just spawned
         if (originBoolCapsule())
         {
             StartCoroutine(OriginRunSquence());
         }
 
         //If I've reached my first destination, fire and move to my next one
         else if (firstBoolCapsule())
         {
             StartCoroutine(RunSquence1());
         }
 
         //If I've reached my second destination, fire and move to my next one
         else if (secondBoolCapsule())
         {
             StartCoroutine(RunSquence2());
         }
 
         //If I've reached my final destination, fire and loop back to the start
         else if (finalBoolCapsule())
         {
             StartCoroutine(RunSquence3());
         }
 
     }
 
     //Used for movement from spawn tofirst destination
     public IEnumerator OriginRunSquence()
     {
         yield return StartCoroutine(moveToDestination());
     }
 
     //Used for sequencing first movement and firing and any other coroutines.
     public IEnumerator RunSquence1()
     {
 
         yield return StartCoroutine(fire());
 
         yield return new WaitForSeconds(1.0f);
 
         yield return StartCoroutine(firstMove());
 
     }
 
     //Used for sequencing second movement and firing and any other coroutines.
     public IEnumerator RunSquence2()
     {
         yield return StartCoroutine(fire());
 
         yield return new WaitForSeconds(1.0f);
 
         yield return StartCoroutine(secondMove());
     }
 
     //Used for sequencing final movement and firing and any other coroutines.
     public IEnumerator RunSquence3()
     {
         yield return StartCoroutine(fire());
 
         yield return new WaitForSeconds(1.0f);
 
         yield return StartCoroutine(finalMove());
     }
 
     //Fire bool and fire coroutine
 
     private void reloadBullet()
     {
         bulletAlive = false;
     }
 
     public IEnumerator fire()
     {
         if (bulletAlive == false)
         {
             //I'm loading up a bullet
             Rigidbody newenemyBullet = Instantiate(enemyBullet, weapon.transform.position, weapon.transform.rotation) as Rigidbody;
 
             //I'm firing a bullet
             newenemyBullet.AddForce(iEnemy.transform.forward * bulletSpeed, ForceMode.VelocityChange);
 
             bulletAlive = true;
             yield return new WaitForSeconds(0.5f);
         }
 
         else if (bulletAlive == true)
         {
             yield break;
         }
     }
 
     //Coroutine sequences
 
     //Move to the destination from spawn
     public IEnumerator moveToDestination()
     {
         if (iEnemy.transform.position != x0y5z30)
         {
             //Move me
             iEnemy.transform.position = Vector3.Lerp(iEnemy.transform.position, x0y5z30, approachSpeed * Time.deltaTime);
             yield return new WaitForSeconds(0.5f);
         }
 
         if (iEnemy.transform.position.z <= 30.5)
         {
             //Snap me
             iEnemy.transform.position = x0y5z30;
             yield return new WaitForSeconds(1);
             originBool = false;
             firstBool = true;
             yield break;
         }
 
     }
 
     //My first move after arriving at my destination
     public IEnumerator firstMove()
     {
         if (iEnemy.transform.position != x20y10z15)
         {
                 //Move me
                 iEnemy.transform.position = Vector3.Lerp(iEnemy.transform.position, x20y10z15, movementSpeed * Time.deltaTime);
                 yield return new WaitForSeconds(0.5f);
         }
 
         if (iEnemy.transform.position.z <= 15.2)
         {
                 //Snap me
                 iEnemy.transform.position = x20y10z15;
                 yield return new WaitForSeconds(1);
                 originBool = false;
                 firstBool = false;
                 secondBool = true;
                 finalBool = false;
                 yield break;
         }
     }
 
     //My second move
     public IEnumerator secondMove()
     {
         if (iEnemy.transform.position != xm20y0z45)
         {
                 //Move me
                 iEnemy.transform.position = Vector3.Lerp(iEnemy.transform.position, xm20y0z45, movementSpeed * Time.deltaTime);
                 yield return new WaitForSeconds(0.5f);
                 //Debug.Log("Working");
         }
 
         if (iEnemy.transform.position.z >= 44.8)
         {
                 //Snap me
                 iEnemy.transform.position = xm20y0z45;
                 yield return new WaitForSeconds(1);
                 originBool = false;
                 firstBool = false;
                 secondBool = false;
                 finalBool = true;
                 yield break;
         }
     }
 
     //The last scripted move in my behaviour pattern
     public IEnumerator finalMove()
     {
         if (iEnemy.transform.position != x0y5z30)
         {
                 //Move me
                 iEnemy.transform.position = Vector3.Lerp(iEnemy.transform.position, x0y5z30, movementSpeed * Time.deltaTime);
                 yield return new WaitForSeconds(0.5f);
         }
 
         if (iEnemy.transform.position.z <= 30.2)
         {
                 //Snap me
                 iEnemy.transform.position = x0y5z30;
                 yield return new WaitForSeconds(1);
                 originBool = true;
                 firstBool = false;
                 secondBool = false;
                 finalBool = false;
                 yield break;
         }
     }
 }
 
Comment
tpusch

People who like this

1 Show 0
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
Best Answer

Answer by tpusch · May 08, 2016 at 07:08 PM

In answer to your question it appears that you never call the function reloadBullet(), so your fire function always goes through to the else if portion.

 public IEnumerator fire()
          {
          if (bulletAlive == false)
          {
              ....
              //Only gets called the first time
          } 
          else if (bulletAlive == true)
          {
              //Gets called all remaining times
              yield break;
          }
      }

One note, I would suggest using enums and a switch case in your update instead of your complex booleans. It would also make it easier to add more moves in the future if you wanted.

 public enum Sequence{origin, first, second, final}
 
  public class EnemyAI001 : MonoBehaviour
  {
     public Sequence currentStep = Sequence.origin;
     ...
     Update()
     {
         ...
         switch(currentStep)
         {
              case(Sequence.origin):
              StartCoroutine(OriginRunSquence());
              break;
              case(Sequence.first);
              ...
         }
     }

Then instead of having to change 4 bools just assign the next enum in the switch case you want to run.

  public IEnumerator firstMove()
  {
      ... 
      if (iEnemy.transform.position.z <= 15.2)
      {
              //Snap me
              iEnemy.transform.position = x20y10z15;
              yield return new WaitForSeconds(1);

              // instead of these:
              //originBool = false;
              //firstBool = false;
              //secondBool = true;
              //finalBool = false;

              // it would just be this:
              currentStep = Sequence.second;

              yield break;
      }
  }
Comment

People who like this

0 Show 2 · 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 SeanBamberger · May 08, 2016 at 08:03 PM 0
Share

Ah! Thanks for the advice! Can't believe I missed something so obvious.

I've found a working solution that changes the Coroutines back to standard functions, adds a new fire function (duplicating the old one) for each step and then uses Invoke to trigger them in a row. So instead of the coroutine it now looks like (where fire, firstMove etc were the previous coroutines):

         if (firstBoolCapsule())
         {
                 Invoke("fire1", 1f);
                 Invoke("reload3", 0.5f);
                 Invoke("firstMove", 2f);
                 //Debug.Log("I'm Still Standing");
         }

While I have you, can I just ask quickly if the Enum / Switch & Case route would be better for performance down the line? The game I'm making doesn't have hugely AI patterns (space shooter) so I don't mind lengthy copy and paste jobs, but if I'm hamstringing performance with complex bool switching, then I'll bear that in mind too while I implement your suggestion and future coding. :)

Thanks again, tpusch.

avatar image tpusch SeanBamberger · May 08, 2016 at 09:18 PM 0
Share

As far as performance goes you would need a lot of boolean comparisons before it started making an impact, so even if switch case is faster (which I don't know it it is) it would be negligible.

The main benefit would be if you wanted to say, add a third move between second and final, with the bools you would have to add a new thirdBool = false in every function as well as an && !thirdBool in every capsule function. Whereas with a swtich/case you would just need a new case, and change the transition in secondMove. Maybe not something worth re-writing what you have, but something to think about in future projects.

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.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Script gets stuck on WaitForSeconds() 1 Answer

Another yield not working problem 2 Answers

Is there any way to stop WaitForSeconds() ? 1 Answer

Understanding yield inside a for or a while loop 2 Answers

Waypoint / Yield help 1 Answer


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