Welcome back.
So we have our custom service and so far it doesn't do a whole lot for us.
It just prints a debug message.
Now I'd like to gather information.
I'd like to find the nearest player and then we can set variables.
Now where do we store these variables.
Are they going to be on the service itself?
Well no the service is not where we store variables.
Is it going to be on the behavior tree itself?
Well actually no, the behavior tree doesn't store variables either.
What we do is we designate a specific asset to store the variables that the behavior tree will use,
and that asset is the blackboard.
Let's click on that blackboard tab and we'll see that right here we have all the keys right under the
keys category.
So there's a reason it's called the blackboard.
Blackboard also known as chalkboard is what you would see in a typical old school classroom where the
instructor is putting things on the blackboard with a piece of chalk.
Think of this as a chalkboard used by the behavior tree, where we can put variables and their values,
and we can update those values.
The behavior tree has access to the blackboard that it's associated with.
And here in the behavior tree we can access those values and behave based on what those values are.
By default the blackboard has a self actor.
Self actor is a blackboard key and the key type is object.
But we can change the key type.
If we click the dropdown, we'll see that there are a lot of different types that our keys can be of,
and we can even pass in the values of keys into our nodes on our blackboard.
For example, find nearest player could have a variable of type blackboard key if we wanted to and we
can pass it in here.
We can take one of our blackboard keys and pass it straight in.
The way we would do that is we would go into our service blueprint and add a variable.
And we can call this self actor key and change its type to key.
And the type is actually blackboard key selector.
If we choose blackboard key selector for the type.
And we open the eye icon here and compile, then all of a sudden this self actor key variable, if we
go back to our blackboard, is something that we can set on this particular node that we've attached
to our selector.
Here it is in the details panel.
It's self actor key.
And we can set it to one of our key values in our blackboard.
Now if we expand the dropdown we see that there's only one option that we can set for this.
And it's the self actor that we have now if we have this set then we can use it here in our service.
If I take my self actor key and get it and then drag off of it, we can get the value stored in that
blackboard key selector.
Now we don't know what self actor key is we actually do because we know that we're setting it equal
to self actor which has the object type.
But really this behavior tree service doesn't know that.
But we can drag off of self actor key and type get blackboard value as.
And notice we can get the blackboard value as any of these types that a key can be.
So if we choose object which we know this is the type, then we're accessing the value stored in that
key.
If it's set now because this is an object we can drag off and type get object name, which is a blueprint
function node we can get and we can print this string here.
And why don't we just see what it says.
If we press play then we can see goblin spear and goblin slingshot printed to the screen.
Now what does this tell us?
This tells us that we automatically have a key in our blackboard called Self Actor.
And what is it set to?
Well, it's set to the character, the character associated with this behavior tree.
So at least we already have one blackboard key.
And we now know that we can add a variable of type blackboard key selector to a behavior tree service
blueprint and expose that to the details panel.
And if we do that then we can set the value based on one of our blackboard keys.
So that's kind of cool.
But because I don't need that right now I'm going to go ahead and just remove all that and remove self
actor key compile and save.
And I'll come back and see that that's no longer in the details panel there.
I really don't need to pass self actor.
And to find nearest player.
What I really want is for find nearest player to do its job.
Find the nearest actual character controlled by a human player.
Right?
And once I find that player, I'd like to set the value of one of my blackboard keys, but not self
actor.
I want a blackboard key for a target for this particular AI character to follow.
I'd like a blackboard key based on object that I can move towards and potentially attack.
So I'm going to add a new key to my blackboard.
And I go back to my blackboard tab to do that and click on New Key.
And I can choose the type for it.
It's going to be object.
And this object I'd like to call target to follow.
And we see that its key type is object.
Now notice key type has a drop down.
And for the base class we can change it from object to really any base class we like.
And that would make this act kind of like a subclass of it'll restrict it to that type or lower in the
hierarchy.
And we can choose actor.
And I'd like to choose actor for target to follow so that we can only follow actors.
And there are certain things in our behavior tree that we can do with actors that we can't do with objects,
and we'll learn about those soon.
Now, if we were to check this one instance synced, then that would mean that this field would be synchronized
across all instances of this blackboard.
Now we have multiple enemies, potentially all using this blackboard and instance synced would make
this variable shared among all of them.
If one of them sets it, then all of them have access to that same variable.
But if it's unchecked, then each instance of the blackboard has its own version of that variable.
So now we have a target to follow blackboard key.
I'd also like another key for the distance to that target.
And that's going to be a float key.
I'm going to call this one distance to target because I'd like to set that as soon as I find the nearest
player.
And then we can act based on distance.
So now that we have these keys, we can flesh out what our behavior tree service does, how it actually
finds the nearest player.
So back here in our behavior tree service, the first step to finding the nearest player is getting
all players.
Now if I'm going to say get all actors of a certain class, maybe it's the character class, for example,
that's going to include all actors of the character class, including all the enemies.
But we want only those characters that are controlled by a human player.
So how do we make that distinction?
Well, one way is through tags, and I'm not talking about gameplay tags like what we've been using,
but there are actor tags that are basically set with f names on actors.
We can identify actors with actor tags pretty easily, and these tags are exposed to the details panel
for all actors, and we can set those tags.
I can go into blueprints into character, and I can open BP enemy base, and in the details panel I
can simply search for tags.
Now here we have under advanced tags, it says an array of tags that can be used for grouping and categorizing.
So these are almost as useful as gameplay tags but they're a little more simple.
They're based on f names.
If we click plus we can add one of these.
For example the base enemy class could just have the tag enemy.
And we can always check an actor for its tags.
We don't have to cast to Ora enemy or anything like that.
We can just check the actor tags.
So this makes it really nice and useful.
And if we want to distinguish between, say, Ora and any of the enemies, the enemies could have the
enemy tag and Ora could have, say, a player tag.
So I can open BP Ora character and search for tags, and I can add a tag for Ora and this can be player.
Then by checking these tags, we always know if we're dealing with an enemy or if we're dealing with
Ora.
So after doing that I'm going to save all.
So here is where we're going to try to find the nearest player.
Before we do, I'm going to add a couple of variables to this behavior tree task.
And the type for these variables is going to be blackboard key selector.
This is a struct for generic blackboard keys.
So we have a blackboard key selector.
The first one is going to be called target to follow.
Underscore selector.
So we have a target to follow blackboard key selector.
We're going to add another one called distance to target selector.
This will allow us to make a link between this service and our blackboard keys.
So we have those for whenever we find the nearest player.
But the player may not be an aura if this behavior tree belongs to a pet that's following aura around,
well, it's nearest player from its perspective may be an enemy.
So now that we have tags on both aura and on enemies that we can use to identify whether they're players
or enemies, we can find all actors with one of the two tags, either player or enemy.
If our controlled pawn is a player as or as pet would have in that hypothetical case, then we'd want
to find all actors with the enemy tag.
But if the controlled pawn has the enemy tag, we'd like to find all actors with the player tag and
so on.
So what we can do is have a tag for the target of this AI controlled pawn.
We can add a new variable.
We can call it target tag, but this can be an F name.
Because these actor tags are implemented as f names, they're simpler than gameplay tags and we can
check our controlled pawn.
We can see if it has a tag we can use.
The function actor has tag.
Now, if the actor has the tag player, then the target tag is going to be enemy.
So let's put a branch here and we'll set target tag to enemy.
Otherwise the actor does not have the player tag, and we'll just assume that it has the enemy tag and
its target tag will be player, which is the case for our goblins, right?
So this could be collapsed to a function and we can call this set target tag.
And the input parameter is going to be what this input is controlled pawn.
So the first thing we do is we set the target tag.
Then all we have to do is find all actors with the tag we can use.
Get all actors with tag.
Passing in the target tag.
And this gives us an array of all the actors that have this tag.
Now, if this eye is an enemy, this will be all the players.
And if this eye is a player, this will be all the enemies.
So we can prove this with a for each loop.
And in the for each loop we'll take the array element.
We'll call get object name, and we'll print that name just to prove that this array contains what we
think it contains.
In the case of a game where there's one aura, we'll see one aura printed to the screen.
Of course we'll see three messages because we have three goblins every 0.5 seconds.
Let's check it out.
There we go.
All of them are finding aura perfect.
So that's as expected.
We now have all actors with the target tag.
The next thing we need to do is find the nearest of these actors, and we'll implement that algorithm
next.
I'll see you soon.