티스토리 뷰
🟩 오늘의 목표
- UBoxComponent를 활용해 특정 영역 내 랜덤 좌표에 아이템을 스폰하는 SpawnVolume을 제작한다.
- 언리얼 엔진의 DataTable 기능을 활용해 아이템별 스폰 확률 데이터를 외부에서 관리하는 법을 익힌다.
- 누적 확률 알고리즘을 코드로 구현하여 설정된 확률에 따라 아이템이 스폰되는 실전 로직을 완성한다.
🟧 1. 랜덤 위치에 아이템 스폰하기
🟦 레벨 세팅 및 콜리전 컴포넌트 활용
- Edit - Project Settings - Maps & Modes에서 Default Maps를 BasicLevel로 변경하여 실습 환경을 준비함.
- UBoxComponent는 박스 형태의 충돌 범위를 제공하며, 실제 눈에 보이는 메시는 아니지만 내부의 오버랩이나 충돌을 감지하고 좌표를 계산하는 용도로 활용 가능함.
🟦 SpawnVolume 클래스 설계
새로운 액터 클래스인 ASpawnVolume을 생성하여 스폰 영역과 로직을 정의함.
// SpawnVolume.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SpawnVolume.generated.h"
class UBoxComponent;
UCLASS()
class SPARTAPROJECT_API ASpawnVolume : public AActor
{
GENERATED_BODY()
public:
ASpawnVolume();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Spawning")
USceneComponent* Scene;
// 스폰 영역을 담당할 박스 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Spawning")
UBoxComponent* SpawningBox;
// 스폰 볼륨 내부에서 무작위 좌표를 얻어오는 함수
UFUNCTION(BlueprintCallable, Category="Spawning")
FVector GetRandomPointInVolume() const;
// 특정 아이템 클래스를 스폰하는 함수
UFUNCTION(BlueprintCallable, Category="Spawning")
void SpawnItem(TSubclassOf<AActor> ItemClass);
};
🟦 랜덤 좌표 계산 및 스폰 구현
GetScaledBoxExtent와 FMath::FRandRange를 조합하여 박스 범위 내의 임의의 위치를 리턴함.
// SpawnVolume.cpp
#include "SpawnVolume.h"
#include "Components/BoxComponent.h"
ASpawnVolume::ASpawnVolume()
{
PrimaryActorTick.bCanEverTick = false;
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
SetRootComponent(Scene);
SpawningBox = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawningBox"));
SpawningBox->SetupAttachment(Scene);
}
FVector ASpawnVolume::GetRandomPointInVolume() const
{
// 박스 컴포넌트의 가로/세로/높이 절반 길이를 구함
FVector BoxExtent = SpawningBox->GetScaledBoxExtent();
// 박스 중심 위치
FVector BoxOrigin = SpawningBox->GetComponentLocation();
// 각 축별로 랜덤 값 생성 후 중심점에 더함
return BoxOrigin + FVector(
FMath::FRandRange(-BoxExtent.X, BoxExtent.X),
FMath::FRandRange(-BoxExtent.Y, BoxExtent.Y),
FMath::FRandRange(-BoxExtent.Z, BoxExtent.Z)
);
}
void ASpawnVolume::SpawnItem(TSubclassOf<AActor> ItemClass)
{
if (!ItemClass) return;
GetWorld()->SpawnActor<AActor>(
ItemClass,
GetRandomPointInVolume(),
FRotator::ZeroRotator
);
}
🟧 2. 아이템 스폰 확률 데이터 테이블
🟦 ItemSpawnRow 구조체 정의
데이터 테이블의 각 행을 매핑하기 위해 FTableRowBase를 상속받는 FItemSpawnRow 구조체를 선언함.
// ItemSpawnRow.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "ItemSpawnRow.generated.h"
USTRUCT(BlueprintType)
struct FItemSpawnRow : public FTableRowBase
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName ItemName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> ItemClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float SpawnChance;
};
🟦 CSV 임포트 및 관리
- RowName을 첫 번째 열로 둔 CSV 파일을 작성하여 데이터를 엔진으로 임포트함.
- 블루프린트 클래스 경로를 사용할 때는 반드시 끝에 _C 접미사를 붙여야 UClass로 인식됨.
- 기획 데이터 수정 시 코드를 빌드할 필요 없이 DataTable만 Reimport 하거나 에디터에서 직접 수정하면 즉시 반영됨.
🟧 3. 스폰 로직에 확률 적용
🟦 확률 기반 아이템 선택 로직
DataTable에 등록된 모든 아이템의 확률 합을 구하고, 누적 확률 알고리즘을 통해 하나의 아이템을 선정함.
// SpawnVolume.cpp 확률 관련 함수 구현
FItemSpawnRow* ASpawnVolume::GetRandomItem() const
{
if (!ItemDataTable) return nullptr;
TArray<FItemSpawnRow*> AllRows;
static const FString ContextString(TEXT("ItemSpawnContext"));
ItemDataTable->GetAllRows(ContextString, AllRows);
if (AllRows.IsEmpty()) return nullptr;
// 1) 전체 확률 합 구하기
float TotalChance = 0.0f;
for (const FItemSpawnRow* Row : AllRows)
{
if (Row) TotalChance += Row->SpawnChance;
}
// 2) 0 ~ TotalChance 사이의 랜덤 값 생성
const float RandValue = FMath::FRandRange(0.0f, TotalChance);
float AccumulateChance = 0.0f;
// 3) 누적 확률로 아이템 선택
for (FItemSpawnRow* Row : AllRows)
{
AccumulateChance += Row->SpawnChance;
if (RandValue <= AccumulateChance)
{
return Row;
}
}
return nullptr;
}
void ASpawnVolume::SpawnRandomItem()
{
if (FItemSpawnRow* SelectedRow = GetRandomItem())
{
if (UClass* ActualClass = SelectedRow->ItemClass.Get())
{
SpawnItem(ActualClass);
}
}
}
🟧 핵심 요약 및 주의사항
- UFUNCTION(BlueprintCallable) : C++에서 작성한 스폰 로직을 블루프린트의 BeginPlay나 Tick에서 호출하기 위해 필수적임.
- FTableRowBase : 데이터 테이블 기능을 사용하기 위한 필수 상속 기반임.
- 누적 확률 로직 : 단순 랜덤보다 기획 의도를 정확히 반영할 수 있으며, SpawnRandomItem 함수 하나로 간편하게 제어 가능함.
- 레벨별 관리 : SpawnVolume의 ItemDataTable 변수에 레벨마다 다른 데이터 테이블 에셋을 할당하여 난이도 조절이 용이함.
'내일배움캠프 Unreal_7기 > 본캠프' 카테고리의 다른 글
| TIL - 46일 (0) | 2026.02.02 |
|---|---|
| TIL - 45일차 (0) | 2026.01.30 |
| TIL - 43일차 (1) | 2026.01.28 |
| TIL - 42일차 (0) | 2026.01.27 |
| TIL - 41일차 (0) | 2026.01.26 |

