0
Answered
Steve 2 months ago • updated by Lazlo Bonin (Lead Developer) 2 months ago 16

Hello,

Wondering how I can send events to branching flow states like in the image below? How do I call or send an message to fire the correct state? In this case, the top flow state would be a restart level, and the bottom would be a You Won state.


I should ask, can I just use start a custom event in a different flow graph to get the state machine to fire? And if so, how can you branch flows with those events?

There are a ton of ways, but what you want to do is setup the transitions.

You can have a transition with a bool, for instance PlayerScore = 1,000.  Attach it to the update state in the transition so that when True ---> Trigger Transition

You can use custom events too.  In one of your transitions have a Custom Event like "Player Died".  Then in some other FSM or script you will have a Custom Event Trigger that will go off when health is below 0.

Here are my two examples

Ahh, very clever way of doing that! Thanks, NeedsLoomis!

But, on the Start State, does that just sit empty? If so, how does the start state know what to transition to? It is solely based on checking both transitions every frame to true or valid?

I'm just not getting sending events to state machines. On both sides, setting up the station machine start state to properly receive events and then firing the Custom Event Trigger in the proper way. Feeling very discouraged here. I can do this in about 2 seconds in playmaker. Just send a custom event based on some conditions, but this is just so complicated and there are so many wrong answers to supply.

Correct, the state will check the transition to see if it should advance at the end of every frame.  So if your transition has an "On Collision Enter" it will check every frame to see if anything has collided before advancing the state.  

Outside of the transition, in the state itself, everything in "On Enter State" will happen once, everything in "Update" will happen every frame, and everything in the "On Leave State" will happen in one frame, and only when the transition activates.

Your States don't need to have anything in them.  I often leave Start empty because I don't want part of my game to activate until it is supposed to.  These are usually called "Idle" states.  They just sit there waiting until the transition is true.

I'll explain custom events thoroughly, hopefully it will help.  In BOLT, your events need a Messenger object.  This can be any object in your scene.  Until you are used to it, I recommend using the same object for everything, like an object named "GobalEvents".  It looks like you are using one called "GameManager02".  Make sure this object is in the GameObject slot (the cube) of both the CustomEvent and the CustomEventTrigger.  

Events, as you know, have two units, the "Custom Event Trigger" which sends a message to the messenger object, and the "Custom Event" that listens to the messenger object for a specific message, and then activates when it gets one.  The Custom Event and the Custom Event Trigger can be anywhere in your scene, even on two different things in two different State Machines, and you can have multiples.  Just make sure they all listen and broadcast the correct message to the same object.

I will post again with some ScreenShots using an example just like yours.

So here's the blueprint to a simple little maze game.  If you touch a wall you die, and if you touch the end you win.

We see a wall that works just like your example.  The transition checks for a collision with a collider containing the tag "Player".  Then it advances to the next state.  The state sends a message called "Kill Player".



As you can see, the Event is very simple.  It trigger is subscribed to a listener/broadcaster named "Global" (some other game object somewhere) and it sends the message "KillPlayer".

You'll notice mine works a little differently than yours.  I use the collision on the transition, and the state to send the event.  I highly recommend when starting out to think of Transitions as "If" and States as "Then"  IF there is a collision THEN trigger KillPlayer event.


Now I will show you my Player object.



Here, I have a simple transition.  Custom Event listens to the broadcaser (Global), for the message (KillPlayer).  When it hears the message, it triggers the transition to the Death state where I do stuff like reset the players position.  One frame later (due to update transition) I go back to start to Idle until we press play.  

IF "KillPlayer" event is received THEN reset player position.

Somewhere else is another FSM with the same Event listening to the same Global object that will transition to a state that displays a "YOU DIED" message.  

Heres a tip, strings are weak, a single typo can throw a wrench in your system.  I recommend making strings in BOLTS scene variables.  Then when you need an event, not only you have a nice list of them in front of you, but you can use the fuzzy finder and type "KillPlayer" and find the "Get KillPlayer Variable" unit, which you can link to the message box in your event to prevent broken links. 

Thanks for the detailed feedback! That does clear some things up, like basing things around transitions, not current state. Below is what I'm trying to do:


You will have the excuse the crudeness of the diagram. Installed a new CPU today, now my tablet doesn't work. 

But, yes, separate game objects, From a flow graph that says if cubes y position is equal to or less than -1, a  custom event fires to a separate object called GameManager. GameManger is a state machine with one Idle state, and two finishing states. One is Restart the game, the other is load a You Won screen. 

But yeah, I can't get the states to fire. It seems to have more with mistakes in getting the Custom Event Trigger to correctly set up.

Finally, BOLT vs PlayMaker.

Playmaker is a great tool.  Its biggest boon and bane is the fact that it is relatively simple.  All of its actions are "Bread and Butter" chunks of code that do the complex stuff behind the scenes.  The downside to this is that when you want to do something a little less common, even something uncommon but simple, you either are stuck, forced to make large elaborate work arounds, make your own custom actions, or interface with your own scripts (which playmaker is absolutely terrible at).

BOLT is the opposite.  It strips away all the premade stuff and gives you the internals.  Everything you do in BOLT is either actual Unity code or based on it.  Theres practically nothing you can do in code that you cant do in BOLT.  In fact, if you start writing code, Bolt will automatically add your stuff into itself as usable Units.  On the downside, all this means that sometimes you have to arrange lots of little bits into something bigger, where in playmaker you start with something bigger.  On the plus side, you can arrange those bits however you want.  In short, learning BOLT is learning how to code.


In my above game, the code would look like this:

//Somewhere else an event called "KillPlayerEvent" would be defined.  
//In this case we will imagine it's on on object called "Global" to tie in to the BOLT example.
public class Wall : MonoBehaviour 
{
    void OnCollisionEnter(Collision thingThatCollided)
    {
        if (thingThatCollided.CompareTag("Player") == true)
        {
            KillPlayerEvent.Invoke();
        }
    }
}
Public class Player : MonoBehaviour
{
    void Start()
    {
        KillPlayerEvent.AddListener(Death());
    }
    
    public void Death()
    {
        //Do death stuff
    }
}

Im willing to bet that if you don't code, but have some BOLT experience, you can kinda get the gist of what's happening up there.  

Wow, thank you so much for this! I will try to implement again. I have to read all of this thoroughly tonight. Just wanted to reply now to say thank you, and again will read in detail.

But, the info about playmaker and bolt was good to hear as well. I was wondering why I can do so much when understanding so little.

I will look at your pic and get back to you tonight, you managed to post while I was writing :D  Glad to hear it helps!

+1

OK, so I will walk you through everything.  In this example I will will use the GameManager itself as the broadcaser/listener.  As I said before, this could be any object, it just has to be the same on all Custom Events that are part of the same chain.

The starting state effectively has an invisible transition, something like an "On Game Start" transition that activates itself.  We can think of it as "IF game starts".  Then we move to the state itself which, in your case, does nothing; "THEN do nothing".  

So lets go ahead and create a start state on our cube.  Notice that I cleared everything out of the state and renamed it to make its purpose obvious and avoid potential overhead (Lazlo will have to chime in on that second part).


So now we have a state doing nothing forever.  Lets lets hook up another state, and work on the transition.  

"IF the Cube (Self) moves to -1 or less"...

(Here I am isolating the X dimension.  Much like real code, there are many ways to do the same thing)

Now that we have a transition, we can move on and edit the next state. Clear naming and cleaning out stuff you don't use is a good idea. 

"THEN an event message called "Restart" is broadcasted to GameManager"


Great!  Now we move on to the GameManager.  Similarly we want an empty Start state. "IF the game starts, THEN do nothing". 


How about we go a little further this time and do two states.

"IF an event called "Restart" is received from GameManager(self in this case), THEN output text saying you lost"

 Followed by:

"IF 3 seconds pass, THEN restart the level"


Lookin good!  Time to test!

https://gfycat.com/gifs/detail/LavishImpartialChimpanzee

It works!  (Sorry about the error, after 3 seconds it tries to load Level1, which doesn't exist!)

From here it's the same process to branch your GameManager "Start" state to another "Win" state using some other  event or some win condition in your IF transition.

Just to show there are a million ways to do any one thing, here's an example without events at all, and no FSMs on anything but the GameManager.  We just save the cube as a Variable, then we can reference it anywhere.  In this case, we will reference it in the GameManager after reusing some of the logic we did in the first example.

IF our scene variable "Player" (the cube) moves to -1x, THEN do stuff"



In case you were wondering, if you wanted to do this through a script on your GameManager (also called GameManager), the code for this would be:

using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
    
    GameObject player;
    
    void Start ()
    {
        player = GameObject.Find("Cube");
    }
    
    void Update ()
    {
        if (player.transform.position.x <= -1)
        {
            GameOver();
        }
    }
    
    void GameOver()
    {
        Debug.Log("Game Has ended!");
        SceneManager.LoadScene("Level1");
    }
}

You'll notice lots of parallels!  The only thing not here is the Wait for 3 seconds, because that code would make this a little harder to read (BOLT's yield function is more tidy!).

NeedsLoomis.

Thank you so much for this! This is incredibly helpful and just what I've been looking for. I don't know if you're part of the Bolt team, or just a very talented user, but this is fantastic. I was reading your previous post and learned quite a bit, but really spelling it out like this is perfect - and probably time consuming on your part. I really do appreciate you taking the time to put this together. Getting late my time so I will dive in tomorrow morning.

Cheers,

Steve

<3  No problem!  Im just a dude who knows what it's like being self trained ;P

+1
Answered

Wow NeedsLoomis, thanks for chiming in while I was away :) That's the community spirit!

Glad you got it working Steve. :)