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

Control Flow Problem

Discussion in 'Scripting' started by Vija02, Jan 9, 2015.

  1. Vija02

    Vija02

    Joined:
    Jan 9, 2015
    Posts:
    2
    I have some some objects that need to run a few methods in order.
    Those methods run only if the methods before that all returns false

    For example, all object execute methodA, if any is true, then stop. If it's false, execute methodB, etc

    I've tried using flags, scripts execution order, but nothing works.
    How do i solve this? Thanks!
     
  2. SevenHams

    SevenHams

    Joined:
    Dec 27, 2012
    Posts:
    67
    If you want that to happen during one Update iteration then you need to have something external to all the objects tell them all to do it in order. For example...

    Code (CSharp):
    1. objectOne.Action1();
    2. objectTwo.Action1();
    3. objectThree.Action1();
    4. objectOne.Action2();
    5. objectTwo.Action2();
    6. objectThree.Action2();
    7.  
    This would cause the Action script on each object to wait for the previous one to be done. You can store a static boolean value that starts false during the whole mess and then gets set to true. After that you can have the Action2 script check to see if a value is true or false. You could do something like...

    Code (CSharp):
    1. public void Action2()
    2. {
    3.  
    4. if (thebool == false)
    5. DoStuff;
    6.  
    7. }
    In that case it would only do stuff if the boolean thing it checks is false. If not it just skips it and the function will do nothing.

    Alternately you could have...

    Code (CSharp):
    1. if (thebool == false)
    2. {
    3. objectOne.Action2();
    4. }
    5.  
    6. if (thebool == false)
    7. {
    8. objectTwo.Action2();
    9. }
    10.  
    11. if (thebool == false)
    12. {
    13. objectThree.Action2();
    14. }
    If anything happens that makes you want no more actions to go off you just set the static thebool to true. Then nothing else will happen.

    At least I think this is the sort of thing you're asking.
     
  3. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    Have a class centrally manage it, yes, but I'd go with the function return type being bool.

    Code (csharp):
    1.  private void TryInOrder(){
    2.     if (Object1.MethodA()) return;
    3.     if (Object2.MethodA()) return;
    4.     if (Object3.MethodA()) return;
    5.     if (Object1.MethodB()) return;
    6.     if (Object2.MethodB()) return;
    7.     if (Object3.MethodB()) return;
    8. }
    9.  
    10. //Or use loops
    11.  
    12. private void TryInOrder(){
    13.     for(int i=0;i<objects.Count;i++) {
    14.         if (objects[i].MethodA()) return;
    15.     }
    16.     for(int i=0;i<objects.Count;i++) {
    17.         if (objects[i].MethodB()) return;
    18.     }
    19. }
     
  4. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Some good examples here, but this is a perfect time to use a custom enumerator!

    Attach this script to any object. The camera on a new scene will do:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class MethodChain : MonoBehaviour {
    6.  
    7.     //We've got some chain of objects that have some method that
    8.     //must return true to move onto the next object in the chain. For
    9.     //the sake of simplicity, we'll use Foo, here:
    10.     class Foo {
    11.  
    12.         public static int count = 0;
    13.         public readonly int index;
    14.  
    15.         //Increment count each time we make a foo, so each foo has it's
    16.         //own unique index
    17.         public Foo() {
    18.             index = count;
    19.             count++;
    20.         }
    21.  
    22.         public bool ReturnsTrue(){
    23.             return true;
    24.         }
    25.     }
    26.  
    27.     //If we wanted, we can change the order of the firing by using a list.
    28.     //In this case, we wont, but the option exists.
    29.     List<Foo> foos = new List<Foo>();
    30.  
    31.  
    32.     void Awake () {
    33.  
    34.         //Make a bunch of foos.
    35.         for(var i = 0; i < 10; i ++) {
    36.             foos.Add(new Foo());
    37.         }
    38.  
    39.     }
    40.  
    41.     //Here's where the magic happens.
    42.     IEnumerable Chain() {
    43.  
    44.         foreach(var foo in foos) {
    45.  
    46.             //Just like a couroutine, we can use the yield statement in
    47.             //a regular loop to return to the function in the state we left it. In this case, it will
    48.             //keep looping through every foo that returns true until it runs out of foos.
    49.             if (foo.ReturnsTrue()) {
    50.                 yield return foo;
    51.                 continue;
    52.             }
    53.  
    54.             //We'll never get here in this example, but this is how you would stop the loop,
    55.             //if foo DIDN'T return true;
    56.             yield break;
    57.  
    58.         }
    59.  
    60.     }
    61.  
    62.     void Start() {
    63.  
    64.         //Treat the function like a collection and loop through it.
    65.         foreach (Foo foo in Chain()) {
    66.  
    67.             Debug.Log ("Foo "+foo.index+" just returned true!");
    68.  
    69.         }
    70.  
    71.     }
    72.  
    73. }
    74.  
     
    Kiwasi likes this.
  5. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    No, the non-generic IEnumerable/IEnumerator is obsolete and should never be used anymore.

    My recommendation:
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. class NewBehaviourScript: MonoBehaviour {
    4.     Func<bool>[] functions;
    5.  
    6.     void Awake () {
    7.         functions = new Func<bool>[] {
    8.             TakesNoArgument,
    9.             () => TakesAnArgument(null),
    10.             () => true
    11.         };
    12.     }
    13.  
    14.     bool TakesNoArgument() {return true;}
    15.     bool TakesAnArgument(object argument) {return true;}
    16.  
    17.     void Update() {functions.ExecuteUntil(result => result);}
    18. }
    19.  
    20.  
    21. namespace System {
    22.     using System.Collections.Generic;
    23.     public static class FuncExtensions {
    24.         /// <param name="predicate">Fed by results of source functions.</param>
    25.         public static void ExecuteUntil<T>(this IEnumerable<Func<T>> functions, Func<T, bool> predicate) {
    26.             foreach (var function in functions) if ( predicate(function()) ) return;
    27.         }
    28.     }
    29. }
     
    Last edited: Jan 10, 2015
    Kiwasi and BenZed like this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Unless you are building a coroutine ;)

    On a serious note nice job, I was going to suggest a similar thing based on the first couple of posts.
     
  7. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    StartCoroutine should take an IEnumerator<YieldInstruction>, not an obsolete IEnumerator. I don't think it would cause any problems if Unity just added that overload, would it?

    I don't know that this has any practical uses...
    Code (CSharp):
    1. namespace UnityEngine {
    2.     public static class CoroutineExtensions {
    3.         public static void StartCoroutine<T>(this MonoBehaviour monoBehaviour, IEnumerable<T> coroutine)
    4.         where T: YieldInstruction
    5.         {monoBehaviour.StartCoroutine(coroutine.GetEnumerator());}
    6.     }
    7. }
    ...but it was fun to see that it worked:
    Code (CSharp):
    1. this.StartCoroutine(Enumerable.Repeat(new WaitForSeconds(1), 10)
    2.     .Select((waitInstruction, i) => {
    3.         Debug.Log(i + 1);
    4.         return waitInstruction;
    5.     })
    6. );
     
    Last edited: Jan 10, 2015
    Dantus and Kiwasi like this.
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This would be much, much better. It would also make coroutines instantly easier to understand in the context of the .NET frame work.
     
  9. Vija02

    Vija02

    Joined:
    Jan 9, 2015
    Posts:
    2
    Thanks for all the replies!
    I had problems before because i handle all the actions on every objects.
    Somehow didn't think of handling all of it in a place, probably cause i have 80-ish object.

    Should be working right now. I don't understand all the IEnumerator stuff yet, i'll probably take a look at them later
    Thanks again!
     
    Jessy likes this.
  10. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Personally, I'd consider using a delegate with a bool return for this case, and asynchronous use of the delegate. If any of that sounded like gobbledygook to you, you'll probably want to use another of the presented approaches for now.
     
  11. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    What can be added to my Func<bool>-based approach to add asynchronicity? I don't know a lot about asynchronous programming yet.