Unreal Engine Plugin Quickstart

This page will present you with simple steps you need to take in order to integrate Grail with your Unreal Engine project and create a simple agent using Grail’s Utility Reasoner.

1. Prerequisities

The project should start out of the box in Editor on Windows. If you need it to run on another platform or you’re packaging the project, see Plugin Compilation page.

2. Importing the Plugin

To start off, you simply need to put the contents of our plugin in the Plugins/ directory inside your project structure. Then locate the project file (MyProjectName.uproject), right click on it and select Generate Visual Studio project files. This operation will also automatically build Grail from source. Now you can build and launch your project.

3. Creating a Configuration File

The first step of creating a Grail agent is making a configuration file. In this tutorial, we’ll go through creating an agent based on Utility AI. We assume that you are familiar with Grail Tool, especially Utility Creation Tools.

3.1. Adding a Utility Reasoner

Open Grail Tool and select Create new project.

Create new project
Figure 1. Creating a new configuration file in Grail Tool

Switch to Utility AI tab, select Utility Reasoner List and add a new reasoner by clicking on the plus button.

Utility Reasoner List
Figure 2. Adding a new utility reasoner

3.2. Adding Behaviors

Let’s say that we want our agent to be able to pick up items or wait. In the newly created utility reasoner’s panel, add two behaviors by clicking on the plus button and assign names to them - pick_up and idle. Let’s also name our reasoner picker_reasoner. In this tutorial, we leave the reasoner’s parameters unchanged. For more info on those, see the Utility Reasoner page.

Reasoner with behaviors
Figure 3. Naming the reasoner and adding behaviors

3.3. Creating Behavior Evaluators

To add evaluators to our newly created behaviors, switch the right column view to Evaluator List.

Evaluator List
Figure 4. Evaluator List view

Now create two new evaluators - pick_up_evaluator and idle_evaluator - by clicking on the plus button.

Behavior evaluators
Figure 5. Adding behavior evaluators

3.3.1. Idle Behavior Evaluator

Click on the Edit buton on idle_evaluator's entry. This will take you to the Evaluator Designer view. Because we want the idle behavior to be executed only if there’s nothing better left to do, we’ll assign to it a mock consideration returning some constant value and a constant utility curve returning 0.1, regardless of its input.

Idle evaluator
Figure 6. Utility evaluator for the idle behavior

3.3.2. Pick Up Behavior Evaluator

We want our agent to intelligently select between the items that it picks up. For simplicity’s sake, we’ll use only one consideration - the distance from the item.

Let’s start by creating the basic evaluator, containing the consideration and a single utility curve.

Pick up evaluator
Figure 7. Utility evaluator for the pick_up behavior

Now we want this curve to reflect our agent’s preference to pick up items that are nearby. Open the curve editor and modify the utility curve to decline with distance.

Distance curve
Figure 8. An example utility curve for the pick_up behavior

3.4. Assigning Evaluators to Behaviors

Now we can assign the evaluators to their respective behaviors in the reasoner panel.

Evaluators assigned to behaviors
Figure 9. Utility evaluators assigned to their respective behaviors

3.5. Creating an AI Entity

The last step that we need to perform is to define the AI Entity that should use the reasoner. Switch to Entity Editor, create an entity named picker and assign to it the picker_reasoner.

Picker entity
Figure 10. Creating an AI Entity and assigning a reasoner

3.6. Saving the File

Now we can save the configuration file. For the purposes of this tutorial, we assume that it is placed in the Unreal project’s content folder, under Content/AIData/my_config.gcf.

4. Setting Up Grail in Unreal

Now that we created a proper configuration, we can proceed to set up Grail in the project itself.

4.1. Grail Configuration Loader

UGrailConfigurationLoader class allows us to load a configuration file and store its content in a format understandable for the rest of the plugin. To define your own configuration loader instance, simply derive a blueprint class from UGrailConfigurationLoader and enter the path to your configuration file in the Configuration File Path` text field. After entering the path, click on the _Reload data button and then compile the blueprint.

Grail configuration loader

4.1.1. Development Mode

If you want the configuration to automatically reload each time you enter a PIE session, you can enable the Development Mode checkbox. However, keep in mind that you’ll still have to manually reload configuration data if you want the changes to be reflected in the packaged game.

4.2. Consideration Repository

As you probably noticed, the configuration loader has two more fields that are not yet assigned any values. The first one is Consideration Repository.

Consideration Repositories are responsible for matching consideration implementations to string keys that you defined in the configuration file. Consideration implementation will be described in more detail below, but for now we’ll simply create an empty repository. Right-click on the project window, select Blueprint Class and choose UConsiderationRepository as the base class. Let’s call the new blueprint BP_ConsiderationRepository and assign it to our configuration loader’s Consideration Repository property.

Grail configuration loader with consideration repository
Figure 11. Adding a consideration repository to the configuration loader

4.3. Behavior Repository

Our next task is creating a Behavior Repository. Behavior Repositories are responsible for matching behavior implementations to string keys that you defined in the configuration file. Right-click on the project window, select Blueprint Class and choose UBehaviorRepository as the base class. Let’s call the new blueprint BP_Behavior Repository and assign it to our configuration loader’s Behavior Repository property.

Grail configuration loader with both repositories
Figure 12. Adding a behavior repository to the configuration loader

4.4. Implementing Behaviors and Behavior Providers

Let’s move on to the behavior implementation part.

To provide our agent with the ability to pick up objects, we need to override Grail’s Behavior class and create a new behavior called PickUpBehavior . The best way to learn about behaviors is to read this article.

Let’s assume that we have a special Actor class - APickable that represents a pickable item. Let’s also assume that the class has a single public method, PickUp that we want to call as soon as our character is close enough. Add a new C++ class and fill it with the following code. For readability, we’re placing function definitions in the same code snippet. In a real project, you’d probably want to have a separate header file for the declaration of the class.

class PickUp : public grail::Behavior
{
public:
  PickUp(APickable* Pickable)
    : Pickable{Pickable}
  {
  }

  virtual void Start(grail::AIEntity& owner) override
  {
    //The behavior assumes that the AI Controller is added to the entity's blackboard
    AAIController* EntityController = owner.GetBlackboard().GetValue<AAIController*>(EntityBlackboardKeys::AI_CONTROLLER);

    //Move to the pickable's location
    UAIBlueprintHelperLibrary::SimpleMoveToLocation(EntityController, Pickable->GetActorLocation());
  }

  virtual void Update(grail::AIEntity& owner, float deltaTime) override
  {
    const AAIController* EntityController = owner.GetBlackboard().GetValue<AAIController*>(EntityBlackboardKeys::AI_CONTROLLER);

    //If you're too far away, don't do anything
    if (FVector::Dist2D(EntityController->GetCharacter()->GetActorLocation(), Pickable->GetActorLocation()) > 120.0f)
    {
      return;
    }


    //Pick up the item and change the value of the flag
    Pickable->PickUp();
    bWasPickUpCalled = true;
  }

  virtual bool IsFinished(const grail::AIEntity& owner) const override
  {
    // The behavior should be considered finished after picking up the item
    return bWasPickUpCalled;
  }

  virtual std::string GetName() const override
  {
    return "pick_up";
  }

private:
  APickable* Pickable;
  bool bWasPickUpCalled = false;
};

Following that, we need to create a Behavior Provider for this behavior. UBehaviorProvider is an abstract class which requires us to implement GetBehaviorName, ProduceContext and ProduceInstance methods, which should allow Grail’s Utility Systems to create behavior blueprints and instantiate behaviors in runtime.

class MYGAME_API UPickUp : public UBehaviorProvider
{
  GENERATED_BODY()

protected:
  virtual std::string GetBehaviorName() const override
  {
    return "pick_up";
  }

  virtual std::vector<grail::EntityBlackboardPair> ProduceContext(const grail::AIEntity& Entity) const override
  {
    std::vector<grail::EntityBlackboardPair> Contexts;
    const TArray<APickable*>& Pickables = *Entity.GetBlackboard().GetValue<const TArray<APickable*>*>("pickables");
    for (APickable* Pickable : Pickables)
    {
      grail::Blackboard Blackboard;
      Blackboard.SetValue("pickable", Pickable);
      Blackboard.SetValue("location", Pickable->GetActorLocation());
      Contexts.emplace_back(grail::EntityBlackboardPair{&Entity, Blackboard});
    }

    return Contexts;
  }

  virtual std::unique_ptr<grail::Behavior> ProduceInstance(const grail::EntityBlackboardPair& Context) const override
  {
    return std::make_unique<PickUp>(Context.second.GetValue<APickable*>("pickable"));
  }
};
Be sure that the name returned by GetBehaviorName EXACTLY matches the name provided in the configuration file. Otherwise, the plugin won’t be able to properly provide this behavior to AI Entities.

Now we need to do something similar for the idle behavior.

class Idle : public grail::Behavior
{
  virtual void Start(grail::AIEntity& owner) override
  {
    UE_LOG(LogTemp, Log, TEXT("Starting idle behavior"));
  }

  virtual void Update(grail::AIEntity& owner, float deltaTime) override
  {
  }

  virtual bool IsFinished(const grail::AIEntity& owner) const override
  {
    return false;
  }

  virtual std::string GetName() const override
  {
    //this name is used as a display name in debug data
    return "idle";
  }
};
class MYGAME_API UIdle : public UBehaviorProvider
{
  GENERATED_BODY()

protected:
  virtual std::string GetBehaviorName() const override
  {
    return "idle";
  }

  virtual std::vector<grail::EntityBlackboardPair> ProduceContext(const grail::AIEntity& Entity) const override
  {
    //A single context is returned as there's always only one variant of the idle behavior
    return std::vector<grail::EntityBlackboardPair> { {&Entity, grail::Blackboard{}}};
  }

  virtual std::unique_ptr<grail::Behavior> ProduceInstance(const grail::EntityBlackboardPair& Context) const override
  {
    return std::make_unique<Idle>();
  }
};

4.5. Adding Behaviors to the Behavior Repository

As we now have our behaviors defined, we can add them to the Behavior Repository that we created before. Simply open the Behavior Repository’s blueprint editor and add both UPickUp and UIdle to the Behavior Providers array.

Behavior repository with behaviors
Figure 13. Behavior repository with two behaviors

4.6. Implementing Consideration Providers

Similarly to the Behavior Providers, we need to implement Consideration Providers

We need to implement DistanceConsideration which will calculate distances to pickable items. After that’s done, we’re going to write a class for DistanceConsiderationProvider and override UConsiderationProvider’s method `ProduceConsideration to return freshly implemented consideration.

The final DistanceConsideration and UConsiderationProvider code should look like this:

DistanceConsideration should look like this:

class DistanceConsideration : public grail::evaluator::UtilityConsideration
{
public:
  virtual float Evaluate(const grail::EntityBlackboardPair& Context) const override
  {
    return FVector::Dist2D(Context.first->GetBlackboard().GetValue<AAIController*>(EntityBlackboardKeys::AI_CONTROLLER)->GetCharacter()->GetActorLocation(),
      Context.second.GetValue<FVector>("location"));
  }

  virtual std::string GetDisplayName() const override
  {
    return "distance";
  }
};
UCLASS()
class MYGAME_API UDistance : public UConsiderationProvider
{
  GENERATED_BODY()
public:
  virtual FString GetConsiderationName() const override
  {
    return "distance";
  }

  virtual std::shared_ptr<grail::evaluator::UtilityConsideration> ProduceConsideration() const override
  {
    return std::make_shared<DistanceConsideration>();
  }
};

Lets do the same for the constant consideration.

class ConstConsideration : public grail::evaluator::UtilityConsideration
{
public:
  virtual float Evaluate(const grail::EntityBlackboardPair& Context) const override
  {
    return 1.0f;
  }

  virtual std::string GetDisplayName() const override
  {
    //this name is used as a display name in debug data
    return "constant";
  }
};
class GRAILPLUGINMVP_API UConstant : public UConsiderationProvider
{
  GENERATED_BODY()
public:
  virtual FString GetConsiderationName() const override
  {
    return "constant";
  }

  virtual std::shared_ptr<grail::evaluator::UtilityConsideration> ProduceConsideration() const override
  {
    return std::make_shared<ConstConsideration>();
  }
};
Be sure that the name returned by GetConsiderationName matches EXACTLY the corresponding consideration is called in the selected configuration file. Otherwise the plugin will be unable to properly provide this consideration to entities.

4.7. Adding Considerations to the Consideration Repository

Now we can add our consideration to the Consideration Repository that we created before. Simply open the Consideration Repository’s blueprint editor and add both UConstant and UDistance to the Behavior Providers array.

Consideration repository with considerations
Figure 14. Behavior repository with two behaviors

4.8. Using Other Reasoners

If you want to use another reasoner or make a custom one, you don’t need to implement behavior or consideration providers, as they are specific to Utility AI. What we need is to provide our own UReasonerFactory. An implementation can be found in the code snippet below:

UCLASS()
class UCustomReasonerFactory : public UReasonerFactory
{
  GENERATED_BODY()

  class FactoryInternal : public grail::helpers::IReasonerFactory
  {
    TMap<std::string, TFunction<std::unique_ptr<grail::Reasoner>()>> ReasonerCreationFunctions;

    public:
    //...
    grail::Reasoner CreateReasoner(const std::string& ReasonerID) override
    {
      return ReasonerCreationFunctions[ReasonerID];
    }
    //...
  }

public:
  virtual std::shared_ptr<grail::helpers::IReasonerFactory> CreateFactory()
  {
    return std::make_shared<FactoryInternal>();
  };
}

Then you can add the newly created factory to Grail Configuration Loader’s Reasoner Factories array.

4.9. Adding an AI Manager Actor to the Level

To properly update reasoners and entities' behaviors, AGrailAIManager actor needs to be placed on the level. Simply drag it onto the level viewport and it should be good to go.

You can customize the number of worker threads used by the manager and the reasoner timestep in Grail AI Manager’s details panel.

Grail AI Manager
Figure 15. Grail AI Manager’s settings

4.10. Preparing the AI Controller

Now that we have the configuration in place, we can create the AI Controller that will be used by Grail to move our character. Just create a new blueprint class inheriting from Unreal’s built-in AAIController and name it BP_GrailAIController

4.10.1. AI Entity Component

AIEntity is a basic Grail agent able to reason and execute selected behaviors. We’ve prepared an AIEntityComponent which simply needs to be attached to the AI Controller. This soldier is supposed to be an autonomous unit, so we need to attach AIEntityComponent to its Character Controller.

AIEntityComponent
Figure 16. AI Entity Component added to the AI Controller

Once that’s done, we can configure this component to suit our needs. First, select the GrailConfigurationLoader we’ve created from the Configuration Loader blueprint and then select the picker entity from the dropdown in the Entity Selection section. After that, you should be able to see the imported entity’s name in the corresponding field.

If you need your physical object to be controlled by more than one entity (for example in a twin stick shooter - one entity responsible for movement and one for attacking) you can follow the above process but using EntityGroupComponent instead of AIEntityComponent and everything should be just fine.

4.10.2. Blackboard Data Providers

As you could see in the code examples above, we assume that there are pieces of data that are present on the entity’y blackboard. The data should be provided by a custom Behavior Provider, inheriting from Grail’s UEntityBlackboardDataProvider.

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class GRAILPLUGINMVP_API UPickerBlackboardDataProvider : public UEntityBlackboardDataProvider
{
  GENERATED_BODY()

public:
  UPickerBlackboardDataProvider::UPickerBlackboardDataProvider()
  {
    PrimaryComponentTick.bCanEverTick = true;
    PrimaryComponentTick.bStartWithTickEnabled = true;
  }

  virtual void UPickerBlackboardDataProvider::ProvideBlackboardData(grail::Blackboard& InBlackboard) override
  {
    InBlackboard.SetValue(EntityBlackboardKeys::AI_CONTROLLER, Cast<AAIController>(GetOwner()));
    InBlackboard.SetValue<const TArray<APickable*>*>("pickables", &ObservedPickables);
  }

protected:
  virtual void UPickerBlackboardDataProvider::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override
  {
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    ObservedPickables.Reset();

    for (TActorIterator<APickable> PickableItr(GetWorld()); PickableItr; ++PickableItr)
    {
      ObservedPickables.Add(*PickableItr);
    }
  }

private:
  UPROPERTY()
  TArray<APickable*> ObservedPickables{};
};

Now you can add this component to the AIController.

Blackboard Data Provider
Figure 17. A Blackboard Data Provider added to the AI Controller

4.10.3. Creating and Registering Entities in Runtime

Having implemented all the necessary component, we need to add a small piece of blueprint code to make our entity register to Grail’s AI Manager.

We need to perform the following operations:

  1. Create the AI Entity, by calling Create Entity on AI Entity Component

  2. Provide blackboard data, by calling Provide Blackboard Data on Picker Blackboard Data Provider

  3. Get a reference to an AAIManager actor and register the entity. For simplicity, in this tutorial it is done by searching for AGrailAIManager using the Get Actor Of Class node, but you might want to store the manager actor in your game instance object.

AI Controller Blueprint Code
Figure 18. A Blackboard Data Provider added to the AI Controller

4.10.4. Attaching the Controller to a Character

Now you can attach the AI Controller to you characters like you normally would in a standard Unreal workflow.

5. Plugin ready

Congratulations! Now we’re ready to extensively make use of Grail in Unreal Engine projects. Good luck!