How To Implement AI Movement On A Basic Pawn?

Hello Everyone, I have a question about Pawn. I want to make an AI that from Pawn Class not from Character Class. I want to make an AI with Pawn class because I need to spawn big number of Pawn in game but only need a basic movement only. So I make a basic pawn and I create a simple movement component for the pawn.

// This is Header File

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PawnMovementComponent.h"
#include "CustomPawnMovementComponent.generated.h"

/**
 * 
 */
UCLASS()
class MEDIEVALPANDEMIC_API UCustomPawnMovementComponent : public UPawnMovementComponent
{
	GENERATED_BODY()

public:
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	// Nav Movement Component Function
	virtual void RequestDirectMove(const FVector& MoveVelocity, bool bForceMaxSpeed) override;
	virtual bool CanStartPathFollowing() const override;
	virtual bool CanStopPathFollowing() const override;
	virtual float GetPathFollowingBrakingDistance(float MaxSpeed) const override;
};
// This is CPP File


#include "CustomPawnMovementComponent.h"

void UCustomPawnMovementComponent::TickComponent(float DeltaTime, ELevelTick TickType,
	FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if(!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime)){return;}

	FVector DesiredMovement = ConsumeInputVector().GetClampedToMaxSize(1.f);
	if(!DesiredMovement.IsNearlyZero())
	{
		FHitResult Hit;
		SafeMoveUpdatedComponent(DesiredMovement, UpdatedComponent->GetComponentRotation(), true, Hit);

		if(Hit.IsValidBlockingHit())
		{
			SlideAlongSurface(DesiredMovement, 1.f - Hit.Time, Hit.Normal, Hit);
		}
	}
}

void UCustomPawnMovementComponent::RequestDirectMove(const FVector& MoveVelocity, bool bForceMaxSpeed)
{
	Super::RequestDirectMove(MoveVelocity, bForceMaxSpeed);

	
}

bool UCustomPawnMovementComponent::CanStartPathFollowing() const
{
	return Super::CanStartPathFollowing();
}

bool UCustomPawnMovementComponent::CanStopPathFollowing() const
{
	return !IsFalling();
}

float UCustomPawnMovementComponent::GetPathFollowingBrakingDistance(float MaxSpeed) const
{
	if(bUseFixedBrakingDistanceForPaths)
	{
		return FixedPathBrakingDistance;
	}

	// const float Braking = 16384.f;
	//
	// float BrakingDistance = 
	return MaxSpeed;
}

PAWN CLASS

// Header File

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnTest.generated.h"

UCLASS()
class MEDIEVALPANDEMIC_API APawnTest : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	APawnTest();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	// OnConstruction
	virtual void OnConstruction(const FTransform& Transform) override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

private:
	/** Capsule Component */
	UPROPERTY(EditDefaultsOnly, Category = "Components")
	class UCapsuleComponent* CapsuleComponent;
	
	/** Skeletal Mesh Component */
	UPROPERTY(EditDefaultsOnly, Category = "Components")
	class USkeletalMeshComponent* SkeletalMeshComponent;
	
	/** Arrow Component */
	UPROPERTY(EditDefaultsOnly, Category = "Components")
	class UArrowComponent* ArrowComponent;
	
	/** AI Controller */
	UPROPERTY(EditDefaultsOnly, Category = "Controller")
	class AEnemyAIController* EnemyAI;

	/** Boolean for handling auto compute mesh location on Capsule Bottom */
	UPROPERTY(EditDefaultsOnly, Category = "Constructions")
	bool bAutoComputeMeshZLocation;

	/** Movement Component */
	UPROPERTY(VisibleAnywhere, Category = "Components")
	class UCustomPawnMovementComponent* MovementComponent;


	/** Function */

	/** Set Mesh Location To Bottom Capsule */
	void SetMeshLocationToBottomOfCapsule();
	
	/** Set Mesh Rotation To Right Rotation */
	void SetMeshRotationToRightRotation();
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "PawnTest.h"

#include "CustomPawnMovementComponent.h"
#include "EnemyAIController.h"
#include "Components/ArrowComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/FloatingPawnMovement.h"
#include "Kismet/KismetSystemLibrary.h"

// Sets default values
APawnTest::APawnTest()
{
 	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule Component"));
	RootComponent = CapsuleComponent;
	CapsuleComponent->SetCapsuleSize(34.f, 90.f);

	SkeletalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Skeletal Mesh"));
	SkeletalMeshComponent->SetupAttachment(GetRootComponent());

	ArrowComponent = CreateDefaultSubobject<UArrowComponent>(TEXT("Arrow"));
	ArrowComponent->SetupAttachment(GetRootComponent());

	MovementComponent = CreateDefaultSubobject<UCustomPawnMovementComponent>(TEXT("Movement Component"));
	MovementComponent->UpdatedComponent = GetRootComponent();

	bAutoComputeMeshZLocation = false;
	SetMeshRotationToRightRotation();
	SetMeshLocationToBottomOfCapsule();

}

// Called when the game starts or when spawned
void APawnTest::BeginPlay()
{
	Super::BeginPlay();
	
	CapsuleComponent->SetCollisionProfileName(TEXT("Pawn"));
	CapsuleComponent->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_No;
	SkeletalMeshComponent->SetCollisionProfileName(TEXT("CharacterMesh"));

	// FLatentActionInfo Info;
	// UKismetSystemLibrary::Delay(GetWorld(), 3.f, Info);
	
	EnemyAI = Cast<AEnemyAIController>(GetController());
	if(EnemyAI)
	{
		UE_LOG(LogTemp, Warning, TEXT("Move"));
		EnemyAI->MoveToLocation(FVector(0.f, 0.f, 259.f));
	}


}

void APawnTest::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction(Transform);
	
	if(bAutoComputeMeshZLocation)
	{
		SetMeshLocationToBottomOfCapsule();
	}
}

// Called every frame
void APawnTest::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	EnemyAI = Cast<AEnemyAIController>(GetController());
	if(EnemyAI)
	{
		UE_LOG(LogTemp, Warning, TEXT("Move"));
		EnemyAI->MoveToCastleGate();
	}
}

// Called to bind functionality to input
void APawnTest::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
}

void APawnTest::SetMeshLocationToBottomOfCapsule()
{
	if(SkeletalMeshComponent)
	{
		FVector NewLoc = FVector(0.f);
		NewLoc.Z -= CapsuleComponent->GetUnscaledCapsuleHalfHeight();
		SkeletalMeshComponent->SetRelativeLocation(NewLoc);
	}
}

void APawnTest::SetMeshRotationToRightRotation()
{
	if(SkeletalMeshComponent)
	{
		SkeletalMeshComponent->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));
	}
}

I already make the Custom Pawn Movement Component like that code, but I dont know why, the Pawn do not want to move in the level. even that my AI Controller is valid in the pawn class and there is a navmesh bound in the level already. I also tested it using FloatingPawnMovement and it is work but the pawn is floating not walking. So How do you guys think I should do? Thank you very much!

Is this for like an RTS or some other type of game that maybe doesn’t need default character movement?

You may still be better off just using Character. Unreal is supposedly able to handle a large amount of characters pretty well.

When characters move using AI, it uses a different Movement mode, something like Navmesh_Following, instead of Ground. It might be making use of some optimizations there. Chances are whatever Unreal does is a way better optimization than what you may be attempting since it’s been worked on for so long, and people have needed large amounts of characters in the past.

It just a normal game with high amount of AI that only need walking around like that.

I See, thank you for the answer

Thanks for the post ImAGoodSpoon1

I can attest that lots of characters is likely not a good approach if you want thousands of AI’s. Even using vertex texture animation and unreal’s optimization it’s not long before you reach limits

Also worth mentioning that this approach no longer works with UE5 as is :frowning: