Blackboard Documentation

As promised, I’m trying to make some of the documentation I’m working on available while it’s still in progress… hopefully this is in reasonably good shape, but let me know if I need to make anything more clear (or just more interesting ;p).

Here’s the first small bit of documentation about Blackboards:

What is a blackboard?
A blackboard is a simple place where data can be written and read for decision making purposes. A blackboard can be used by a single AI pawn, shared by a squad, or used for any other purpose where it’s convenient to have a central place to look up relevant data. Blackboards are commonly used with behavior trees, but you could use them separately from behavior trees for convenience or you could make a behavior tree with no need of a blackboard.

Why use blackboards?

1. To Make Efficient Event-Driven Behaviors
Changes to the blackboard can throw events to change the flow in the behavior tree without having to update every frame to check the value. I’ll have more details about this in a separate section of Behavior Tree documentation (and I’ll link from here once that’s ready).

2. To Cache Calculations
Some data has to be calculated, which can be a pain to do repeatedly or simply expensive for performance. Therefore, it’s frequently desirable to cache the calculated value for use throughout the behavior tree so it doesn’t have to be repeatedly recalculated.

It’s more performance efficient to calculate the values less frequently, and it’s easier and less error-prone as well. If you recalculate the data in more than one place, you may accidentally calculate it differently (especially if you tweak the calculation after first creating it). Then your entire behavior may suffer from inconsistent values.

3. As a scratch-pad for behaviors
Some data you may need to use in the behavior tree doesn’t have any other obvious home. For example, when starting a sequence, you may want to note that you’ve started it so that other parts of the behavior tree don’t interrupt it. But that sequence may not readily equate to a specific class or knowledge already stored somewhere. In that case, you may want to use a Context Override* to set and clear the value.

For example, set “IsPerformingChargeAttack” true at a subtree root node (in a Context Override). Under that tree, turn to face the goal, roar, charge the goal, attack! The Boolean value for IsPerformingChargeAttack can be used to prevent higher priority behaviors from interrupting.

(* Sorry about the reference to Context Override without explanation… you can tell this documentation is a work in progress! Context Overrides are a special type of behavior Tree node in our implementation of behavior trees. I’m working on the documents that explain this type and many others. In brief, a Context Override sets values when a particular behavior starts and restores them to previous values when that behavior ends for any reason.)
(I’ll try to make some screenshots to show an example here soon.)

4. To Centralize Data
Blackboards are a nice way to centralize data and remove the need for extra knowledge about where that data comes from. Without blackboards, you often have to go through a number of steps to query information that’s stored in various different classes.

For example, get the AI Controller, and ask it for the Pawn or Character. From the Character, ask for the CrouchedEyeHeight. Or, from the Character, get the MovementComponent, cast it to your game’s special derived class MyGameCharacterMovementComponent, and then ask whether the character is sliding. Get the AI Controller and ask what its current Enemy is.

Taking all of these steps whenever you want to read data is a pain. Instead, you can use a service or some other class to post relevant data to the blackboard, and all read access can then be greatly simplified.

Centralizing data in this way is especially helpful for keeping code properly encapsulated! (If you’re not sure what code encapsulation is and you’re writing C++, you may want to read more about Object Oriented Design.)

NOTE: It’s not always best to post every bit of data you can fetch from various classes to the Blackboard. Deciding what belongs in the blackboard and what should be fetched from its primary data location is something that you need to decide on a case-by-case basis. See the other reasons to use blackboards to help make that decision.

There are a few reasons not to copy everything to the blackboard:

  1. Don’t clutter the blackboard with lots of super-specific-case data. If only one node needs to know something, it can potentially fetch the value itself rather than adding one more value to look through while studying every bit of the tree!
  2. If you fail to copy data to the blackboard properly, it may cause you some debugging nightmares! If you’re looking at a value in its source only, or in the blackboard only, you may not realize instantly that the two values differ and so are causing a problem.
  3. If enough values are very frequently updated and so need to be copied to the blackboard constantly, that could be bad for performance (though it’s not likely in most cases; if you’re not sure, I wouldn’t worry much about this issue, as it can also be MORE efficient to copy them).
5 Likes

Also see Behavior Tree Documentation In Progress.

UE4 Blackboard Implementation
Our implementation of blackboards is completely data-driven to make it as easy as possible to add properties to a blackboard. It’s also efficient for our event-driven behavior trees, because any changes to the blackboard can throw appropriate events without significant performance overhead. No need to test values periodically, as we can make any checks that may be required when the value changes!

How to create a Blackboard Asset in the Editor

To create a blackboard asset, in ContentBrowser, right-click, choose “Miscellaneous->Data Asset”, and then select “BlackboardData”.

You can add the keys you need to the blackboard section of its details view.

Further documentation coming soon (in more detail than on previous thread replies). I’ve gone ahead and made empty posts for them so you’ll have some idea what’s coming and to prevent too much interleaving with any responses and questions on the thread. We’ll see how well or poorly this works as a way to get information out there quickly before the final documentation is done.

Blackboard Asset Details View

“Blackboard” Tab:

This tab is the critical part of the details view for blackboards. It has a simple list (TArray) of entries on the blackboard. Entries consist of an “Entry Name” (such as “Enemy”) and a “Key Type” (such as “BlackboardKeyType_Object). Some types then have a further property to help refine the type definition. For example, Object Type has a “Base Class” to specify what specific classes are valid.

“Entry Name” is basically analogous to the name of the variable (entries are analogous to variables). It is an arbitrary string which is used for identifying which entry is which, so the only requirement is that every entry has a unique name. However, you should make these names as clear as possible, just as you would with variables in a Blueprint or in code.

  • “Parent” Tab:*

“Parent” is just an optional blackboard you can “inherit” data from.

For example, you may have multiple types of AI that both share a number of entries in their respective blackboards, but they also need a few specific additional entries. To accomplish that sharing, you can create a “BasicBlackBoard” asset which you use as the parent for “SpecialAI-A” and “SpecialAI-B” blackboards. In that case, the “Parent Keys” entry will show the shared keys as read-only (because they can only be edited in the parent asset itself).

Blackboard “Key Type”s

NOTE: In the “Key Type” property in the Details pane, the options all start with “BlackboardKeyType_”. Once chosen, they’ll generally append a number, like “_4”. The number is used by code for identification purposes, but it’s irrelevant to users, so you can safely ignore it. (Down the road, hopefully we’ll make this display a bit nicer.)

Here’s information about each type we support:

  • Bool: Basic Boolean type support, i.e. “True” or “False”.

  • Int: Integer type support, i.e. -2, -1, 0, 1, 2, etc.

  • Float: Floating point numerical support: I.E. -7.3, 8.523 * 10^5, etc.

  • Vector: A three-dimensional vector (FVector in code). Defaults to (FLOAT_MAX, FLOAT_MAX, FLOAT_MAX) so that you can distinguish vectors which have been set from uninitialized vectors. (If it defaulted to a zero vector (0,0,0), you’d be unable to tell if it was set but really WANTED to be zero vs. unset.)

  • Enum: For data-created enumerated types, which are assets that define a list of named values. Once you choose “Enum”, you need to specify the enum asset as well (under another subtab).

  • NativeEnum: For code-created enumerated types which are exposed to blueprints. (I.E. UENUM(BlueprintType)). Once you specify NativeEnum, you need to type in the name of that enum in code. Note that the name always starts with “E”. If you type it correctly (an hit enter), “Is Enum Name Valid” will be checked. This is a little cumbersome at the moment, so hopefully down the road we can provide a dropdown of valid enums rather than requiring they be typed by hand.

  • String: Character String (FString) support, i.e. “Hello world!” or “Get them!”

  • Name: Similar to string support, but names (FName in code) are identified by a unique identifier, so equals/not-equals comparisons are very fast.

  • Object: UObject support, i.e. most other types of complex objects in UE4, including components and actors.

  • *]Once you choose UObject, you can further specify the type to allow only actors of a specific class (or classes derived from that class).

  • Class: UClass support, i.e. the class of an object type, not the object itself.

How to Get and Set Blackboard Entries

In a Behavior Tree Node blueprint:
Use the Get… and/or SetBlackboardValueAs…() node for the appropriate type. This node doesn’t require the blackboard, as it will fetch it from the owning behavior tree.

Create a variable of type BlackboardKeySelector and use that to feed into the “Key” node.

Check the “Editable” box in the details on this variable (or just click the “eye” icon to open it). That will expose the value of the blackboard key to the node in the behavior tree on any node created from your blueprint, so you can point it to the correct blackboard entry using a drop-down.

(I’ll come back and add a screenshot here soon.)

For clarity and completeness, here are the specific Getter and Setter nodes we have for each type of blackboard entry:

  • GetBlackboardValueAsBool, SetBlackboardValueAsBool

  • GetBlackboardValueAsInt, SetBlackboardValueAsInt

  • GetBlackboardValueAsFloat, SetBlackboardValueAsFloat

  • GetBlackboardValueAsVector, SetBlackboardValueAsVector

  • GetBlackboardValueAsEnum, SetBlackboardValueAsEnum

  • *]NOTE: This function is used to get and set both “Enum” and “NativeEnum” types.

  • GetBlackboardValueAsString, SetBlackboardValueAsString

  • GetBlackboardValueAsName, SetBlackboardValueAsName

  • GetBlackboardValueAsActor, SetBlackboardValueAsActor

  • *]NOTE: This function is provided for convenience. It’s basically the same as the “AsObject” version, but it casts the result to AActor as well (since not all Objects are Actors, it can return NULL for Getting a value if it’s there but is not an Actor).

  • GetBlackboardValueAsObject, SetBlackboardValueAsObject

  • GetBlackboardValuesAsClass, SetBlackboardValuesAsClass

In any other blueprint (not part of a behavior tree):

Get the blackboard you intend to access values on (for example, in Behavior Trees you can use GetBlackboard() node if you choose not to use the Behavior Tree accessors above). (But you should probably be using the Behavior Tree accessors instead.)
Dragging off of the Blackboard, you can create GetValueAs… / SetValueAs… functions for the appropriate types.
Pass in the FName of the key you are attempting to access. This value needs to match the Entry Name property in the Blackboard asset.

Again, I’ll add a screenshot here soon.

And here are the specific getters and setters for various blackboard entry types:

  • GetValueAsBool, SetValueAsBool

  • GetValueAsInt, SetValueAsInt

  • GetValueAsFloat, SetValueAsFloat

  • GetValueAsVector, SetValueAsVector

  • GetValueAsEnum, SetValueAsEnum

  • *]NOTE: This function is used to get and set both “Enum” and “NativeEnum” types.

  • GetValueAsString, SetValueAsString

  • GetValueAsName, SetValueAsName

  • GetValueAsObject, SetValueAsObject

  • *]NOTE: There are no pre-written specializations for these. If you need a specific class such as AActor or APawn (or even a blueprint class), you need to use “Cast To” on the results from this node, and then you should determine if it returned TRUE/FALSE appropriately.

  • GetValuesAsClass, SetValuesAsClass

Some additional helpful blueprint nodes for blackboard access:

  • ClearValueAsVector: Sets the Vector value back to “invalid”, which is a vector of MAX_FLT. This basically makes the vector “unset” again, by making it match the normal “uninitialized” values of these vectors.
  • GetLocationFromEntry: Gets the location vector from any valid key type. Basically, if the key is a Vector, it will return that value. If it’s an Actor, it will return GetActorLocation() from that Actor.
  • GetRotationFromEntry: Returns the rotation of the entry if it can. Specifically, if the entry is pointing to an Actor, it calls GetActorRotation() on that actor.

Getting and Setting Values in C++:
See UBlackboardComponent in BlackboardComponent.h to find the many functions you can use.

Basically, you can call of the GetValueAs/SetValueAs functions I mentioned above for general blueprint use, but you can call them with either the Entry Name as an FName or the uint8 ID of the entry

In addition, a few functions to note are:

  • GetKeyName(uint8 KeyID) fetches the name from the ID.

  • GetKeyID(const FName& KeyName) getches the uint8 ID from the name (which you can use for caching if you like).

  • GetKeyType(uint8 KeyID) returns the class of value for the specified key.

  • You can also call the same helpful blueprint nodes I mentioned above from code:

  • *]ClearValueAsVector (taking KeyName or KeyID)

  • *]GetLocationFromEntry

  • *]GetRotationFromEntry

Using Blackboards with Behavior Trees

To use a blackboard asset you’ve created with a behavior tree, you need to specify the asset in the root node of the behavior tree. You can see an example in ShooterGame, where the “Blackboard Asset” property (in the Details window when the Root node is selected) is set to BotBloackboard.

Once you’ve done that, there are a number of ways to use the data in the blackboard.
There are some native code Decorators which support blackboard entries directly:

  • Blackboard decorator: Allows you to do some basic tests on the property depending on the type of the entry. For example:

    • “Is Set”: For boolean entries, “Is Set” means the value is true. For Actors and other UObjects, it means the value is not “None” (NULL).

    • Numeric comparisons versus a constant value. I.E. <=, <, >, >=, == (Equals), != (Not Equal To).

      • These are valid for ints and floats. In addition, these are allowed for Enums and Native Enums (which only allow you to specify the enum values through a dropdown). However, it’s best (in most cases) to just use Equals/Not Equals for enums. If you want to support multiple values, use a Composite decorator to “OR” the valid values together.

    • For Strings and Names, “Is Equal To”, “Is Not Equal To”, “Contains” and “Not Contains” are all supported.

  • Compare BBEntries decorator: Compares two separate entries on the blackboard to each other.

  • Composite decorator: These nodes can combine multiple Blackboard and Compare BB Entries nodes. (They can also compare blueprint decorators, but that doesn’t assist in using blackboard entries.)

In addition to these decorators, we plan on providing native support for blackboard context override soon.

Additionally, you can get and set the values of blackboard entries through either code (C++) or blueprints for any type of node you need. See information above about how to set or get values yourself through C++ or blueprints.

This sounds pretty useful! Makes me dread programming AI even less! :slight_smile:

Thanks a lot for creating this post. It is the best info I have seen on the Blackboard.

One thing which confuses me is how to actually get the data. For example, if I want to use GetBlackboardValueAsEnum I need to feed it the key. So far the only way I have succeeded in feeding the key is to create a new variable with type BlackboardKeySelector in my Behavior Tree Service blueprint and the in the behaviour tree, set that to the appropriate blackboard key.

What feels strange is that it looks like the Behavior Tree is feeding in the key to the Behavior Tree Service. Why not just feed in the data to the Behavior Tree Service instead?

Any info you could provide to clear up how to use blackboard data in a Behavior Tree Service (or other node) blueprint would be great.

Thanks.

How to do this from inside an actor blueprint? There is a ‘get Blackboard’ node available (4.7.5), but I cannot specify any target. I created a simple blackboard BB_test, but I simply don’t find a way to get a reference to it…

Hi Daniel Broder,

If I want to have a Blackboard shared by a AI squad, what is the best place to put that blackboard ? Is a good practice add a UBlackboardComponent on my GameMode class or is better to add it in another place ?. Please help me to clarify this.

Thanks so much

Best regards