Control Flow Problem

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!

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…

objectOne.Action1();
objectTwo.Action1();
objectThree.Action1();
objectOne.Action2();
objectTwo.Action2();
objectThree.Action2();

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…

public void Action2()
{

if (thebool == false)
DoStuff;

}

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…

if (thebool == false)
{
objectOne.Action2();
}

if (thebool == false)
{
objectTwo.Action2();
}

if (thebool == false)
{
objectThree.Action2();
}

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.

Have a class centrally manage it, yes, but I’d go with the function return type being bool.

 private void TryInOrder(){
    if (Object1.MethodA()) return;
    if (Object2.MethodA()) return;
    if (Object3.MethodA()) return;
    if (Object1.MethodB()) return;
    if (Object2.MethodB()) return;
    if (Object3.MethodB()) return;
}

//Or use loops

private void TryInOrder(){
    for(int i=0;i<objects.Count;i++) {
        if (objects[i].MethodA()) return;
    }
    for(int i=0;i<objects.Count;i++) {
        if (objects[i].MethodB()) return;
    }
}

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:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MethodChain : MonoBehaviour {

    //We've got some chain of objects that have some method that
    //must return true to move onto the next object in the chain. For
    //the sake of simplicity, we'll use Foo, here:
    class Foo {

        public static int count = 0;
        public readonly int index;

        //Increment count each time we make a foo, so each foo has it's
        //own unique index
        public Foo() {
            index = count;
            count++;
        }

        public bool ReturnsTrue(){
            return true;
        }
    }

    //If we wanted, we can change the order of the firing by using a list.
    //In this case, we wont, but the option exists.
    List<Foo> foos = new List<Foo>();


    void Awake () {

        //Make a bunch of foos.
        for(var i = 0; i < 10; i ++) {
            foos.Add(new Foo());
        }
 
    }

    //Here's where the magic happens.
    IEnumerable Chain() {

        foreach(var foo in foos) {

            //Just like a couroutine, we can use the yield statement in
            //a regular loop to return to the function in the state we left it. In this case, it will
            //keep looping through every foo that returns true until it runs out of foos.
            if (foo.ReturnsTrue()) {
                yield return foo;
                continue;
            }

            //We'll never get here in this example, but this is how you would stop the loop,
            //if foo DIDN'T return true;
            yield break;

        }

    }

    void Start() {

        //Treat the function like a collection and loop through it.
        foreach (Foo foo in Chain()) {

            Debug.Log ("Foo "+foo.index+" just returned true!");

        }

    }

}
1 Like

No, the non-generic IEnumerable/IEnumerator is obsolete and should never be used anymore.

My recommendation:

using System;
using UnityEngine;
class NewBehaviourScript: MonoBehaviour {
    Func<bool>[] functions;

    void Awake () {
        functions = new Func<bool>[] {
            TakesNoArgument,
            () => TakesAnArgument(null),
            () => true
        };
    }

    bool TakesNoArgument() {return true;}
    bool TakesAnArgument(object argument) {return true;}

    void Update() {functions.ExecuteUntil(result => result);}
}


namespace System {
    using System.Collections.Generic;
    public static class FuncExtensions {
        /// <param name="predicate">Fed by results of source functions.</param>
        public static void ExecuteUntil<T>(this IEnumerable<Func<T>> functions, Func<T, bool> predicate) {
            foreach (var function in functions) if ( predicate(function()) ) return;
        }
    }
}
2 Likes

Unless you are building a coroutine :wink:

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

StartCoroutine should take an IEnumerator, 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…

namespace UnityEngine {
    public static class CoroutineExtensions {
        public static void StartCoroutine<T>(this MonoBehaviour monoBehaviour, IEnumerable<T> coroutine)
        where T: YieldInstruction
        {monoBehaviour.StartCoroutine(coroutine.GetEnumerator());}
    }
}

…but it was fun to see that it worked:

this.StartCoroutine(Enumerable.Repeat(new WaitForSeconds(1), 10)
    .Select((waitInstruction, i) => {
        Debug.Log(i + 1);
        return waitInstruction;
    })
);
2 Likes

This would be much, much better. It would also make coroutines instantly easier to understand in the context of the .NET frame work.

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!

1 Like

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.

What can be added to my Func-based approach to add asynchronicity? I don’t know a lot about asynchronous programming yet.