📌 RVO란?
RVO(Reciprocal Velocity Obstacles)는 움직이는 객체들이 서로의 속도와 방향을 고려하여 충돌을 피하는 기능이며, 서로를 장애물로 인식하고 충돌을 피하는 알고리즘이다.
이전에 Pathfinding 경로 찾기 기능은 움직이지 않는 오브젝트를 우회하도록 디자인하는 것이었다.
이와 달리 RVO는 "움직이는" 액터를 회피할 수 있고, 특히 경로 재계산 없이 속도와 방향만을 조절하여 실시간 회피가 가능하므로 다수의 액터가 있을 때 더욱 효율적이고 자연스러운 움직임을 제공하는게 장점이다.
✅ AI 캐릭터에 RVO 적용
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "RVO_Character.generated.h"
UCLASS()
class AI_TEST_API ARVO_Character : public ACharacter
{
GENERATED_BODY()
public:
ARVO_Character();
protected:
virtual void BeginPlay() override;
public:
// 타겟 위치로 이동
UFUNCTION(BlueprintCallable, Category = "AI Movement")
void MoveToTarget();
// RVO 회피 활성화/비활성화
UFUNCTION(BlueprintCallable, Category = "RVO")
void SetRVOAvoidanceEnabled(bool bEnabled);
public:
// 이동할 타겟 액터
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI Movement")
AActor* TargetActor;
// RVO 회피 설정
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RVO")
float AvoidanceRadius;
// RVO 계급 설정
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RVO")
float AvoidanceWeight;
private:
// AI 컨트롤러 캐싱
class AAIController* AIController;
};
#include "RVO_Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "AIController.h"
ARVO_Character::ARVO_Character()
{
AvoidanceRadius = 300.0f;
AvoidanceWeight = 0.5f;
UCharacterMovementComponent* MovementComponent = GetCharacterMovement();
if (MovementComponent)
{
// RVO 충돌 회피 알고리즘 활성화 여부
MovementComponent->bUseRVOAvoidance = true;
// AI가 다른 오브젝트를 감지하고 회피를 시작하는 반경
MovementComponent->AvoidanceConsiderationRadius = AvoidanceRadius;
// 캐릭터 회피 우선 순위(0.0f ~ 1.0f, 1에 가까울 수록 계급 높음)
MovementComponent->AvoidanceWeight = AvoidanceWeight;
}
}
void ARVO_Character::BeginPlay()
{
Super::BeginPlay();
AIController = Cast<AAIController>(GetController());
if (AIController && TargetActor)
{
FTimerHandle DelayHandle;
GetWorldTimerManager().SetTimer(DelayHandle, this, &ARVO_Character::MoveToTarget, 5.0f, false);
}
}
void ARVO_Character::MoveToTarget()
{
if (!AIController || !TargetActor) return;
AIController->MoveToActor(
TargetActor, // 목표 액터
150.0f, // 도착 판정 반경
true, // 충돌 영역이 겹치면 도착으로 간주
true, // 경로 탐색 사용
false // 목적지를 네비게이션 메시에 투영(Projection)하지 않음
);
UE_LOG(LogTemp, Display, TEXT("%s Moving to target: %s"), *GetName(), *TargetActor->GetName());
}
void ARVO_Character::SetRVOAvoidanceEnabled(bool bEnabled)
{
UCharacterMovementComponent* MovementComponent = GetCharacterMovement();
if (MovementComponent)
{
MovementComponent->bUseRVOAvoidance = bEnabled;
}
}
- GetCharacterMovement() -> bUseRVOAvoidance = true;
- RVO 충돌 회피 알고리즘 활성화 유무
- true로 설정하면 캐릭터가 다른 오브젝트나 장애물과의 충돌을 피하기 위한 자동 회피 기능이 작동
- 다수의 AI캐릭터가 서로의 존재를 인식하며 이동해야 하는 상황에서 필수적
- GetCharacterMovement() -> AvoidanceConsiderationRadius = 200.0f;
- 회피 반경 세팅
- 값이 작을 경우, 오브젝트가 가까워질 때까지 회피하지 않으므로 급격한 회피 동작 or 충돌 발생 가능
- 값이 클 경우, 오브젝트가 멀리 있을 때부터 회피를 시작하므로 부자연스럽거나 자원 낭비 발생 가능
- GetCharacterMovement() -> AvoiddanceWeight = 0.5f;
- 회피 가중치 세팅
- 값의 범위: 0.0f ~ 1.0f
- 0에 가까울 수록 낮은 계급, 1에 가까울 수록 높은 계급
- 계급이 낮은 캐릭터가 먼저 회피를 시작
✅ 월드 세팅
월드에 Nav Modifier Volume을 2개를 배치해서, Area Class를 Null로 설정하였다.
양쪽에 TargetPoint를 배치한 뒤 Modifier Vlome을 이용해, AI들이 좁은 통로를 이동 하도록 하였다.
그리고 각 캐릭터는 맞은 편 TargetPoint를 지정해주었다.
✅ RVO 캐릭터 BP 세팅
BeginPlay에 C++에서 정의했던 SetRVOAvoidanceEnabled 함수를 연결해주었고, Tick에서 AI에서 TargetPoint까지 Debug Arrow를 그어주었다.
🔔 테스트 결과
왼쪽은 RVO를 껐을 때의 상황인데, 중간 통로에서 AI들끼리 서로 부딪히며 목표 지점까지 정상적으로 이동할 수 없는 것을 확인할 수 있었다.
반면 오른쪽 RVO를 켰을 때의 상황은 서로를 장애물로 인식하며, 자연스럽게 목표지점까지 모든 AI가 도달한 것을 확인할 수 있었다.
'내배캠 > Unreal Engine' 카테고리의 다른 글
AI_NavLink (0) | 2025.04.23 |
---|---|
AI_Perception(플레이어 감지) (0) | 2025.04.23 |
AI_NavMesh (0) | 2025.04.22 |
미로 게임 구현(Dedicated Server) (0) | 2025.03.25 |
언리얼 엔진 패키징(데디케이티드 서버 빌드) (0) | 2025.03.21 |