Procedural Quest System


Goals

  • Learning to use the C++ API of Unreal Engine
  • Creating a quest system based on Goal Oriented Action Programming or GOAP

Starting Point

  • Unreal Engine 5.2.1 with all available libraries

Introduction

I love playing RPG and story driven games, and with that usually come various quests you can complete to advance in the game. Unfortunately, those quests are often quite rigid and don't allow for an individual experience, forcing everyone to play the quest in the same way. Once I learned about Goal Oriented Action Planning, or abbreviated GOAP, I immediately thought of the possibility to use this technique in another way. GOAP is normally used to dynamically drive the AI behaviour of npcs in games by breaking it up in individual tasks. Since a quest is composed of a series of tasks that the player needs to complete, in theory it could be used perfectly to drive a dynamic quest system. The system would look at the world state while building a quest, and will keep doing so every time something changes. This means that the quest can be adjusted to what the player already did before receiving the quest, and can also adapt to what the player does after starting the quest.

The Concept

As mentioned earlier, GOAP is used to dynamically drive AI behaviour, but now I am using it for different purpose, so how do the different concepts map onto it?

  • Effects and Preconditions:
    Normally, the Effect is the part that handles the changes to the world state, this will still be the case in my system, but I will split it up in Effect and Result. The former will handle the changes to the world state right after the action is activated, and the latter will handle it when the player has completed the action's objective. The effect and result are needed to notify the world state that something changed. The precondition describes the effect(s) that need(s) to be completed in order to access the action it is attached to.
  • Action or Objective:
    The main building block of the quest, it tells the player what to do. There is one important difference between GOAP actions and this: in GOAP, the actions actively tell the AI what to do constantly, but here, they take on a passive role, waiting for the player to complete what is required. Once the requirements are fullfilled, the action relays it's effect(s) to the world state and the next objective can start.
  • Goal:
    The starting point of a quest. It contains all the necessary information to effectively built a quest. Once the preconditions of the goal are fullfilled, the quest is completed.
  • The Planner:
    This is the brain of the operation. It starts by taking the preconditions of the goal, and look for an action that has that as an effect. If the action also has a precondition, it will try to find an Action with those effects. If there are multiple actions that match, both will be saved a node tree. This will repeat until an action with preconditions that match the world state, or no preconditions, is found. After that, the quest is built and the action that has no or matching preconditions is used as objective.
  • The Agent:
    A NPC the GOAP system is controlling, however, in this case there is no need for that, as the player can be considered the agent in this case.

The Programming

Since the quest system needs to be able to be accessed by any object that has something to contribute to the world state, I created a single dedicated quest manager actor, which is responsible for all the quest related components.
The most relevant components are:

  • The Blackboard:
    While normally used for AI related code, the blackboard's functionality makes it perfect to store all the relevant quest components and data.
  • The Planner:
    All active quests are calculated and updated in here, from the available actions.
  • The Quest Activator:
    This component is responsible to keep track of which quests are completed and activate the quest when it's activation requirements are fullfilled. Then it will notify the planner, so it can find an appropriate action plan for it.

The other components are mainly used as storage, such as the player's inventory or the available npcs.

The preconditions, effects and results can all be created from the same statemodifier class. This has the advantage that a precondition can be used as an effect, or result, and will update together if something were to change. It also has the further effect of being able to slot in any statemodifier in the blueprints.

Preconditions and results of an action are used in the calculation of the quest itself, while the effect is mainly used to activate something important for the current quest, such as the dialog for a specific npc. However, while currently not supported, it would be entirely possible to add this to the preconditions and results as well if the need ever arises

Conclusion

I have learned a lot over the course of this project. I learned how to use the C++ API in Unreal and created a quest system based on goal oriented action planning.

It is quite interesting how easily concepts in a slightly different area can be adapted to fit the needs of another system. In short I would say that using the GOAP concept for quests is entirely possible, but there are some disadvantages to it that may make it unfit to be used in a project that requires a system like this.

  • Performance: Quests have to be recalculated at every step to make sure they are still up to date with the world state.
  • Unintended Side Effects: Given the flexibility of the system, it is entirely possible that players take a route that was never explored by the designers, thus potentially ruining the intended experience.
  • Setup: It takes quite a bit more time to set up the planner to calculate the quests in a meaningful way, and create all the different goals, actions, preconditions, effects and results. So this might not be a great choice in a smaller game.

What's next?

I have only scratched the surface of what is possible with this system.
A couple of things that I will try to add in the future are:

  • Enemy support: currently npcs can only give you quests that involve items, or talking to other npcs. Enemies would add more depth to the questsystem overall.
  • Save system: Currently, when you restart the game, the game drops you back at the very start, so a system to keep track of your progress would be a tremendous addition. Additionally, reading the various goals, actions and effects from a file instead of creating all of them manually in blueprint would be a nice addition as well.