WARNING - DO NOT USE THE CODE BELOW, READ TO THE END OF THE THREAD FIRST
Last few days I’ve written a behaviour tree system with the new viewGraph tools and almost everything is set up except for the variables.
Seems like blackboards are the way to go, but I’ve no idea how to use them.
Just in case you don’t know: Behaviour Trees only exist once in memory, so all the data of a tree is stored in the agent who is executing it, this data is called “Context” - so when the tree gets executed it looks like this tree.Execute(myContext);
Context, Blackboards? Where is the connection? Local data is not the only data needed, for example: I need to know where the player is located, this is global data, then if the enemy fortress/base is alerted, I call this “group data”, because it only affects a group of enemies (so if one enemy fortress is alerted, enemies on the other side of the game world are not alerted, only the ones of this specific base).
Currently my blackboard looks like this:
public class DataEntitiy
{
object storedValue;
public T Get<T>() {
T result = (T)storedValue;
return result;
}
public object Get() {
return storedValue;
}
public void Set(object value) {
this.storedValue = value;
}
}
public class DataBlackboard
{
protected Dictionary<string, DataEntitiy> data = new Dictionary<string, DataEntitiy>();
public DataEntitiy this[string key] {
get {
DataEntitiy result;
if (data.TryGetValue(key, out result)) {
return result;
}
result = new DataEntitiy();
data[key] = result;
return result;
}
}
// Other GET and SET methods
}
Using it like this: myBlackboard["PlayerPosition"].Get<Vector3>();
Context has 4 of these Blackboards
- BehaviourTree-Node specific data (e.g. a timer, only relevant for the node executing)
- local data (data about the executing agent e.g. agentHealth)
- group data (data of one enemy fortress e.g. lastTimePlayerWasSeen, isAlerted, NrOfAliveAgents)
- global data (e.g. playerPosition, currentDaytime)
Now it just feels wrong to set the data in an update loop, especially if I use Update() to set the Data like PlayerPosition and also Update() to execute the behaviourTree and therefor read the same data - agents could miss shots because they have the wrong information, which was set in the previous frame.
Overall I am not happy setting the data like this, when it may not be needed at all … is this even the correct approach or am I doing something wrong here?
Note: Some VariableNames are created in the Editor, so I can’t just write them all down in code and access them directly like context.agentRigidbody.position;
, also it would break the decoupling and I may want to reuse the behaviour trees in another project.