Introduction

For my specialization at Futuregames I choose to focus on AI. AI behaviors and decision-making systems in games is something I've always enjoyed working on, there are so many cool ways to manage different behaviors, and of course GOAP is one of them.


At this page I describe how every part of the GOAP-system I made works as well as give some examples of how it looks in the editor. At the end I also reflect on the good, struggles, unexpected and further expansions.


This project I've worked on for approximately two months (December 2024 - January 2025), around 60 % part time.


Implemented in Unreal Engine 5.4

Why GOAP Specifically?

There are a few reason why I choose to deep-dive into GOAP:


-   A Great Learning Opportunity.


While I've worked on BehaviorTrees and Finite State Machines before GOAP is something that I've heard mentioned a lot, especially in a lot of GDC talks, but never actually worked with myself. This made it a good learning opportunity to expand my knowledge.


-   Working With Scalability and Adaptability


One of the big advantages is the modular design and reusability for logic. For example the same Action Node can be used across multiple goals and by multiple actors. It's also needs to be adaptable since it needs to be integrated with other systems.


I think this a great mindset to get into in general and is usually how I like to think while designing systems.


-   Showcase C++ & System Design Skills


GOAP isn't just about AI behaviors, it's a data driven system that can be used to more things than just AI behaviors. By developing GOAP from the ground up I hope to demonstrate my ability to design and integrate systems, not only relying on architecture provided by the engine. I also hope it showcase my proficiency in c++.


-   Ability to connect my own system with others


Since this project is made in Unreal 5 I've had to integrate and adapt my GOAP-system with Unreal's infrastructure. I also connected GOAP with a hunger, thirst, and tiredness system, showing that I can bridge AI planning with gameplay mechanics. I also hope this project demonstrates my ability to work within Unreal Engine’s ecosystem while extending it with custom logic.

GOAP Fundamentals

Goal Oriented Action Planning (GOAP) a system for AI agents to determine it's actions by setting specific goals based on the game environment. Conventionally it consists of five main pillars:


    1. AI Agent

    The associated actor connected with the GOAP-system. 


    2. World State

    Data of the state of the world / environment used to select goals & actions.


    3. Goals

    This is the desired state for the AI Agent to achieve.


    4. Actions

    Actions are where the logic and behavior are executed to modify it's current state.


    5. Planner

    The Planner is responsible for selecting the appropriate goal based on the World State and to calculate the best course of actions to achieve the goal. 


    GOAP Mindmap:

    World State

    The World State is a fundamental part of the GOAP-system. It represents the current state of the world and serves as the data for the Planner to determine the AI agents goal and action.


    The data struct I made contains a Map of a String and a bool. The keys identifiers are strings and represent different facts about the world. For example, "IsHungry", "HasWater", "Sleeping" etc. 


    The rest of the struct is a set of helper functions and operators that allow for easy modification and retrieval of the data.


    For this project, I kept the World State implementation simple by using only boolean values, however, a more advanced system could of course expand this by having more data containers with different value types aswell. 


    Showcase on the Current World State for one AI Agent:

    On the right hand side you can see how the effects of actions changes the Current World State of the AI.

    Graph System

    For this project I decided very early on to create a simple graph system to structure the goals and actions. This is because I wanted to utilize A-star to decide on the Goal & Action for the AI.

    Base Node

    The Base Node contains all the properties that both Goal and Action nodes must have.


    WorldState Preconditions: 

    Preconditions define what must be true for this node to be valid and considered by the Planner.


    Example:

    "HasWater" must be true for the node AN_DrinkWater to be considered by the planner. 

    WorldState CostState: 

    A set of factors that influence the H-cost and likelihood of this node being selected. This is the main way to customize how the AI behaves.


    Example: If the food storage is low on food then the H-cost of the Goal Node to gather food should be lowered, and the opposite if the storage is almost full.


    int32 Cost:

    The base cost for the node. This can be modified to prioritise different nodes before World State based filtering is taken into consideration.


    TArray<TSubclassOf<UGOAP_ActionNode>> ConnectedActionsClasses: 

    This is referencing the connected actions to the node. Both Goals & Actions can be connected to multiple actions. 


    The reason it's an array of subclasses of action nodes is so it can reference defined Actions Nodes in blueprints and be able to instantiate them during run time.


    TArray<UGOAP_ActionNode*> ConnectedActions:

    Stores the Action Nodes after they have been instantiated.


    virtual int32 CalculateHCost(): 

    A function to be overridden when calculating the H-cost for the node.

    Example of Goal Node connected actions: 

    Goal Node

    The node for the current Goal of the AI. Here logic and properties only applied to Goals are stored.


    int32 AssignedActorCount:

    Keeps track of how many AI agents are currently pursuing this goal.


    bool bUseAssignedActorsInCalc:

    A bool to be able to modify if the node heuristics cost should increase when an actor is assigned the goal. This is a way to ensure overcrowded tasks become less desirable when selecting a goal.


    virtual int32 CalculateHCost():

    Below is how the heuristics cost for the Goal node is calculated. You can also see how the World State effect the cost, if it doesn't match the set Cost State the node gets more expensive.


    Example of Goal Node configuration in the editor:

    Action Node

    The node for the current Action of the AI. Here logic and properties only applied to Actions are contained.


    WorldState Effects:

    This is the effect on the World State that gets applied when the action is completed.


    Example: Action "DrinkWater" --> Sets "HasWater" to false.

    void ExecuteOnce()

    Function exposed as a blueprint native event to execute initialization logic when the Action gets selected. This can be called both through c++ and blueprints. 


    bool ExecuteAction()

    Function that controls when the action is completed. It's running every tick and when true is returned the action is considered complete. Just as ExecuteOnce this can be controlled both through c++ and blueprints.


    void ResetAction()

    A function to reset variables in the action for a specific actor.


    int32 CalculateHCost()

    Works the same way as in the Goal Node but without the option to use the number of actors assigned as a weight to the H-cost.

    Example of ExecuteOnce & ExecuteAction logic in an Action Node:

    Here's how this action logic looks in the scene (wait action):

    Example of Action Node configuration in the editor:

    Nodes Setup in Editor

    With this design it allows subclasses of the Goal & Action node to be created in the editor. This makes it very simple and quick to set up desired Goals & Actions.

    Goal Nodes I made for the showcase:

    Action Nodes for the Sleep Goal:

    GOAP Planner

    As the title suggest, the GOAP Planner is where the evaluations and planning of the current goal and action is being done. 

    How the Planner works is that it first filters out the nodes set as Precondition for the Node that doesn't match the Current World State. 


    After that A-Star is used to return the best node based on the CurrentWorldState. The H-cost gets calculated in every Node based on the Current World State and A-star then returns the best node.

    AI Agent

    The AI Agent is the actor that interacts with the GOAP system. To do this it maintains its own World State, calls the Planner to evaluate goals & actions. For this I created a GOAPComponent that can be attached to the AI.


    The AI Agent also has a "brain" system that keeps track of hunger, thirst and tiredness. For this to communicate with the GOAP-system I used delegates that gets broadcast whenever the state changes.

    GOAP Manager

    The GOAP Manager is an actor that acts as the central hub for the system.


    Storing & managing goals & actions

    The GOAP Manager is responsible for initializing and storing all goals and actions available to the AI agents. The goals that should be considered by the system is set up in the editor. Actions are initialized by going through all the connected actions for every goal.


    Global World State

    Another responsibility for the GOAP Manager is to manage Global World State. This refers to World State facts that apply to all the AI Agents in the world. For example, "IsDay", "IsNight", "FoodStorageFull", and so on.


    Example setting up goal nodes in the editor:

    GOAP Utility

    The GOAP Utility class is a helper class to provide common task to be called from an Action Nodes. It serves as a static library of functions that can be used across multiple action nodes without requiring duplication of logic.


    The common tasks include:

    • FindFirstActorOfClass

    • FindAllActorsOfClass

    • MoveToLocation

    • StartTimer

    • IsTimerDone


    Certain functions need to run every frame (like MoveToLocation and IsTimerDone). They are designed with that aspect in mind, for example, MoveToLocation only does a move request if it's not already active and restoring a delegate only when the move is completed instead of polling every frame.


    The functions are made static for a few reasons. It avoids unnecessary object instantiation, especially since it's most likely going to be used with a lot of actors. Since some functions are called every frame creating and destroying objects for each function call would be very unnecessary overhead. It is also convenient to use be able to call the functions in blueprints without requiring references.


    Example MoveToLocation being used in an Action Node:

    Reflections

    The good:

    - Allowed to complete the project within time.

    - System works like I had in mind from the start.

    - Got a good mix of a robust system in c++, userfriendly, quick and easy setup of nodes in blueprints.

    - Had time to set up a good scene for showcasing the system.

    - Learned a lot, both by learning the GOAP-system and how some of the Unreal architecture works.

    - System is very modular. Is very easy to expand the number of AI Agents, Goals and Actions without a lot of work.

    - Learned a lot of how the MoveTo logic in Unreal works behind the scenes.


    Struggles:

    - If it gets to congested AI Agents can get stuck on each other.

    - Difficult to find a good way to present the project since parts of the system is difficult to visualize.

    - Didn't account for the fact that UObjects cannot be referenced in the editor like actors and components since they only exist in the memory and haven't been instantiated yet. Had to therefore redesign part of the of the system to be able to reference nodes data set up in the editor.


    The Unexpected:

    - Making a static Utility class for managing action logic inside the node worked surprisingly well. It was not something I had planned from the start.

    - Was expecting to have to expand the World State data structure to make it work smoothly. However, I never got to that point, just a map of a String and a bool is way more powerful than I thought from the start.


    Further Expansions

    - I would like to explore the optimization part a bit more. For example, benchmark the effects of structuring the action behaviour differently and exploring different structures for the A-star formula. 

    - I'd also like to explore the World State data structure more. Adding support for more data types and perhaps expand into a some kind of perception based system. Instead of instantly knowing all facts, the AI could perceive world changes (e.g., through sight, sound, or memory).

    - Another thing that would be fun to explore is to introduce some kind of personality/

    specialization to the AIs. The goals & actions could be weighted differently depending on the personality, roles, and so on.