Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Artificial Intelligence in Unreal Engine 5

You're reading from   Artificial Intelligence in Unreal Engine 5 Unleash the power of AI for next-gen game development with UE5 by using Blueprints and C++

Arrow left icon
Product type Paperback
Published in Oct 2024
Publisher Packt
ISBN-13 9781836205852
Length 358 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Marco Secchi Marco Secchi
Author Profile Icon Marco Secchi
Marco Secchi
Arrow right icon
View More author details
Toc

Table of Contents (22) Chapters Close

Preface 1. Part 1: Introducing Artificial Intelligence in Games FREE CHAPTER
2. Chapter 1: Getting Started with AI Game Development 3. Chapter 2: Introducing the Unreal Engine AI System 4. Part 2: Understanding the Navigation System
5. Chapter 3: Presenting the Unreal Engine Navigation System 6. Chapter 4: Setting Up a Navigation Mesh 7. Chapter 5: Improving Agent Navigation 8. Chapter 6: Optimizing the Navigation System 9. Part 3: Working with Decision Making
10. Chapter 7: Introducing Behavior Trees 11. Chapter 8: Setting Up a Behavior Tree 12. Chapter 9: Extending Behavior Trees 13. Chapter 10: Improving Agents with the Perception System 14. Chapter 11: Understanding the Environment Query System 15. Part 4: Exploring Advanced Topics
16. Chapter 12: Using Hierarchical State Machines with State Trees 17. Chapter 13: Implementing Data-Oriented Calculations with Mass 18. Chapter 14: Implementing Interactable Elements with Smart Objects 19. Index 20. Other Books You May Enjoy Appendix – Understanding C++ in Unreal Engine

Understanding decorators

Decorators provide a way to add additional functionality or conditions to the execution of a portion of a behavior tree. As you already know from previous chapters, decorators are attached to either a composite or a task node and determine whether a branch in the tree (or even a single node) can be executed. By combining decorators with composite nodes, you can create behavior trees with prioritized behavior allowing for powerful logic capable of handling intricate scenarios. In Chapter 8, Setting Up a Behavior Tree, we used some built-in decorators but, in this section, I will give you more detailed information about creating your own custom decorators.

Explaining the BTAuxiliaryNode class

Both decorators and services inherit from the BTAuxiliaryNode class, which will let you implement the following functions:

  • OnBecomeRelevant(): This will be called when the auxiliary node – the one the decorator or service is attached to – becomes active
  • OnCeaseRelevant(): This will be executed when the auxiliary node becomes inactive
  • TickNode(): This will be executed at each auxiliary node tick

In Chapter 8, Setting Up a Behavior Tree, I presented you with some of these functions, so it’s good to know where they come from.

Creating C++ decorators

A decorator extends from the BTDecorator class, and in C++, its main implementable functions are as follows:

  • OnNodeActivation(): This is called when the underlying node is activated
  • OnNodeDeactivation(): This is called when the underlying node is deactivated
  • OnNodeProcessed(): This is called when the underlying node is deactivated or fails to activate
  • CalculateRawConditionalValue(): This computes the value of the decorator condition without considering the inverse condition

Additionally, you can use the IsInversed() function to check whether the decorator will handle the inversed conditional value.

Creating Blueprint decorators

Whenever creating a decorator with Blueprints Visual Scripting, you should extend from the BTDecorator_BlueprintBase class, which includes some additional code logic and events, in order to better manage it. You can create a decorator in the usual way – from Content Drawer – or you can select the New Decorator button from the behavior tree graph, as shown in Figure 9.9:

Figure 9.9 – Decorator creation

Figure 9.9 – Decorator creation

The main events you will have at your disposal when working with Blueprint-generated decorators are as follows:

  • Receive Execution Start AI: This is called when the underlying node is activated
  • Receive Execution Finish AI: This is called when the underlying node has finished executing its logic
  • Receive Tick AI: This is called on each tick
Figure 9.10 – Decorator nodes

Figure 9.10 – Decorator nodes

By keeping this in mind, you will have the ability to implement your own Blueprint decorators for your AI agents.

We are now going to implement our own decorator, one that will be checking a tag on an actor.

Implementing the CheckTagOnActor decorator

Now is the perfect time to create our first decorator. As you may recall, while implementing the BaseTarget class, we ensured that whenever a target gets hit, its tag is set to an undefined value. By implementing a decorator that checks an actor instance tag, we can determine whether the actor itself is a viable target.

So, let’s start by creating a new C++ class extending BTDecorator, and let’s call it BTDecorator_CheckTagOnActor. Once the class has been created, open the BTDecorator_CheckTagOnActor.h file and add the following declarations:

protected:
   UBTDecorator_CheckTagOnActor();
   UPROPERTY(EditAnywhere, Category=TagCheck)
    FBlackboardKeySelector ActorToCheck;
   UPROPERTY(EditAnywhere, Category=TagCheck)
    FName TagName;
   virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
   virtual void InitializeFromAsset(UBehaviorTree& Asset) override;

As you can see, we will be using a Blackboard key value – the ActorToCheck one – to check whether its referred value has a tag equal to TagName. This check will be handled by the CalculateRawConditionValue() function. Additionally, we will need to initialize any asset-related data, and this is usually done in the InitializeFromAsset() function, which is inherited by the BTNode superclass.

Now, open the BTDecorator_CheckTagOnActor.cpp file to start implementing the functions. Let’s start by adding the needed #include files:

#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"

Next, let’s implement the constructor:

UBTDecorator_CheckTagOnActor::UBTDecorator_CheckTagOnActor()
{
    NodeName = "Tag Condition";
    ActorToCheck.AddObjectFilter(this, GET_MEMBER_NAME_      CHECKED(UBTDecorator_CheckTagOnActor, ActorToCheck),         AActor::StaticClass());
    ActorToCheck.SelectedKeyName = FBlackboard::KeySelf;
}

What we are doing here, immediately after naming the node, holds significant importance. We are filtering key values to only allow Actor classes. This step ensures that only valid Blackboard keys related to actors will be accepted, maintaining the integrity and appropriateness of the inputs.

The CalculateRawConditionValue() function is going to be pretty straightforward:

bool UBTDecorator_CheckTagOnActor::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp,
                                                              uint8* NodeMemory) const
{
    const UBlackboardComponent* BlackboardComp = OwnerComp.      GetBlackboardComponent();
    if (BlackboardComp == nullptr) return false;
   const AActor* Actor = Cast<AActor>(BlackboardComp-     >GetValue<UBlackboardKeyType_Object>(ActorToCheck.       SelectedKeyName));
    return Actor != nullptr && Actor->ActorHasTag(TagName);
}

As you can see, we retrieve the Blackboard component and get the ActorToCheck key in order to check whether there is a valid Actor instance and whether it is tagged as a target.

Now, implement the last required function:

void UBTDecorator_CheckTagOnActor::InitializeFromAsset(UBehaviorTree& Asset)
{
    Super::InitializeFromAsset(Asset);
    if (const UBlackboardData* BBAsset = GetBlackboardAsset();       ensure(BBAsset))
    {
       ActorToCheck.ResolveSelectedKey(*BBAsset);
    }
}

This function retrieves the BlackboardData asset and resolves the selected key for ActorToCheck from that asset.

In this section, you have been provided with more advanced information about decorators, including specific considerations for implementing them in C++ or Blueprints. Additionally, you have successfully created a custom decorator that will be utilized by our upcoming gunner AI agent. This custom decorator will play a crucial role in creating the behavior and decision-making capabilities of the AI gunner agent, further improving its performance and effectiveness.

In the next section, I will be presenting you with some detailed information on how to implement services.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image