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