So far we've looked at some pretty simple Sequential Behavior Trees.
Now we're going to add in a condition that tests if something is 'true' or 'false' before the Behavior Tree
continues to run
and so we're going to add in a 'Decision Node' within the code.
We're going to do that inside of our Attack
but first of all, we need to write our method that's going to do the decision for us.
Currently we're attacking all the time. You only really want the bot to attack if it can truly
see the player
and in this case, when you start off, it can't because the player is behind a wall.
Also if the player is outside some visible range, you don't want it to attack either.
Now if you remember at the very top of this program, there is a visibleRange and a shotRange set up, so
this visibleRange (which is 80) is going to determine if the player is within 80 units of the bot, then
it can see it. It will also test at the same time if there's no wall between the player and the bot, which
is a nice enemy situation because you want the bot to be aware that there is infrastructure around
it.
So as the last method in here underneath the Fire, I'll add in a new one which we'll use in our Behavior Tree
code.
So it's called SeePlayer.
It's a boolean
so it's like we had before but this time we're actually going to use it as a logic test in the
Behavior Tree.
So what this is doing is working out the vector from the
bot to the player
and then we're also going to have a RaycastHit.
So we'll raycast along that distance vector to see if we hit something and if we do hit something (which is
in this case, a wall) then we obviously can't see the player because there's a wall in the road, So I've got a little
bool set
here, saying 'seeWall = false' to start with.
I'm also doing a Debug.DrawRay in the environment, so we can see where that distance vector is
being projected.
This is really nice if you're trying to debug visually what's going on and where your vectors are.
I always use this.
Now it's going to project a ray, do the Physics.Raycast as we've done before and then see if we have hit
something called a 'wall'.
If we have, then seeWall will equal 'true'.
Now just for a moment back in Unity, if you go to the Map and drop that down, there's all these walls
and each one has been tagged with 'wall' and that's how that Physics.Raycast is going to work.
Now make sure that your ray that you're projecting does actually go through the wall (or whatever object
you're trying to detect as an obstacle)
and that's why this Debug.DrawRay is such a great idea.
After doing that, I'm also printing the seeWall value in some debug code for the Behavior Tree itself
so that you can see it when you're working through the different nodes and what they're doing,
which is also quite useful.
Then finally, to determine whether we can see the player or not,
the test is: If the magnitude of that direction vector distance between the two players is less than the
visible range AND there's no wall in the way. This time, we're not concerned whether the bot is facing
in the right direction or not.
So let's just imagine it has a little radar system on board and it doesn't need to be looking in
a direction to be able to tell if the player is nearby. Now if the player is in that radius and there's
no wall in the way, then we're going to return 'true' that we do see the player,
otherwise we will return a 'false'.
So this is the method for our new node.
Ok, so we'll save that and then we'll go into the Attack script
and what we're going to do is add a new node into our tree above the 'sequence'. So above here,
I'm now going to add this. So the code is 'while SeePlayer'
Now this 'while' node is a special testing node and it will allow you to run something that is a true
or a false value
and even invert it to check (it doesn't have to be true to run,
it can also be false) and it won't stop your tree from running.
Ok, so you could even put, for example, 'while not SeePlayer' if you wanted to switch the logic around and
we will a little later, but not in this case here.
Now this 'while' node can only have 1 child.
So we need to make sure that we've indented all of these like that, otherwise the Behavior Tree system
will complain about it.
So now the sequence of shooting is only going to happen if the SeePlayer method gives us a 'true'up here.
So let's save that and let's test it out.
So back in Unity, click on the Droid to check that the script is ok and you'll get pink text where everything's
fine with the Attack script.
Ok, let's press Play and have a look at what we get.
So over in the Behavior Tree, while SeePlayer is sitting there, wall = True, you can see that there.
You can also see in the scene that debug ray going through and hitting the wall, which is causing the
'wall = True' here and it's making us so that we can't see the player.
Let's move the player
around into view, so if I come around the corner and there you go. As soon as I come into view and I'm within
the visual range, then the Droid starts firing
and if I turn around and go back again, it will stop as soon as there is a wall in the road.
If I go through this wall because I'm not constrained as the player by the NavMesh, it no longer
can see me. If I come back in, it will and because of the previous code it will also turn towards the
player, in order to shoot
and you can see the Behavior Tree ticking along quite nicely with that little bit of logic. So you're
starting to see the power of Behavior Trees, that you can create a whole bunch of methods that do
a single action for your player and then you can string them together in the Behavior Tree, which makes
it a lot easier for someone who's not really familiar with the code
but it also makes your A.I. quite powerful where you can mix-and-match different behaviors. Just for interests
sake,
let's go back into Attack and change that code up at the top with the while-statement to be a 'not' and
reverse that logic, so that when we DON'T see the player, we're going to attack the player, which doesn't
make logical sense but what that means is that while you're not in visible view of the bot with your player
character, it's going to be shooting in your direction.
So just save that. While I'm here,
I should also mention this while-statement because of the used to normal programming, you're going to think
while statement means 'looping'. In the case of Behavior Trees, it doesn't mean looping.
So this is NOT a loop.
If anything, it's an if-statement,
if you want to think of it as something, so it's not going to cause this to loop around inside of here
and it's just called 'while'. Right, so save that. Let's go back and have a look at how that affects our Behavior Tree.
So, let's press Play and we would expect we're going to get shot at straight away because we can't see
the player.
Now if I move INTO range, then we won't get shot at
and in fact that whole sequence stops running, so we're not even going to turn and look at the player
because this bit here is not being satisfied, as far as being able to see the player.
Right.
So that's how you can add some logic in as well.
Now you don't have to use the 'while not' in this case because the SeePlayer could be part of the sequence.
If you put SeePlayer in the sequence (let's just go back in to here and take that out of there and put
it down here)
then it's quite legitimately allowed to fail...
and because this is a sequence, the rest of it won't run anyway so let's save it and have a little look
at it.
Check that it's reloaded in here.
We now have a sequence. If we press Play...ok, we can't see the player.
So the sequence has stopped with the SeePlayer and let's try and move closer to it to see what happens...
it will fire the SeePlayer
and then the rest will run as it did before.
So in this case, the while-statement is not helping us with the SeePlayer because it has to be true
in order for the rest of the code to run.
So in this case, what we've done is turned the tree on its head as far as this SeePlayer is now a
Leaf Node, whereas before it was a Decorator Node because it had children underneath it and it was allowing
you to make a decision and the decision in that case comes into its own when you want to invert the
value that's coming out of it.
So the 'while not' is going to be a lot more common in this particular case but it depends on what you're
trying to do.
So that's the sequence
and also adding in that logic. In the next video, we will look at the Selector Nodes.