The crowd system works by default with ADetourCrowdAIController only. In this article, we’ll look at how to make it work with other AI controllers, player characters or other types of actors. We’ll start from AI controllers, as that is the easiest one to fix, and then look at the other cases.
Making any AI controller work with crowd avoidance
The detour crowd AI controller uses UCrowdFollowingComponent, a type of UPathFollowingComponent. To make any AI controller work with crowd avoidance, all we need to do is replace the default path following comp with the crowd following comp.
The code below shows the basic set up:
//.h file
class MYPROJECT_API AMyAIController : public AAIController {}
// ... other things ...
AMyAIController(const FObjectInitializer& ObjectInitializer);
}
//.cpp file
AMyAIController::AMyAIController(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<UCrowdFollowingComponent>(TEXT("PathFollowingComponent")))
{
//other contructor logic
}
To replace the default path following component, we need to use the constructor overload which takes an FObjectInitializer as its parameter. Then, all we need to do is use SetDefaultSubobjectClass
to override the component type.
With this, the AI controller should be using crowd avoidance.
Making player characters and other actor types work with crowd avoidance
For crowd avoidance to work with players or other types of actors, we need to implement ICrowdAgentInterface. This makes our actor into a “crowd agent”, which we then need to register with the UCrowdManager.
Below is an example of how this can be done:
//.h file
UCLASS()
class MY_API AMyCharacter : public ACharacter, public ICrowdAgentInterface
{
GENERATED_BODY()
public:
//override the necessary functions from the crowd agent interface
virtual FVector GetCrowdAgentLocation() const override;
virtual FVector GetCrowdAgentVelocity() const override;
virtual void GetCrowdAgentCollisions(float& CylinderRadius, float& CylinderHalfHeight) const override;
virtual float GetCrowdAgentMaxSpeed() const override;
protected:
virtual void BeginPlay() override;
}
//.cpp file
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
auto CrowdManager = UCrowdManager::GetCurrent(this);
if(CrowdManager)
{
CrowdManager->RegisterAgent(this);
}
}
FVector AMyCharacter::GetCrowdAgentLocation() const
{
return GetActorLocation();
}
FVector AMyCharacter::GetCrowdAgentVelocity() const
{
return GetCharacterMovement()->GetVelocityForRVOConsideration();
}
void AMyCharacter::GetCrowdAgentCollisions(float& CylinderRadius, float& CylinderHalfHeight) const
{
CylinderRadius = GetCapsuleComponent()->GetScaledCapsuleRadius();
CylinderHalfHeight = GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
}
float AMyCharacter::GetCrowdAgentMaxSpeed() const
{
return GetCharacterMovement()->GetMaxSpeed();
}
There are two things to note here:
- First, we override four functions from ICrowdAgentInterface. Their implementation is fairly straightforward - we simply return information from the character.
- Second, we register the character with the UCrowdManager in BeginPlay. This is necessary for the crowd system to be aware that this “agent” exists.
With this set up, any AI’s using crowd avoidance should avoid this character.
Alternative approach
Depending on how your game works, it may be beneficial to implement the crowd agent as part of your player controller instead of the player pawn. In this case the implementation is more or less the same, except you need to use the controller’s pawn in the function implementations.