How to make two statemachines communicate with each other?

I want to create a system where let’s say there are 4 enemies and they are all in range to attack the Player, then only one can attack at a time. When it’s done attacking and retreats, then another enemy will try to attack the Player.

Is there a clean way of communicating between the enemy statemachines to let them know who’s attacking right now?

Thank you,
Seemanta

1 Like

Yes, is possible, I might not be the absolute best but here’s how is approach it.

  1. Create a script and attach it to your enemy object(simple enough), “EnemyController” will be mine

  2. Create a static variable to represent the attacking enemy:
    public static EnemyController attackingEnemy;

  3. In the “Attack” state of the enemy’s state machine, set the attackingEnemy variable to the current enemy:
    void Attack() { attackingEnemy = this; // do attack }
    4.In the “Retreat” state, set the attackingEnemy variable to null:
    void Retreat() { attackingEnemy = null; // do retreat }
    5.In the “Idle” state, check if the attackingEnemy variable is null before attempting to attack:
    void Idle() { if (attackingEnemy == null) { // do attack } else { // wait } }
    This hopefully would allow you to achieve what you want, hope this helps!

1 Like

This sounds like a job for a Blackboard…

Blackboards allow you to have a sort of “memory” so that states can keep track of important things. For example, if I were following a series of waypoints, I might have a Blackboard class on my StateMachine, and then the PatrolState state might store the current waypoint like

stateMachine.Blackboard["CurrentWaypoint"] = 1;

and when going back to the Patrol State, I can then poll the blackboard:

if(stateMachine.Blackboard.ContainsKey("CurrentWaypoint") currentWaypoint = (int)stateMachine.Blackboard["CurrentWaypoint"];

The tricky part here, though, is that it has to be a shared blackboard. All the enemies need to know who is currently holding the talking stick.

Let’s start with a Blackboard class I’ve already written for a future tutorial for this course:

Blackboard.cs
using System.Collections.Generic;
using UnityEngine;

namespace RPG2.States
{
    public class Blackboard : MonoBehaviour
    {
        private Dictionary<string, object> blackboard = new Dictionary<string, object>();

        public object this[string key]
        {
            get => !blackboard.ContainsKey(key) ? null : blackboard[key];
            set => blackboard[key] = value;
        }

        public bool ContainsKey(string key) => blackboard.ContainsKey(key);

        public T TryGetValue<T>(string key, T fallback)
        {
            if (!ContainsKey(key)) return fallback;
            if (blackboard[key] is T t)
            {
                return t;
            }
            return fallback;
        }
    }
}

This Blackboard class looks tricky, but on the face of things, it’s fairly simple. I’m not going to focus too much on the details, but suffice to say, you can store quite literally anything in this Blackboard under a string key, and then access that thing via the key. The Dictionary is a Dictionary<string, object>, so you can put any value you wish. You just have to convert that object back into what you want.

Now this Blackboard is currently set up to just operate individually. It’s attached to the StateMachine and referenced just like our other components, and it’s designed to mimic the important features of a Dictionary (important for us… I may extend the rest of Dictionary’s behaviors at a later date.

public Blackboard Blackboard {get; private set;}

So how do we make a SHARED blackboard? Let’s take this class and extend it a bit, so that it functions as both an individual and a shared blackboard. The trick to this is statics.
Let’s add this to our Blackboard class:

        private static Dictionary<string, object> sharedBlackboard;
        public static Dictionary<string, object> Shared
        {
            get
            {
                if (sharedBlackboard==null) sharedBlackboard = new Dictionary<string, object>();
                return sharedBlackboard;
            }
        }

All we’ve really done here is ensure that there is a static Dictionary Shared available to Blackboard…
To access the regular personal Blackboard, use

statemachine.Blackboard

To access the shared Blackboard, use

Blackboard.Shared

With this, you can now have the currently attacking set that he is attacking EnemyAttackState.Enter, with BlackBoard.Shared["CurrentAttacker"] = stateMachine, and clear it in EnemyAttackState.Exit. You can then make a new state to do something like move away… Don’t enter the Attack state if Blackboard.Shared indicates there is a current attacker:

if(Blackboard.Shared.ContainsKey("CurrentAttacker") && (EnemyStateMachine)Blackboard.Shared[CurrentAttacker]!=stateMachine) //Don't attack
1 Like

Hi @Christopher_Powell ,

This is great! Thank you for the solution. This feels very intuitive and simple. I’m quite interested in @Brian_Trotter 's approach too, where he’s used a Blackboard. I’ll take my time to try out both.

Thank’s again!

1 Like

Hi @Brian_Trotter ,

Thank you for the descriptive answer!
I had come across the concept of Blackboard when I was learning Behaviour Trees(specifically when two agents try to communicate with each other). Your Blackboard script makes much more sense than the one I had studied earlier. Thank you for this! I also learnt about indexers after going through your script!

Also, I would love to know your thoughts on Statemachine vs Behaviour Trees vs GOAP and in which situations you would use one over the others. My apologies if this isn’t the right place to ask this.

Thank you

1 Like

Both Behaviour Trees and GOAP (Goal Oriented Action Planning) are actually complex examples of state machines, albeit a bit more complex in their setups. Both can be used quite effectively to model complex behaviors.

While both topics are well outside the scope of this course (they can both be courses in their own right!), I would say which one I would use depends on the scope and size of my project, and what I’m trying to achieve.

When it comes to guard behavior, nothing beats a well crafted Behavior Tree, but for background npcs, things like populating a village with characters or beasts in the woods, GOAP can really add some realism to your game.

2 Likes

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms