Thus far, we've created a whole bunch of little Behavior Trees and now it's time to put them all together.
If we were to draw them in a diagram, they'd look something like this
and what you're seeing on the screen is a close representation of what we're heading towards but it's
not complete (by any means) because there's not enough room on the screen to do it
but you can see the key areas. You can see all of the key trees that we've created thus far and all of
the ways that they are interconnected.
The question is: How do you put them all together?
because they're not a 'sequence' and they're not something that should be selected from. What I've done here is just
one way that you could logically structure them and you'll see that there's a new node type added right
at the top called 'Parallel'. Parallel allows you to run 2 or more sides of the tree at once
(in this case, it's just 2) and one side is called the Patrol and the other side is called
Dead.
The Dead side is taking the code that we've already written for the Dead tree.
The Patrol combines all of the behaviors that the bot can do when it's alive.
We want the nodes under Patrol to run in a quasi kind of sequential order, so that each Update, the bot is checking
whether it should Flee, Attack, Look Around or Wander.
We'll start by going straight to the bots
AI Behavior Tree, which is combining everything we've done thus far.
So create a new script called 'BotAI.BT' and into that we will combine all of the trees.
You don't have to put all of the trees into one file, you can put them in separate files and attach
them all to the Panda Behavior that's attached to your agent.
However, I find it a lot easier to see what's going on when we're debugging if they're all in one single
file.
So here's what they're going to look like.
First of all, we start off with our tree 'Root' node.
This is where the tree will start running from.
Then we have our 'parallel' node which will run its children at the same time
and you can see there's 1 tree called Patrol that will run and 1 tree called Dead.
We've already written the Dead one.
These are prefixed by a 'repeat' node, a 'mute' node and then, of course, the 'tree' node. The 'repeat' node will
just continue repeating (over and over again)
this part of the entire tree. 'mute' doesn't care whether the node underneath it succeeds or fails, it just
keeps running it.
So it's saying "Don't listen to whether you succeed or fail" which allows this Dead tree
here,
when your agent's not dead, to keep running and keep checking if the agent's dead.
Now what's in each of these trees?
You'll see that's a separate part in the code for each tree, so there's Patrol...and here's Attack and each one is
named.
They don't have the Root any more at the top.
They need a name so that they can find how they're all linked together.
Now you could put this in one great big tree without all these little subtrees but it would become
quite a mess and very difficult to debug. The Patrol tree has a sequence and it's going to call the
'Flee' tree, so it's going to check if we can flee.
That's also then going to go on and say 'while our health is not less than 40' we will do our little sequence
of Attack,
LookAround and Wander. This is the code for the Attack tree, taken straight out of our little file that
we made before, for Attack.
The only difference is I've changed Attack here, which had Root back in the Attack file, if I find it...
it's just there, you can see and it's the same for all of the others. So Dead is down here.
We've got Flee, LookAround and then Wander.
So once you've got that, you can save it and then you want to go to your Droid and set that as your
Behavior Tree.
We don't need to change anything in the AI class code at the moment because we've not created any new
methods.
Your tree, if it's got pink and red all the way through it, means that it's all ready to go, it's found all the
methods it needs.
Ok, so let's press Play and see what happens.
Right.
If we go over to the Inspector and have a look, both of the 'parallel' things are running and the bot itself
is just sitting there...under Patrol. It has fired off the 'Flee' and the Flee it's fired off is down here and
it's saying 'while IsHealthLessThan(40.0)', so it can't satisfy this node and
therefore nothing else is going to run. Up here, where that Flee is being called,
it's failing that tree, therefore nothing else is happening because a sequence isn't going to continue
when a node in it fails, so we immediately have a problem with our tree not being able
to continue and go through all of the processes that the bot can do when it's still alive.
Now the same thing has happened for the dead tree but that's ok because our bot isn't dead, so it's
stuck on this 'IsHealthLessThan(0.1)' which it's not, so it's not going to immediately explode.
It's all right,
it's running parallel up here.
The problem is down here with the Flee, that we want to keep going.
So if this tree (for Flee) fails, we still want it to go and check the others.
So stop playing and I'll introduce you to a new node type. Go back into your bot tree code. In Patrol,
we'll make 2 changes. Where there's 'sequence'
here,
we're going to call that 'fallback' and this other 'sequence' down here,
we will call that 'fallback'.
Now the fallback node is a sequence node but it allows the nodes underneath it to fail.
So even though they do fail, it will still go ahead and keep running the things underneath it
and so this is what we want to use in this case because Flee will fail when we don't have to flee and
things like Wander and LookAround,
they're going to fail when we're in the range of the player and we're actually attacking them and Attack's going
to fail when the player's not around,
so we need to allow for that.
So save that now and let's go back here and press Play again.
Now when we run it, you'll find that the bot will move and it's going to respond to the code that allows
it to turn around because we're setting in our fallback: Attack (which fails because it can't attack at
the moment) then it has a LookAround tree (which you can do
and so it just looks around).
You'll see that the Wander isn't getting triggered whatsoever, it's not getting down that far
and if we look at the LookAround (let's go down further) you'll see that within the LookAround, there's nothing
in here that's going to cause it to fail
while it is healthy and away from the player. So we can force it to fail
every now and then, so it will allow a callback, up to the Patrol tree which will call the Wander, so
that LookAround can finish. It doesn't really have a finished state.
I guess that's what I'm saying
and we want it to go back up here, so that Wander can run.
So stop playing,
open up your code again
and in the LookAround, we're going to add another node in this sequence.
So after we've waited a random amount of time, we will put another 'random' in here and then as children
to that, we'll put 'Fail' and 'Succeed'. So randomly, it will either fail or succeed which will throw it back
out of this tree to the Patrol tree, where it will be allowed to go on to the Wander. Save that and then press
Play.
All right.
So the Droid now has done a LookAround but it's also triggered a Wander as well, as you saw happen there
and then it goes back into LookAround...
and when that fails, it will Wander again and here you can see it going off on a wander.
Now if we're lucky...
yep,
as it goes past here, what's happened is the Attack has been triggered
and that's because it has a SeePlayer and a TargetPlayer and a LookAtTarget, which is what made it
turn towards the player
and you don't want that to happen unless it's actually in Attack mode.
Now it's come into here (again it's back in Attack mode) and it's starting to Attack our lpayer because it's
found it, which is interesting in itself but we don't really want that happening so we have to add a
few more conditional statements into our tree to make it behave exactly as we want it to.
When the bot sees the player and goes to attack it (or knows that it's able to attack it), it should be able
to move towards the player as well, so that it gets into range and we can modify the Attack script to
do that.
So back in our full Behavior Tree,
we're going to replace the Attack sequence that we had before with something a little more complicated.
So let me put it in, right so I'll go through and explain it.
First of all, we're going to check if we can see the player in Attack,
before we continue
and then if we can, we'll start a sequence that does TargetPlayer, LookAtPlayer. Then we're going to have
a fallback on 2 'whiles'.
The first one will be
'while ShotLinedUp'
and then 'while not ShotLinedUp'. If a shot IS lined-up, then the sequence will be 'Wait and Fire', which
we had before, for shooting.
If the shot IS NOT lined-up, then we want to 'SetTargetDestination' and 'MoveToDestination'.
So setting target destination will be setting the player as that particular target.
The 2 new methods here, that we have to write and add to the AI script is ShotLinedUp and
SetTargetDestination.
So let's go back to those scripts and at the bottom under the other tasks, we will add these.
So the first one is SetTargetDestination.
We've already set the target itself because we used (in our script) TargetPlayer, which sets that target
value to equal the position of the player.
That's why we're only setting a target destination down here
and what that's doing is telling our NavMesh to now move towards the target.
The next one is the ShotLinedUp, which is returning a 'true' or 'false'
if we're able to shoot at the player.
So again, this is a visibility-type problem where we're testing the distance being within the shot range
value (which is defined up the top) and that the angle between the forward vector of your agent and the
vector to the target is less than 1 (so quite a small narrow angle).
If it IS, then we can shoot.
If it's NOT, then we will move and adjust our position to get closer to the player.
So, save that
and let's go back into Unity and press Play to see how this now plays out.
The bot is going to look around and also wander. Now when it goes past the player
this time, it's noticed the player and turned towards him and is coming in for an attack.
So rather than completely ignoring and going off and saying "I didn't see you", it DID see us, so now we
can come back and let's start shooting at it, at this point,
so it's health will go down.
Off it goes, it's going to wander away...
find a hiding place...
Now if we just leave it alone (we can't see it there, can we?
Let's just move around.
Ok).
When it's health gets above 40, it'll go back doing whatever it was doing before, which is looking around
and wandering and now it's not going to attack unless it finds us again or we go and approach it and
try shooting at it.
All the while, it's health will keep going up.
Ok, so that is Behaviour Trees in a pretty tight little nutshell.
There's still a lot for you to learn. What I would suggest is now you know your way around them,
I would go to the pandabehaviour.com website, where the PandaBT plugin comes from and read through
the documentation because it's very helpful to see what nodes are available, what they do and how you should
structure them.
Also, download the full Panda Behaviour plugin from the Asset Store because it has some great examples
that come with it and some of them are really quite complex and they'll show you how to do some amazing
things with Behavior Trees.