UnityLearn – AI For Beginners – GOAP – Adding Goal Challenge & Taking a Break

August 19, 2020

AI For Beginners

Goal Orientated Action Planning (GOAP)

Parts 14 and 15


Beginner Programming: Unity Game Dev Courses

Unity Learn Course – AI For Beginners

Adding Goal Challenge

Adding Goal Challenge

My Approach

This tutorial starts with another challenge to implement a new GAction. This time it is the GoHome GAction for the patient to return to a transform tagged “Home” after they receive treatment. This was rather straight forward as I created this new GAction that just had a Preperform method setting the target to the tagged gameobject “Home” and putting in the Postperform method that the TreatingPatient world state should be reduced by 1 (since they have finished being treated).

Once this GAction was applied to the patient, it needed to connect to the previous action by having a precondition of “isTreated” and I added an after effect of “isCured”. The “isCured” state was also added as the new secondary subgoal of the patient GAgent to ensure they would move entirely through the chain of actions. I did initially just add “isCured” as a 3rd goal (keeping the 2nd goal of “isTreated”) but this created a strange behavior where the agent would take one extra step of actions and return to the hospital entrance after returning home.

Their Approach

They named their new subgoal “isHome”, which I think is important to note because I think with a system like GOAP you should keep in mind that the actions should be somewhat isolated for their specific purpose and you should at least be cognizant of chaining the actions together too much for what you think their intended purpose may be and recognizing when to keep them more encapsulated as their own action. It makes sense that the patient’s true goal is just “to get home” and getting cured of their ailments is just an obvious way to us to achieve that goal given the settings, but being cured is just a major step towards simply getting home.

They also added an extra step of states for the patient agent. I simply connected the previous action to the new action by matching the GoHome action’s precondition with the GetTreated after effects (“isTreated”), but they actually had the GetTreated action modify the state of the agent to add “isCured” and used this (“isCured”) as the precondition for their GoHome action.

While this does not seem necessary in the current state of the tutorial, I think this makes sense to add this step when developing a real GOAP system because there could be several ways for a patient to be cured (such as simply waiting it out if it is a minor ailment like a cold). So this approach may make more sense when building a proper system, which again, is good to note for the future if building a system like this for your own project.

I ran into an issue where the patient agent was not leaving the waiting room, and it turned out this was because I still had the “isTreated” intermediate subgoal commented out because it was necessary for my approach to work. Adding this subgoal back in (along with the additional “isHome” goal) allowed the agent to work properly again.

Errors

They ran into a problem with the tutorial (that I also replicated) when they turned the spawner back on to add many patients to the system. Many of them began congregating at the hospital entrance, which was due to the fact that “isTreated” was one of their subgoals and it was being immediately met because the first patient treated added “isTreated” to the entire world state so every patient thought it was already treated.

This led to them pointing out a rule of thumb where you do not want any of the subgoals of the agents to match a state that is directly added to the world state or the agent’s beliefs. So to follow this up, I changed the world state modification within the GetTreated action from “isTreated” to just “Treated” (this appears to be what they did in the tutorial but it is not clearly stated).

While fixing this, they also note a possible issue with the NavMesh system in Unity. Something about the NavMesh that even when it gets a path it thinks the destination was the previous destination, so it returns that the path was “completed” before the agent actually gets there. This can cause agents to start on the next action since many are location based. This was clarified to possibly be a result of their large update method within GAgent that is using NavMesh’s SetDestination method. They suggest that the loop may be processing through again before the SetDestination has been fulfilled which can cause issues.

Distance Check

To solve this issue they decide to just have their own distance check for destinations. For this they just did a basic distance check between the agent’s position and its destination position.

Speeding Up the System

To help with the debugging process they simply sped up the entire game system. This can be done very easily by just setting Time.timescale to a value greater than 1.0 anywhere in your scripts. This is actually very handy to know and would be an insanely useful tool moving forward to add to a major static debugging script for any project that could be toggled and edited to help with various debugging processes (i.e. they just added that line to the GWorld class since it’s a systematic static class).

With all of these fixes and modifications in place, the system now appears to run rather smoothly and appropriately. It was working so well I tested adding another nurse agent to see how it would adapt and it continued to work flawlessly. The new nurse would properly grab patients that the other nurse was not treating and take them to a different cubicle to treat them, so there was no strange noticeable overlap of actions.

Taking a Break

Taking a Break

This tutorial looks at adding a Rest action to the nurses so they will sometimes take a break and move to the lounge area to regain energy to continue working. To begin, they added a small method within the nurse GAgent class to modify their beliefs every so often to add the exhausted state to them. This gets the exhausted state into the overall system to help start allowing it to control their actions.

They then added the new action named Rest which simply has a Postperform method for removing the exhaust state. This paired with adding a duration to this action is all that is necessary to provide a timed cool down for the nurse to remain out of commission for a time before coming back to action rested.

How they set it up the nurse agent’s are only able to rest if there is time (so either there are no patients or no available cubicles). So when there is only a single nurse and a lot of patients, the rest action is never seen. To show it working, they added several nurses (about 5) to the system. Once this was done, it could be seen that occasionally when a nurse was finished treating a patient they would go to the lounge area instead of directly to another patient. They would then come out of the lounge to grab a patient after being rested and having another action to perform.

Finally, they added the removal of the patient agents once they returned home just to keep the system cleaner. This was very simple as they just added Destroy(this.gameobject) to the Postperform method of the GoHome action.

The following is a quick link showing my finished GOAP tutorial project in action
Link to Video of My Completed GOAP Tutorial in Action

Summary

This overall tutorial was amazing for introducing me to a new AI system. I still need to look over some of the foundational interactions of the major classes, but I understand the system from a conceptual view pretty well now and it seems very useful as another AI option depending on your case.

The next to last class (Adding Goal Challenge) was very useful for understanding some of the issues you can run into and how to ensure the system runs smoothly and controlled. Understanding keeping the states very separated between the different actions/agents and the overall world state is crucial to make sure you do not get some nasty bugs. Then the speed up feature available in Unity is definitely something I need to take more advantage of when debugging in particular.