In this second lecture on Sequential nodes, we'll have a closer look at how they work and some
of the problems you can get when you're trying to create your own and run them.
So, first of all, let's create a new Behavior Tree which is based on the 'Wander'. So create a
new one.
What I usually do is just duplicate the one I've got and I'm going to call that Patrol.BT.
We're going to get the bot to patrol around the map, so we'll set it destinations that are in the particular
corners.
Now it's remembered what was in Wander because it was a duplicate of it.
What we want to do is to be able to tell it where to go, rather than picking a random destination.
So for that, we're going to need a new method back in our AI class and let's call it PickDestination
and we'll tell it that we want it to go to our extreme points that we've set up.
So we'll start at -100
and this is an example of how you can pass through arguments from the Behavior Tree script into your
methods.
So when you declare them, they can be 'int's that you're sending through,
they can be 'bool's, they can be 'float's.
Let's make these floats actually, I'll put '.0' on the end because they're locations in space
and then after that, we can reuse MoveToDestination because MoveToDestination is just monitoring our
bot moving towards a destination.
If you didn't have this and you just had PickDestination, PickDestination, PickDestination, PickDestination
it would fail to move anywhere because before it got a chance to move, it would get a new
destination set in the Nav Mesh Agent. It's MoveToDestination here that's monitoring what Nav Mesh Agent is doing in the
background that's giving you time to get from one location to the other.
Now there will be 4 of these (I'll just copy that and put them in here) because we've got 4 different
corners we want to go to.
So we'll start at -100, -100
and then we'll go to -100, 100 and then we'll go to 100, 100 and then we'll go to 100, -100 like that.
So save that.
Then you want to go back to Unity.
You want to select the Droid and you want to change its Behavior Tree to patrol, so drag-and-drop
Patrol.BT into your Behavior Tree and you'll see here the new one that's come up.
Now if you wait a little while, you will get an error message that will come up and these will go red
if they don't exist as a method in your AI class. I've already written mine, so it's found it and it's
fine.
Even if you've got a spelling mistake in the names of your methods,
it will show it
and so just make sure you've checked that it's the same in either place.
The same thing goes for your parameters.
We're sending through floats.
It's got to be waiting for a float inside the AI class.
So in your AI class (because you're probably seeing errors at the moment, let's open it up and have
a look,
here it is, here) above PickRandomDestination.
I've created a new method called PickDestination with an X and a Z value for our map
and then we're using 'dest' as a new vector which is at X and Z, rather than it being a random
value.
Y is set to 0
because our ground plane is sitting at 0.
If your ground plan isn't at 0, you might want to adjust this
and then we run SetDestination and return with a Succeed
and that's all we need to do, to now patrol because the code for MoveToDestination is already doing
what we need it to do.
So add that in, save it and then let's go back to Unity and press Play and we'll see the Droid in action
and if you click on the Droid in the Hierarchy, you'll be able to watch each of these leaf nodes run
as the Droid makes its way around the path. So MoveToDestination is currently running.
It's going to flick to PickDestination (or just did then) down to the next MoveToDestination
when it reaches each waypoint that you're setting up through the code for it to go to and because this
'Repeat Root' is ticked at the top, it will just go round and round and round and round this sequence, which
is what you want it to do.
So with all that working nicely, let's force an error to occur so that you can see what happens.
So let's go into the AI script and we're looking for PickDestination.
So PickDestination has at the end of it a 'Task.current.Succeed();'.
This is NOT a return statement, so it doesn't throw you out of this method and you can call it from anywhere
inside the code. It's just going to send a Succeed back to the Behavior Tree.
So you can actually leave it out and you won't get an error,
which is what we're going to do. We're just going to comment it out and save it and then we'll go back into
Unity and press Play.
Now let's go and have a look at what we're getting here.
You can see that PickDestination has run.
See that it's blue?
It hasn't finished running.
It's green when it finishes running.
Now you're probably wondering "Well the bot's still moving, it's still moving to the destination." and it will
move to the destination because the Nav Mesh Agent code is what's driving it forward.
After we go 'agent.SetDestination', the agent's going to go for it.
It's the MoveToDestination that's sitting there monitoring, waiting for it to get to where it's got to
go, so that it can then pick the NEXT destination
but you can see now that it has finished that 1 path find over to here,
it's stopped. The whole Behavior Tree has just stopped completely.
So that Succeed is absolutely necessary and if you find that your Behavior Trees are just stopping, then
you've forgotten to put a Succeed or a Fail.
Now you can put a Fail in here as well
and because it's on repeat, it will just loop around, loop around, loop around and constantly keep failing,
so it's going to move through the sequences until it hits a Fail and then shoot you out
and we can actually force that to happen and have a look.
So let's go back into the code
and I'm going to put this Succeed back in
so that we're going to move down
but what I want to do is have this one here, the PickRandomDestination.
Let's set that to Fail. So for Panda, it's like that, so that will return a 'fail' when we try and do a
PickRandomDestination.
Let's go into our patrol
and as the 2nd destination setting, we'll get rid of the parameters and put this as PickRandomDestination
as we did before, when we were wandering. So save that and let's go back over
here, press Play
and let's watch the tree. So PickDestination has now succeeded (it's green) and we're moving to our destination,
so this will keep ticking over until the bot gets up into the corner, at which point it's going to try
and run this PickRandomDestination which we know is going to fail.
So it's getting to the corner,
it stopped and there, you can see that it's wandering around trying to turn itself because that's
the 'agent.SetDestination' constantly getting called, over in this PickDestination and then trying to move
to it and it's picking the same destination that we already went to.
So when we try and do a MoveDestination, we're already there.
Then we go and do a PickDestination which fails immediately
and because we're looping around this route, it's stuck.
You can see it's stuck and you can see the Success and Fail values flicking really quickly up there
and because this is red, you know that's where you've got a failure point in your tree and you might
want a failure point in your tree, if that's what the sequence is going to do. If a certain condition
ISN'T met in the sequence, then you don't want to continue with the sequence.
So let's just take that out of there and go back and repair our code.
So in here, I'll just Ctrl-Z to get back what I had before and back in my AI script, I'm
going to set my random destination back to Succeed...like that.
Let's now consider an Attack sequence.
So in this sequence, I'm going to have 3 different behaviors.
We want the bot itself to be able to turn towards a particular destination, not MOVE to that destination.
So that's going to involve setting the point that we want to look at and then actually doing the turning
and then once that has happened, we can then do a firing action/ Ok, so the Behavior Tree for that, call
it 'Attack.BT' and in there, you want something that looks like this.
So we've got our 'Root',
we've got our 'sequence', we've got 'TargetPlayer', 'LookAtTarget' and then 'Fire'.
Now this means that each one of these needs to be a method in our AI class. For each of the leaf nodes in
the new Attack tree, there needs to be a corresponding method.
First of all, I have 'TargetPlayer'.
All it's doing is setting a Vector3 which is declared at the top of our program to the player's position.
That's all it's doing, so it's just setting the value of 'target'. Now doing it this way means that we could
have other leaf nodes that will say 'look at tree' or 'look at random' and be able to set this
'target' value without moving towards it.
After that, I've got a 'LookAtTarget', so this is a generic one which will turn the bot around
so it's facing towards wherever that target happens to be set.
So just scroll up a little bit and make this a bit bigger so you can see the whole thing.
This is pretty much the same code that we've used before
(I'll put this down on the next line for you,
there we go) for targeting something and moving towards it
but in this case, we're targeting it and turning towards it. So you'll see in here the familiar
Quaternion.Slerp code that we've used before
and our calculation of the direction vector. So...direction...slerp towards it.
Now I've got a Debug.Log in here, which I'm just going to get rid of because we don't need it
and instead there's a 'Task.isInspected'. This line here is going to allow us to see the current
angle between the bot and where our player is (in this case, the target) in the debug line inside of
the Behavior Tree, which is fantastic because we can actually see this value changing as the bot
turns around and then we're saying at the end, if the angle is within 5 degrees, then this task has
succeeded.
So we're going to keep turning, keep turning, keep turning and then we'll get a 'succeed' on this particular
task because there hasn't been a fail and until there's a fail received by the Behavior Tree, it's still going
to be executing this. Now after we've looked at the 'target', then we're going to fire and the fire code
is pretty familiar. In this case,
it's not going to be the same kind of set up that we've got here
where you've got a public void and then a return of Succeed.
What you can also do with these methods for these leaf nodes is have them return a 'true' or a 'false' value
so that you can then check whether they happened or not and perform a little boolean logic in the
Behavior Tree itself,
even though we're not really doing that with this, in this case
but you will see it happening in the future
and if you're not, you could set this up as the same set-up as the other ones with the Succeed being returned
instead.
Now in here, we're just getting the bullet prefab and then instantiating a copy of it on the end of the bulletSpawn. Again
the same thing we did with the little tanks demo, much earlier in the course. Then we're adding a force
to the bullet, just to push it forward in the world environment.
Right, so with those things done, let's save it. Save your AI script and your Attack script and go back into
Unity.
So I'll just select the Droid, so we can see its Behavior Tree, put 'Attack.BT' over here as the script and then
when you're ready, press Play and the Droid turns towards our droid, ok. At the moment it's not concerned
with walls or anything, it's just turning
(that's what we've told it to do) to where it knows our player is and if I move the player, it's just going
to keep turning
and shooting like that...
Now
there's no accounting for the speed of the shooting in our current sequence.
It's just going to be looping quickly through the entire tree, constantly targeting the player, looking
at the player and then firing.
If you wanted to add in a 'wait' in here, you can do that quite easily in this Behavior Tree system.
If we go back into our Attack script here, is that there's 'waits' built into it, so we can put
'Wait(0.5)' and it'll wait half-a-second before firing and then it'll loop back around again and wait
half-a-second before firing. So just save that and go back to Unity and press Play.
Now you can see that there will be half-a-second between each shot
and so you can add nice delays into the Behavior Tree itself.
All right.
So we'll leave this lecture here. In the next lecture,
we'll start to add in some conditions and the bot should really test whether it can see the player before
it starts shooting.
So we'll do that next.