2. Idle

Goal

In this tutorial we will create a simple GOAP system that will make an agent wander around when idle. The agent can also pick up apples and eat them. The agent will only eat apples when it's hungry.

Setup in Unity

  1. The package comes with a Generator Scriptable that can help you quickly boilerplate all the classes that are used by the GOAP system. Let's get started by creating a new location for our scripts to go. Create a new folder called Getting Started in your Assets folder.

  2. Right-click the Getting Started folder and select Create > GOAP > Generator. Call the scriptable GettingStartedGenerator.

  3. When you select the GettingStartedGenerator you can see all it's properties in the inspector. The generator requires you to set a base namespace in the inspector. To make following the getting started easier, please set the namespace to CrashKonijn.Docs.GettingStarted.

    Getting Started Generator
  4. If you like using assembly definitions you can add it to the Getting Started folder. Please make sure to also set the Root Namespace to CrashKonijn.Docs.GettingStarted. Also make sure to include the com.crashkonijn.goap.core, com.crashkonijn.goap.runtime, com.crashkonijn.agent.core and com.crashkonijn.agent.runtime assemblies.

    Assembly Definition

Generator For setup through scriptable objects the generator is required!

The generator is a scoped entrypoint (when using ScriptableObjects) that will keep track of all available GOAP classes within it's scope. All classes (goals, actions, sensors and keys) and SO Configs (Capabilities and Agent Types) must be in subfolders of a generator.

Generating classes

Namespace Don't forget to set the namespace you want to use. All classes must be in this namespace in order for the generator/system to find them. The tutorial uses CrashKonijn.Docs.GettingStarted.

  1. Let's generate the required Goals, Actions, WorldKeys and TargetKeys using the generator. In the inspector of the GettingStartedGenerator please fill in the following classes in their respective fields:

    • Goals: IdleGoal

    • Actions: IdleAction

    • WorldKeys: IsIdle

    • TargetKeys: IdleTarget

    Generator classes
  2. Hit the Generate button! The generator will now create all the classes for you. Unity doesn't always register the new files, you can fix this by going to another program and then going back to Unity. All the classes should now be visible in the Getting Started folder, in their respective subfolders.

  3. Later on we also need sensor classes, but these can't be generated by the generator.

Sensors

Each WorldKey or TargetKey that is used in general also needs a value assigned to it. To get this value we use Sensors. Sensors are classes that can read the current state of the world and provide this information to the WorldState when it's needed.

In this part of the demo we use two keys, the IsIdle (WorldKey) and the IdleTarget (TargetKey). The IsIdle key in this example is mostly used to match the IdleGoal and IdleAction together, it doesn't actually require to actually update the value. 'Manually' coupling a goal and action together is generally bad practice, but for this demo it's fine.

The IdleTarget key does need a value, so we need to create a sensor for it. To create a sensor we need to use the correct base class. The correct base class is determined by the Type of key (Eg WorldKey or TargetKey) and the Scope of the sensor (Eg Global or Local). Global sensors are used to provide information for all agents (eg PlayerPosition), while local sensors are used to provide information for a single agent (eg ClosestTree).

Local
Global

WorldKey

LocalWorldSensorBase

GlobalWorldSensorBase

TargetKey

LocalTargetSensorBase

GlobalTargetSensorBase

In this case the IdleTarget is a TargetKey and it is for a single Agent, so we require the LocalTargetSensorBase.

  1. Let's create a new folder in the Getting Started folder called Sensors.

  2. In the Sensors folder create a new script called IdleTargetSensor that extends LocalTargetSensorBase.

ScriptableObjects or Code

The GOAP system can be setup in two ways. You can either use Code or ScriptableObjects. The Code way is more flexible and allows you to create your own setup systems and use generics. The ScriptableObjects way is more visual and allows you to set up the system in the Unity Editor. Please pick the one that fits your project best.

Creating the scene

  1. In the Getting Started folder create a new scene called GettingStarted. Open this scene.

Adding the GOAP system

  1. Create a new GameObject and name it GOAP.

  2. Add the GoapBehaviour to the GOAP GameObject.

  3. Each GoapBehaviour needs a Controller. The controllers determine when and how the resolver is run. For the demo we will use the ReactiveController. Add the ReactiveControllerBehaviour to the GOAP GameObject.

Capabilities

The GOAP system is build around the concept of Capabilities. These capabilities are used to determine what an AgentType can do. Each AgentType can have multiple capabilities. Capabilities are re-usable subset of Goals, Actions and Sensors that are merged together into an AgentType. For this demo we will start with a single capability called IdleCapability.

  1. Let's create a new folder in the Getting Started folder called Capabilities.

  1. In our newly created folder lets create a script called IdleCapabilityFactory. This script will include a CapabilityBuilder that will help us create our Capability.

Agent Type

Each agent belongs to an AgentType. The AgentType holds all available goals, actions and sensors for the agent to use and are shared between all agent of that same AgentType.

  1. Let's create a new folder in the Getting Started folder called AgentTypes.

  1. In our newly created folder lets create a script called DemoAgentTypeFactory. This script will include an AgentTypeBuilder that will help us create our AgentType.

  1. In the open scene, add a child GameObject to the GOAP called ScriptDemoAgent

  2. Add the newly created DemoAgentTypeFactory script to the ScriptDemoAgent GameObject.

  3. On the GOAP GameObject, add the ScriptDemoAgent GameObject to the Agent Type Config Factories list.

    GOAP Behaviour
  4. With the ScriptDemoAgent GameObject selected, you can now open up the Graph Viewer to view the generated graph for this AgentType. You can open the Graph Viewer by going to Tools > GOAP > Graph Viewer, or by pressing the shortcut Ctrl + G or Cmd + G (on Mac)

Graph Viewer

Creating the agent

  1. Let's create a sphere in the scene and call it Agent. (GameObject > 3D Object > Sphere) This will be our agent that will wander around.

  2. Make sure the Agent is at the position (0, 0, 0), or at least withing the bounds defined in the IdleTargetSensor.

  3. For this demo we won't use any physics. You can remove the Sphere Collider from the Agent.

  4. Each agent always needs an AgentBehaviour component. Add the AgentBehaviour component to the Agent.

  5. Each agent also needs an ActionProvider, let's add the GoapActionProvider to the Agent.

  6. On the AgentBehaviour, set the Action Provider Base value to that of the GoapActionProvider on the same GameObject.

No further steps required for code.

Moving the agent

In order to move the agent you can use the events on the AgentBehaviour. These events are called when the agent is in range of a target, when the target changes and when the target is no in range. Based on these events you can determine when and where to move the agent.

  1. Let's create a new folder in the Getting Started folder called Behaviours.

  2. In the Behaviours folder create a new script called AgentMoveBehaviour.

  1. Add the AgentMoveBehaviour to the Agent GameObject.

Deciding what goal to perform

Deciding what goal to perform is very game specific and can be done in many different ways. For this demo we will use a simple 'FSM' script that I like to call a Brain. The Brain will decide what goal to perform based on the current state of the agent.

  1. Let's create a script called AgentBrain that extends MonoBehaviour.

  1. Add it to the Agent GameObject.

Play the scene!

When you play the scene, your freshly created agent should start moving around!

You can open up the Graph Viewer and select the agent in the scene to see what it's doing!

First idle run

Updating the IdleAction

Currently, our idle action works because it's target is a random position. The agent will move in range of that target, then start performing the action. By default, the action script immediately completes the action and the resolver will kick off again. This will result in the agent moving to a new random position.

Let's update the IdleAction to actually wait for a few seconds before completing the action.

  1. The Generator created a boilerplate including all the possible method you can use. We only use the Start and Perform methods right now. The other ones can be removed.

  2. Update the Data subclass to include a public float Timer { get; set; } property.

  3. In the Start method, let's initialize the Timer to a random value between 0.5f and 1.5f.

  1. In the Perform method, let's update the Timer and check if it's below 0. If it is, we can complete the action.

Your IdleAction should now look like this:

When playing the scene the agent will now wait for a while before moving to a new position!

Second idle run

Last updated