콘텐츠로 이동

UE5 Smart Object System — AI 상호작용 프레임워크

Smart Object System은 UE5의 AI 생태계 플러그인 중 하나로, 월드의 “상호작용 가능한 지점”(앉기, 숨기, 음료 마시기 등)을 데이터로 정의하고 여러 AI 에이전트가 경쟁적으로 사용할 수 있게 관리합니다. MassAI, StateTree, EQS와 함께 사용하면 완성도 높은 AI 행동을 구성할 수 있습니다.


Edit > Plugins:
- Smart Objects → 활성화
- MassAI (선택적) → 활성화
Build.cs:
PublicDependencyModuleNames.AddRange(new[]
{
"SmartObjectsModule",
"GameplayTags",
"StateTreeModule",
});

// SmartObjectDefinition은 에디터에서 데이터 에셋으로 생성
// Content Browser > 우클릭 > Miscellaneous > Data Asset
// 클래스: SmartObjectDefinition
// 코드에서 슬롯 정의
UCLASS()
class UBenchSitDefinition : public USmartObjectDefinition
{
GENERATED_BODY()
public:
UBenchSitDefinition()
{
// 슬롯 2개 (벤치 좌석)
FSmartObjectSlotDefinition Slot1, Slot2;
// 슬롯 위치 (오브젝트 로컬 공간)
Slot1.Offset = FTransform(
FRotator::ZeroRotator,
FVector(-30.f, 0.f, 0.f));
Slot2.Offset = FTransform(
FRotator::ZeroRotator,
FVector(30.f, 0.f, 0.f));
// 태그: 이 슬롯이 제공하는 기능
Slot1.Tags.AddTag(TAG_SmartObject_Sit);
Slot2.Tags.AddTag(TAG_SmartObject_Sit);
Slots.Add(Slot1);
Slots.Add(Slot2);
}
};

// 월드 오브젝트에 컴포넌트 추가
UCLASS()
class ABench : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere)
USmartObjectComponent* SmartObjectComp;
UPROPERTY(EditAnywhere)
USmartObjectDefinition* Definition;
public:
ABench()
{
SmartObjectComp = CreateDefaultSubobject<USmartObjectComponent>(
TEXT("SmartObject"));
}
virtual void BeginPlay() override
{
Super::BeginPlay();
if (Definition)
SmartObjectComp->SetDefinition(Definition);
// Smart Object 시스템에 등록
SmartObjectComp->RegisterWithSubsystem();
}
};

#include "SmartObjectSubsystem.h"
#include "SmartObjectComponent.h"
UCLASS()
class UMyAIComponent : public UActorComponent
{
GENERATED_BODY()
FSmartObjectClaimHandle _claimHandle;
USmartObjectSubsystem* _soSubsystem;
public:
virtual void BeginPlay() override
{
Super::BeginPlay();
_soSubsystem = GetWorld()->GetSubsystem<USmartObjectSubsystem>();
}
// 특정 태그의 Smart Object 검색 및 클레임
bool TryClaimSeat()
{
if (!_soSubsystem) return false;
// 조건: "앉기" 태그를 가진 슬롯 검색
FSmartObjectRequestFilter Filter;
Filter.BehaviorDefinitionClass = nullptr;
Filter.RequiredTags.AddTag(TAG_SmartObject_Sit);
// 가장 가까운 오브젝트 찾기
FSmartObjectRequest Request(
FBox::BuildAABB(GetOwner()->GetActorLocation(),
FVector(500.f)),
Filter);
FSmartObjectRequestResult Result;
if (!_soSubsystem->FindSmartObject(Request, Result))
return false;
// 슬롯 클레임
_claimHandle = _soSubsystem->Claim(Result);
if (!_claimHandle.IsValid())
return false;
// 클레임된 슬롯의 월드 트랜스폼 획득
FTransform SlotTransform;
_soSubsystem->GetSlotTransform(
_claimHandle, SlotTransform);
// AI를 슬롯으로 이동
MoveToTransform(SlotTransform);
return true;
}
// 슬롯 사용 시작
void StartUsing()
{
if (_claimHandle.IsValid())
_soSubsystem->Use(GetOwner(), _claimHandle);
}
// 슬롯 해제
void Release()
{
if (_claimHandle.IsValid())
{
_soSubsystem->Release(_claimHandle);
_claimHandle = FSmartObjectClaimHandle::InvalidHandle;
}
}
virtual void EndPlay(EEndPlayReason::Type Reason) override
{
Release();
Super::EndPlay(Reason);
}
};

StateTree에서 Smart Object 태스크 사용:
- SmartObject Find Task → 조건에 맞는 슬롯 검색
- SmartObject Claim Task → 슬롯 클레임
- Move To Smart Object Task → 슬롯으로 이동
- SmartObject Use Task → 사용 시작 / 애니메이션 재생
- SmartObject Release Task → 슬롯 해제
StateTree 상태 구성 예:
Idle → FindBench (EQS) → MoveToBench → SitDown → Sitting → StandUp → Idle
↑ SmartObject Find ↑ SmartObject Claim/Move ↑ Use ↑ Release

// Environment Query System에서 Smart Object 슬롯을 쿼리 아이템으로 사용
// EQS Generator: Smart Object Slot Generator
// - Definition 필터
// - Tag 필터
// - 거리 기반 점수
// C++에서 직접 반경 쿼리
TArray<FSmartObjectRequestResult> Results;
FSmartObjectRequest Request(
FBox::BuildAABB(AgentLocation, FVector(1000.f)),
Filter);
_soSubsystem->FindAllSmartObjects(Request, Results);
// 점수 기반 정렬
Results.Sort([&](const auto& A, const auto& B)
{
float DistA = FVector::Dist(
GetSlotLocation(A), AgentLocation);
float DistB = FVector::Dist(
GetSlotLocation(B), AgentLocation);
return DistA < DistB;
});

Smart Object System은 “어디서 무엇을 할 수 있는가”를 데이터로 정의해 AI 코드를 단순화합니다. 슬롯 클레임은 반드시 EndPlay 또는 AI 사망 시 해제해 다른 에이전트가 사용할 수 있도록 하세요. StateTree와 연동하면 Find → Claim → Move → Use → Release 워크플로를 노드 그래프로 선언적으로 구성할 수 있어 복잡한 AI 상호작용을 깔끔하게 관리할 수 있습니다.