In the last lecture, we set up the basic GOAP system with a simple agent and now we're going to add some actions.
So create yourself a new C# file called
'BakeBread' and I'll just bring one in that I've written before and we'll have a look what's in there.
So, if we open it up...
all of the actions that you write will be of this same format.
Now they're all separate and individual modules by themselves, so there's no linking together.
Remember: what links them together are
their Preconditions and their Effects matching.
Now this is called BakeBread and it's a GoapAction
so it's going to inherit our stuff that's in that GoapAction set.
The first thing we're going to do is to set 'completed' to False, so this will be a boolean value
we keep, to know when we've finished our task. Tasks take time as well,
so we're going to keep track of a startTime for the task and initialize it to 0 and then set a workDuration
(so how long the task takes). It's public, so that you can set it in the Inspector.
So I've got it for 2 seconds and you can change it as you like.
Next we have our public constructor for this class, which is where we add in our Preconditions and our Effects.
So these are probably the most important things in this whole system because these have to match each
other, in other actions,
in order for them to chain together. Now you can see here that our Precondition for BakeBread is that
'hasFlour' is True and our addEffect (after we've baked the bread)
is going to say that 'doJob' is True.
So 'doJob' is True in our goal that we set, back here in Worker, which is up here...THAT is matching THAT.
So that's going to make the goal backward-chain to THIS, so that it can then run it
and then if 'hasFlour' is, in fact, True then it will start running this particular action,
otherwise it's going to chain back up the list,
if there are other actions, to see if it can keep satisfying them at some point. Now, in this case, we
do have flour
(because remember we've given our agent 5 bags of flour) and then back in this Worker system here, we're
injecting hasFlour, which will give it a True because flour is greater than one.
Right.
So the name here is just for debug purposes, so that we can print it out if we need to (in the Console)
to see what's going on.
Right, then every action needs a 'reset', so that when that action is performed again, it's going to start
with new values. So, startTime
obviously back to 0 and 'completed' back to False
and then there's a few other methods that are required, one's returning 'completed'
(isDone, to say "Are we done or not?"), then we have another one called 'requiresInRange' and this is a function
that you can set to False if you allow your agent to perform a task when they're not actually at the
location they need to be at.
So we're saying that "Yes, you do have to be at your assigned location to perform your task (or this particular
task).
You then have another option for more procedural Preconditions, so you can check that other things
in the environment are True or False as well, before the agent carries out any of these actions.
So at the moment we don't have any.
We're just baking bread,
quite simply but you might want to check something like maybe "You need to have 10 loaves of bread, before
you deliver them to the bakery." or
"You might not be able to bake if it's flooding or raining, depending on the weather.",
all sorts of other things that don't fit in with the actual chain of actions that you would put into
here.
Next we have our 'perform' method, which ticks over while the task is taking place, so it initializes the
startTime
and I've got some Debugs in here as well, so that you can see them in the Console when it's starting a particular
job
and when it's finished that job, so this just ticks over for the amount of time it takes to perform that
task.
For every action, this is pretty much going to be the same
but then you can perform different things based on which action you're in.
This is the BakeBread action,
so at the end of the BakeBread action, I'm saying that our flourLevel in our inventory is going to go
down by 2 (because remember it takes 2 bags of flour to make one loaf of bread) and
then we've got rid of 2 bags of flour and then we're going to add 1 loaf of bread into our inventory,
then we say 'completed' is True
which will finish this task because it will be returned to the system up here.
So that's the entire code for our BakeBread.
So save that...go back to Unity...you want to add that code onto your agent.
So this is an action, you drag-and-drop it onto the agent
and now you can create as many different actions as you like and you just keep dragging-and-dropping
them onto the agent and the agent will know about these actions.
Now the only thing you need to set at the moment is the Target for BakeBread.
Where do you want to bake bread?
We're going to bake it at the Bakery, so we find our bakery marker (which is my Bakery 'Door' there) and I'm
going to drag-and-drop THAT as the Target for baking.
Ok.
Now you're ready to run it, so I'm going to switch to the Console so I can see what's going on...just clear that and let's
press Play.
So it's going to move to the location where it can perform the BakeBread action
and then when it gets there, it will start baking bread and you can see that in the Console. When it's
finished, you'll see that your inventory and your flour will go down 2 each time.
Now that's happened quite quickly
but if you go back and look through the Console, there's an awful lot there.
Let's just pause it because now it can't find any plan, it's just going to keep going 'Failed Plan'.
We can find where the baking happened...here we go.
So we're moving just here.
So we've moved to BakeBread...
we're starting BakeBread...finished BakeBread...moved to BakeBread...starting BakeBread...finished
BakeBread.
So we've made 2 loaves of bread and then it says NO PLAN
and that's because we've run out of flour. So there's only 1 lot of flour left and we've got 2 loaves
of bread out of that.
Now there's no way to get more flour, at this point.
So the plan for doing his job has failed.
He can't keep going when processing it.
So what we can do now is add in another action that helps him get some flour, so that he can keep baking.
Right, so stop playing that
and in the project you're going to create a new C# file called 'PickupFlour'
and that will look like this one here.
So PickupFlour...
if it's easier for you, then just make a complete copy of the BakeBread action and then put it in your
PickupFlour script and then just make sure you change the name here...
and in the constructor...
so THERE and then also the PickupFlour as well. Everything else can stay exactly the same because
it's just processing the action.
Now the Precondition and the Effect for PickupFlour is that we're going to get the agent to pick up flour
when we've run out of flour.
So when 'hasFlour is False' is when this action should take place
and then the Effect after that, is that hasFlour will be equal to True.
So this Effect here is setting hasFlour to True, which will then allow the BakeBread action to run.
So that's the linkage that will happen there.
Now further down in our 'perform', where we need to set up how the inventory is going to change, is that
after we've finished picking up the flour, then we can pick up the flourLevel in our inventory. In this
case,
I'm saying that when he goes to the mill to pick up the flour, he can actually carry 5 lots of flour
and so we're going to add 5 into the inventory, which will allow him to keep going and creating more
bread for a little while.
Right.
So once you've got all that in there, just save that, go back into Unity and that script again can get dropped
(PickupFlour) onto the agent.
Remember we need to set up our Target destination for picking up the flour
and in this case, we'll say that it's at the Windmill Door.
So we'll use the Windmill Door for our Target for picking up the flour.
Now when we press Play, we can watch the flour go down (so it'll go down 2 with each loaf of bread that's
produced) and then we know when there's 1 lot of flour left and 2 bread,
he's going to have to go and pick it up from the mill and here you can see him toddling off to pick up the
flour.
Now if this is all just a little bit too slow for you, remember you can click on your BAker agent, go to
your Nav Mesh Agent and change his Speed.
So if we set it to 15, which should speed things along a bit,
it won't increase the time it takes to complete tasks,
it will just increase how long it takes for him to go between places, so that you can see it happening
a little bit quicker.
Now we can see he's going back over...getting the flour...the flour in your inventory will go up to 6 because
you had 1, he's added 5 and then he will go ahead and use that flour and bake more bread
and when he runs out, he will go back and get that.
Now you can make quite a complex chain of all the different events that you want to occur in any
particular behavior
but just with a couple you can get some quite complex things happening, as you can see here.
All right, so that's setting up your little chain
and in this case, it's kind of like a supply-chain.
In the next lecture, what we'll get him to deliver the bread (when he's got enough) to the
markets.