Skip to main content

AI Characters and AI Agents

Overview

This chapter mainly introduces how to create non-player AI Characters and AI Agents in the project.

Configuring and Driving Default AI Characters

Creating Characters

Create a new Level in the Component Editor and click Create Game Object -> Character to create a default character object.

Configure the default target character. Select the character object just created in the Hierarchy panel, then select CharacterMesh0(Inherited) in the Inspector panel. Click the button next to the Skeletal Mesh Config File property below. Select an .actor file in the pop-up File Browser window and click OK.

Note: Skeleton and animation assets of the selected .actor file need to be preset.

image-20230313154155040

Adjust the position of the Capsule and the Mesh respectively to make them both slightly above the terrain after selecting the CollisionCylinder(Inherited) and CharacterMesh0(Inherited) in the Inspector panel.

image-20230313174457929

Creating Blackboards and Behavior Trees

Take driving the AI character to move to the specified position as an example. Create a behavior tree resource (ai_walkman.bt) and a blackboard resource (ai_walkman.bb).

Create driving character behavior tree files. Please refer to Creating Behavior Trees and Creating Blackboards sections in the Behavior Tree Editor User Manual to create a behavior tree asset and a blackboard asset respectively.

image-20230314094239992

Double-click the blackboard resource ai_walkman.bb in the Resource Preview window to open the Behavior Tree Editor and enter the blackboard editing mode.

Click New Key in the Behavior Tree Editor to add two blackboard keys respectively, with one named Init of Bool type and the other named WayPoint of Vector type. Click Save after adding.

Please refer to Editing Blackboards for details on adding blackboard keys.

image-20230314102826921

Double-click the behavior tree resource ai_walkman.bt in the Resource Preview window to open the Behavior Tree Editor for editing mode.

Specify an associated blackboard resource for the behavior tree. Click the Entry node in the behavior tree diagram, and then set the blackboard resource just created as the blackboard associated with the behavior tree in the Details Panel.

image-20230314095337370

Association completed:

image-20230314103202430

Click the Entry node pin in the behavior tree diagram and drag it off to the blank space. Select the Selector node in the pop-up menu.

image-20230314103140676

Drag off the Selector node pin to create and connect two Sequence nodes in turn.

image-20230314100104069

Right-click on the left Sequence node and click Add New Decorator Node -> Blackboard to link a Blackboard Decorator node for it.

image-20230314100320546

Select the Blackboard Decorator node and set Blackboard Key as Init and Key Operation as Is Not Set in the Details Panel respectively.

image-20230314103114158

Link a Blackboard Decorator node for the other Sequence node and set Blackboard Key as Init and Key Operation as Is Set respectively.

image-20230314102520955

Connect a Wait node to the Sequence node on the left, and set Wait Time to 1.0 in the Details Panel.

image-20230314103037414

Set the position where the AI character will move to. Continue to connect a Set Vector Value node to the Sequence node on the left, and modify Value X and Value Y and Value Z in the Details Panel (The values here can be customized according to the current level). Set its Name to Set WayPoint and Blackboard Key to WayPoint.

image-20230314104711778

Connect a Set Bool Value node to the Sequence node on the left. Set its Name to Set BB Key Initialize Value, check Current Value, and modify the Blackboard Key to Init.

133464110547260302

Connect a Move To node to the Sequence node on the right. Set its Name to Move to WayPoint and Blackboard Key to WayPoint in the Details Panel.

133232586302282177

Finally, connect a Wait node to the Sequence node on the right, and set Wait Time to 300.0.

133232587905652646

Click Save after all the editing is done.

133232588523835668

To learn more about behavior trees, please refer to Behavior Tree Editor User Manual.

Setting AI Characters

Select LCharacter in the Hierarchy panel and LCharacter(Instance) in the Inspector panel respectively. Check the Auto Control AI property, and then click the button next to the Behavior Tree property. Select a behavior tree file in the pop-up window, where the behavior tree file just created is selected here.

image-20230314130040412

Configure AI command processing scripts. Select LSceneSettings-1 in the Hierarchy panel, then click the button next to the AI Script property in the Inspector panel. Select the script file ai_command_processor to be configured in the pop-up window. (The script files used are under editor_script/pl_ai of the Engine resources. The ai_command_processor.lua and public_attr.lua can be copied to the Script folder under the Project folder first, and then add AI Script.)

Note: Make sure that the resource directory is the one that the Editor can locate.

image-20230314130828900

Script files:

image-20230314130702595

At this point, configuring and driving a non-player AI character of is completed.

Playing

To make it easier to view the results after playing, click Create Game Object -> Default Pawn to create another Default Pawn object.

image-20230314113059713

After the creation is complete, adjust the Position and Angle of Default Pawn, and set Auto Possess Player to Player 0.

image-20230314131330001

Click the Play In Editor button to view the final result of this demo (The AI character moves to the specified position).

133232599777615044

Customizing AI Behaviors

Two callback methods are provided by the Engine currently to implement custom AI Behavior, i.e. C++ and Lua.

C++ Code Examples

Header file: i_ai_command_processor.h

Function declaration:

// Callback function prototypes
// This will be called when adding the AI object.
typedef void (*on_add_ai_object)(IAICommandProcessor* from, PERSISTID object,
const char* bt_file);

// This will be called when removing the AI object.
typedef void(*on_remove_ai_object)(IAICommandProcessor* from, PERSISTID object,
const char* bt_file);

// This will be called when updating the service node.
typedef void (*on_service)(IAICommandProcessor* from, PERSISTID object,
const char* bt_file, const char* bb_key);

// This will be called when updating the move to node.
typedef void (*on_move_to)(IAICommandProcessor* from, PERSISTID object,
bool is_move_to_object, PERSISTID goal_object,
const XMFLOAT3& goal_position, float accept_radius);

// This will be called when updating the play animation node.
typedef void (*on_play_animation)(IAICommandProcessor* from, PERSISTID object,
const char* animation_name);

// This will be called when aborting the play animation node.
typedef void(*on_abort_play_animation)(IAICommandProcessor* from, PERSISTID object,
const char* animation_name);

// This will be called when updating the play sound node.
typedef void(*on_play_sound)(IAICommandProcessor* from, PERSISTID object,
const char* sound_name, bool is_non_block);

// This will be called when updating the custom task node.
typedef void (*on_custom_task)(IAICommandProcessor* from, PERSISTID object,
const char* bt_file, const char* custom_task_name, const char* custom_task_data);

// This will be called when aborting the move to node.
typedef void (*on_abort_move_to)(IAICommandProcessor* from, PERSISTID object);

// This will be called when aborting the custom task node.
typedef void(*on_abort_custom_task)(IAICommandProcessor* from, PERSISTID object,
const char* bt_file, const char* custom_task_name, const char* custom_task_data);

// This will be called when updating the AI object direction.
typedef void(*on_update_direction)(IAICommandProcessor* from, PERSISTID object,
float x, float y, float z);

Callback registration method:

// Register callback
virtual bool RegisterOnAddAIObject(on_add_ai_object f);
virtual bool RegisterOnRemoveAIObject(on_remove_ai_object f);
virtual bool RegisterOnService(on_service f);
virtual bool RegisterOnMoveTo(on_move_to f);
virtual bool RegisterOnPlayAnimation(on_play_animation f);
virtual bool RegisterOnCustomTask(on_custom_task f);
virtual bool RegisterOnPlaySound(on_play_sound f) override;
virtual bool RegisterOnUpdateDirection(on_update_direction f) override;

// Register abort task callback
virtual bool RegisterOnAbortMoveTo(on_abort move_to f) override;
virtual bool RegisterOnAbortCustomTask(on_abort_custom task f) override;
virtual bool RegisterOnAbortPlayAnimation(on_abort_play_animation f) override;

Parameter types:

IAICommandProcessor* from: AIcommandProcessor object

PERSISTID object: The ID of the current AIObject

const char* bt_file: The currently bound behavior tree asset file

const char* bb_key: The blackboard key set by the current ServiceNode

Callback function registration query:

AICommandProcessor::IsRegisterCallback(EAICommandCallback:: OnService)

Lua Code Examples

The default callback file is the script file configured in the AI Script option of SceneSettings in the Component Editor.

For example: ai_command_processor.lua provided by the Engine has already provided a default implementation.

--AI
function on_init(self)
nx_log("ai init")

--Enable debug information sending
local ai_world = nx_value("ai_world")
ai_world.EnableDebug = true

--Initialization: Add AI object callback to perform blackboard initialization
nx_callback(self, "on_add_ai_object", "ai_add_ai_object")

--Task: AI move to
nx_callback(self, "on_move_to", "ai_move_to")

--Abort task: Abort task move to
nx_callback(self, "on_abort_move_to", "ai_abort_move_to")

--Task: Play animation
nx_callback(self, "on_play_animation", "ai_play_animation")

--Task: Custom task (For customization when common tasks implemented by the Engine are not yet included)
nx_callback(self, "on_custom_task", "ai_custom_task")

--Service: Update the blackboard regularly
nx_callback(self, "on_service", "ai_service")

--Event: Update AI object direction
nx_callback(self, "on_update_object_direction", "update_object_direction")

return 1
end

Register callback function sample codes:

--AI
function on_init(self)
--Service: update blackboard
nx_callback(self, "on_service", "ai_service")
return 1
end

Function sample codes:

function ai_service(self, target, behavior_tree, blackboard_key)
// File judgment

if "" == behavior_tree then
// TODO Something
end

// Blackboard key judgment
if "" == blackboard_key then
// TODO Something
end
end

Creating AI Agents

AI Agent provides a development method that allows developers to rebuild all AI behaviors.

It contains the callback function of AI properties and AI events, which can be used to create new AI behaviors by overloading and modifying the corresponding implementation.

Activating AI Agents

Select LSceneSettings-1 in the Hierarchy panel, and check Use AI Agent in the Inspector panel. After enabling this feature, all AI characters in the Level will be hosted by AI agents.

The initial AI Agent Class is the default one provided by the Engine. If you want to customize part or all of the AI Behavior, you can overload the default class LAIAgentObject provided in the Engine, and fill in the overloaded class name in the column of AI Behavior.

Note: All AI character properties and behaviors in the Level will be called back to the newly derived class after successful setup. Any class object that is not derived from LAIAgentObject will be invalid.

Interface file name:

i_ai_agent_interface.h

Default callback AI behavior (Partial):

public: /* Avoidance */

/* Is enabled crowd avoidance */
virtual bool IsEnabledDetourCrowd() const { return false; }

/* Get current group mark */
virtual int GetAvoidanceGroup() const { return -1; }

public: /* Event */
virtual void OnAddedToAIWorld(class IAIWorld* pAIWorld) {}
virtual void OnRemovedFromAIWorld(class IAIWorld* pAIWorld) {}

/* Path follow event */
virtual void OnPathFollow() {};
virtual void OnAbortPathFollow() {};
virtual void OnPathFollowFinished(bool bSuccess) {};

public: /* Behavior */

virtual bool OnPlayAnimation(const char* sAniName) { return false; }
virtual bool OnAbortPlayAnimation(const char* sAniName) { return false; }

virtual bool OnPlaySound(const char* sSoundName, bool bNonBlock) { return false; }
virtual bool OnAbortPlaySound(const char* sSoundName) { return false; }

virtual bool OnMoveTo(XMFLOAT3 vGoalPosition, float fGoalRadius,
float fTimeout = 0.0f) { return false; }
virtual bool OnAbortMoveTo() { return false; }

virtual bool OnCustomTask(const char* sFileName, const char* sTaskName,
const char* sTaskData) { return false; };
virtual bool OnAbortCustomTask(const char* sFileName, const char* sTaskName,
const char* sTaskData) { return false; };

Default callback properties (Partial):

    virtual void SetScale(float x, float y, float z) {}
virtual void SetAngle(float x, float y, float z) {}
virtual void SetPosition(float x, float y, float z) {};

virtual bool GetScale(float& x, float& y, float& z) const { return false; }
virtual bool GetAngle(float& x, float& y, float& z) const { return false; }
virtual bool GetPosition(float& x, float& y, float& z) const { return false; };
virtual XMFLOAT3 GetPosition() const { return XMFLOAT3(FLT_MAX, FLT_MAX, FLT_MAX); };
virtual XMFLOAT3 GetVelocity() const { return XMFLOAT3(0.0f, 0.0f, 0.0f); };

virtual void SetPositionX(float fValue) {}
virtual void SetPositionY(float fValue) {}
virtual void SetPositionZ(float fValue) {}

virtual bool GetPositionX(float& fOutValue) const { return false; }
virtual bool GetPositionY(float& fOutValue) const { return false; }
virtual bool GetPositionZ(float& fOutValue) const { return false; }

virtual void SetAngleX(float fValue) {}
virtual void SetAngleY(float fValue) {}
virtual void SetAngleZ(float fValue) {}

virtual float GetAngleX() const { return 0.0f; }
virtual float GetAngleY() const { return 0.0f; }
virtual float GetAngleZ() const { return 0.0f; }
    virtual float GetYaw() const { return 0.0f; }
virtual float GetRoll() const { return 0.0f; }
virtual float GetPitch() const { return 0.0f; }

virtual bool GetEyeHeight(float& fOutValue) const { return false; }
virtual bool GetAgentHeight(float& fOutValue) const { return false; }
virtual bool GetAgentRadius(float& fOutValue) const { return false; }

virtual void SetEnableFocus(bool bValue) {}
virtual bool GetEnableFocus() const { return true; }

virtual bool GetAgentBoundsExtent(float& ExtentX, float& ExtentY,
float& ExtentZ) const { return false; }

virtual float GetMaxSpeed() const { return 0.0f; }

virtual bool IsCanFly() { return false; }
virtual bool IsCanWalk() { return false; }
virtual bool IsCanJump() { return false; }
virtual bool IsCanSwim() { return false; }
virtual bool IsCanCrouch() { return false; }

virtual bool IsFlying() { return false; }
virtual bool IsFalling() { return false; }
virtual bool IsWalking() { return false; }
virtual bool IsJumping() { return false; }
virtual bool IsSwimming() { return false; }