POST
Joining the Living: Simple Blackboard Architectures for Agent Knowledge
Your AI agents have data that they need to store and reason about. This could be as simple as having data members sprinkled throughout your classes/components. While this gets the job done, it can quickly get messy when you’re wanting to view these values or serialize them to disk, pass them around, etc.
To that end, creating a data structure to store agent information (i.e. per agent) has a nice value add. A simple struct can suffice here:
struct AgentData
{
Vector3 mTargetPosition;
int mDifficultyLevel;
int mAmmo;
float mHealth;
// etc.
};
Structures for centrally storing information can essentially be used as “Blackboards”. Many components can write to and read from that data structure in order to facilitate basic communication between one another as well as a mechanism for objects to influence the behavior of one another in an implicit way (i.e. as opposed to each component calling a function or setting data directly on its peers).
In a couple of setups I’ve used accessor functions to determine the setting of a “dirty” flag so that I would only re-run the AI decision making when the input data had changed. As an aside, that seems to work much better for highly reactive AI than for any sort of planning or long running behaviors, but arguably that has a lot to do with the way that your behaviors and underlying decision making systems are implemented.
Accessor functions can also be quite handy for setting bits of read only data that are to be calculated from other variables either as conveniences or otherwise.
The first time I went about this, I started thinking about how nice it would be to have a representation that would facilitate storing arbitrary bits of data; essentially a map of values. And that’s exactly what I did and it was great for early prototyping of AI. But in the end, the extra layer there, and cost associated with table lookups, were hard to justify. Unless you’re doing something truly wild, the set of data stored with any particular AI will stabilize. At which point any sort of map hurts that as there’s no guarantee that any particular piece of data will be available (or rather, another mechanism would need to be present to enforce that constraint).
Also, and this is something that isn’t necessarily all that evident at the outset but is obvious in hindsight, adding new pieces of information based on whim or immediate need, without thinking about the implications or overall structure, leads to a messy and difficult to utilize pile of data.
There is however one feature that I implemented in the mapped value system that did not translate well to a structure, and that is automatically expiring pieces of data. Entries could be added to the map with an expiration time and they would get removed at that point. This made things like arbitrarily flagging specific AI with bits of data. But, strictly speaking, this hardly a critical feature.
It’s extremely tempting to develop elaborate and feature heavy systems for your agent knowledge. After having given in to that temptation, and come to see the value in a more pious approach, my advice to you is to start with the most simple implementation possible (which is inherently just a struct/class with members) and move on from there.
-r