What's an easy way to have something happen just once?

So I’m trying to wrap my head around an efficient way to handle an event that happens basically once, or less.

For example, I have the very first level of my game, which naturally has a few things different from other levels. Should the player happen to die on this very first introductory level, I can’t quite handle things the same way I normally do. Normally I would drop the player back onto the world map, and then they would just try again.
But for the introduction level, maybe I don’t want the player to drop back into the world map, but just restart the level again? Or what if instead of starting the level over again from the beginning, I want them to start from a select part of the intro level?

Sure, I can add functionality for this; it isn’t exactly much to write a simple if statement.
But it really bothers me to have my code run through an if statement it would never use again. If I have my death/restart code check if that first intro level has been finished, then it will run that check every single time I ever die, or every time I ever return to the map, or wherever I put that check, it will run through ultimately hundreds or even thousands of times, when there is only ever the one point in the entire course of the game where it is actually “needed.”

And okay, one extra command, big deal. This wouldn’t even be a noticeable drop in performance on computers from 20 years ago, let alone today. I don’t have a legitimate reason to care about this. But it still bugs me, and feels like a waste. It seems to me like there should be a smarter way to handle it, rather than add an extra if statement that will probably never execute.

So, what other options are there?

bool eventTrigger;

public void MyEvent()
{
    if (!eventTrigger)
     {
           eventTrigger = true;
          // Do something
      }
}
2 Likes

You could have a delegate that stores the function called when the player dies. And then set the function based on the level - use a specific function for the first level, and then use another one for all levels. Then it won’t run an if condition each time, it will just call whichever function is in the delegate.

However, maybe the cost of calling through a delegate is higher than the cost of an if condition - I don’t really know.

You can also create special components that get removed after the event occurs.

But most of the time I would just run with @Ironmax 's solution with bools.

In other words, there is no more efficient way, and I should just accept that.

Delegates should be pretty quick if its something you do a lot of. But you are adding in another layer of abstraction in your code, that has its own overheads and code maintenance problems.

You can also use a coroutine called in Start.

There are plenty of ways to do this if you must do it.

1 Like

Its pretty efficient, dosn’t generate overheads or calls that will impact performance in any way, you can have 1000 of this . Like BoredMormon said, there are many other ways to do it, thought i find this the most “controlling/safe” way, because boolean are meant to set things to true of false, and that makes perfect sense in this case.

If you worry about performance, booleans are the way to go, bool are basically in low level machine integer 1/0; Thought if any one have a better solution, I would love to see it.

There is no shame on using the computer as a calculator :slight_smile:

2 Likes

The only real problem with a bool is something is still happening every frame. If the bool check is the only thing that happens in Update then you get an added method call. Throw a thousand extra method calls in every frame and you might notice a slow down. It’s a pretty micro optimisation, which means it’s only worth it on minor cases.

Delegates work best if you are throwing in and out a bunch of different behaviours, like one would expect in a state machine. It’s overkill for this purpose. But it does eliminate the bool check.

Coroutines are a nice option. They also have the advantage of being able to pause execution. Again, it’s overkill. But it does work. Especially if you need set and forget behaviour.

@JasonBricco and @Kiwasi have the right idea, if you want something very clean without one off unique exceptions scattered throughout your project. Create a delegate event. The function that’s called in the delegate event is based upon different scenario’s in the game. You can then have standard action events, and then add/remove a myriad of unique exception methods based on different scenarios.

If you find that there’s a large number of unique exceptions, perhaps it’s time to look at delegates. Otherwise a simple bool check is totally good enough IMO.

Delegates and events are always nice, but still you need some type of boolean. And you need to trigger the subscription at some point. Want to post some example how to use delegates as a one time event MoredMormon?

Rough pseudo code for delegates. I’ve probably messed up the syntax slightly for delegates.

MyDelegate myUpdate;

void Start (){
    myUpdate = FirstEvent;
}

void Update (){
    myUpdate();
}

void FirstEvent () {
    // Do something once
    myEvent = SecondEvent;
}

void SecondEvent () {
    // Do something else
}

And for coroutines

void Start () {
    StartCoroutine(DoStuff);
}

IEnumerator DoStuff () {
    // Do something at the start
    while (true){
        yield return null;
        // Do something every frame
    }
}

For simple cases a bool is better. But when things start to get complex there are other avenues to explore.

2 Likes

thx BoredMoron. You inspire me here, I think am gonna do some experiment to see if there are some performance gain with delegate in this kind of situation. I do see the potential for its use, it also gives a better structure of your game logic. But when it comes to Coroutine i have to disagree, if you start a coroutine like that, you can trigger it every time that script gets invoked. I never
trust coroutines 100%, thats why i never use coroutines without some booleans. Thought I do love coroutines in general.

Yeah with coroutines they’re normally just used for timed repeated events like animations and stuff. I tend to have something like this

// Tutorial stage handling variables
    private TutorialState tutorialStage;
    private bool bTutorialInProcess = false, bNextStagePending = false;

    void Start()
    {
        // Getting the ball rolling
        StartCoroutine(TutorialStageActions());
    }

    // Sets the next stage pending to be played
    public void NextTutorialAction()
    {
        bNextStagePending = true;
    }

    // Checks if a new stage is pending, and calls the coroutine upon previous stage complete
    void Update()
    {
        if(bNextStagePending && !bTutorialInProcess)
        {
            tutorialStage = (TutorialState)((int)tutorialStage + 1);
            StartCoroutine(TutorialStageActions());
            bNextStagePending = false;
        }
    }

    // Main actions in the tutorial stages
    IEnumerator TutorialStageActions()
    {
        bTutorialInProcess = true;

        // actions in WaitForSeconds are float methods beginning new coroutines for the same amount of time
        switch (tutorialStage)
        {
            case TutorialState.PreStageSetup:
                {
                    // Stuff and stuff
                    NextTutorialAction();
                    break;
                }
        }

        bTutorialInProcess = false;
        yield return null;
    }
1 Like

Yeah, they are perfect for animation , you can even sync the delay with animation time. And of course State machine with animation

1 Like

You’ve never used a coroutine as a finite state machine before? It’s a nifty trick. Here is some pseudo code for a weapon.

void Start (){
    StartCoroutine(Fire());
}

IEnumerator Fire (){
    while (true){
        ReloadWeapon();
        yield return new WaitForSeconds(reloadTime);
        while (!Input.GetKeyDown("Fire")) yield return null;
        FireWeapon();
    }
}

Note that the input check now only occurs when the weapon is actually in the state to fire. While reloading input does nothing.

Depending on your use case this sometimes leads to more readable code. It doesn’t use as much jumping around as a delegate based FSM. Not does it require as many if checks as a bool based solution.

If you have trouble trusting coroutines, check out this video. It goes into detail on the internals of a coroutine. Once you understand how they work you’ll find all sorts of uses for them.

1 Like

Well that was my point, i never trust coroutines “without” a state machine or a boolean statement. I use it on almost all projects i work on. Thought i do have my logic in a separated manager. Bool statements does not cause any performance issue, i tested that pretty thoroughly if done correctly. You must really make sure that coroutine only started once, thats why we also have the stop function. I like to think of it as a egg cook timer, dont cook to many eggs at ones. I seen overheads happing for no reason with coroutines, even they are running just ones each.

The video is good for a beginner, i am not really there atm, and made allot more advanced codes with coroutines. than the does on this video, i think he copy some of of the other Unity tech guy presentation there is this nice Danish guy explaining coroutines more technically, but I am sure its great for some one new to it.

2 Likes

I just want to confirm (after doing some tests) that using Coroutines for trigger, will cause overhead, use delegate/event or simple boolean or state machine as trigger.

Statement checks are only 1 call (in update method), so zero performance issue, thought CG allocation with coroutines is.

Also if your going to use single delegate, you have to make sure your signature dosent get lost.

1 Like