Dev./UE 언리얼 엔진

[TIL] 아이템 랜덤 스폰 및 레벨 데이터 관리(아이템 테이블)

raindrovvv 2025. 2. 12. 18:36

💭회고

오늘은 언리얼 엔진에서 아이템 스폰 시스템을 구현하는 방법을 학습했다.
특히 데이터 테이블을 활용한 스폰 확률 관리에 대해 집중적으로 다뤘으며, 실무에서 많이 사용되는 누적 랜덤 뽑기 알고리즘을 적용했다. 앞으로 레벨별 차별화된 스폰 시스템과 성능 최적화를 위한 오브젝트 풀링 기법을 추가적으로 공부해보면 좋을 거 같다.

감기 때문에 학습 속도가 느려져서 과제하기 좀 빠듯해졌다. 밤을 새야 하나... 아 그러면 또 몸이 더 안좋아질 거 같고 허허;;

🗺️마인드맵

📒학습 내용

🎲 랜덤 아이템 스폰 구현

📌 1. SpawnVolume 클래스 구현

아이템을 스폰할 영역을 정의하고, 랜덤 좌표에서 아이템을 생성하는 클래스를 만든다.

  1. 새로운 C++ 액터 클래스 생성
  2. UBoxComponent를 활용하여 스폰 영역 설정
  3. GetRandomPointInVolume()을 사용해 박스 내 랜덤 좌표 생성
  4. SpawnItem()을 호출하여 해당 위치에 아이템 생성

🎯 1.1 GetRandomPointInVolume() 구현

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)  
    );  
}

핵심 개념

  • BoxExtent를 이용해 스폰 영역 크기를 가져온다.
  • BoxOrigin을 기준으로 랜덤 위치를 계산한다.
  • FMath::FRandRange()를 활용하여 각 축별 랜덤 좌표를 생성한다.

🎯 1.2 SpawnItem() 구현

void ASpawnVolume::SpawnItem(TSubclassOf<AActor> ItemClass)
{
    if (!ItemClass) return;

    GetWorld()->SpawnActor<AActor>(
        ItemClass,
        GetRandomPointInVolume(),
        FRotator::ZeroRotator
    );
}

핵심 개념

  • TSubclassOf<AActor>를 활용해 스폰할 아이템 클래스를 지정한다.
  • SpawnActor()를 호출하여 랜덤 위치에 아이템을 생성한다.
  • 초기 회전값은 FRotator::ZeroRotator로 설정한다.

🛠 디버깅 및 테스트

  • 블루프린트에서 SpawnVolume을 상속받아 맵에 배치한다.
  • GetRandomPointInVolume()이 정상적으로 랜덤 값을 반환하는지 디버깅한다.

📊 데이터 테이블을 활용한 스폰 확률 관리

📌 2.1 데이터 테이블을 활용하는 이유

왜 데이터 테이블을 사용할까?

  • 하드코딩 시 문제점 → 코드 수정 후 빌드가 필요해 불편하다.
  • 데이터 테이블을 활용하면 CSV, JSON에서 쉽게 관리 가능하다.
  • 기획자나 디자이너도 수정 가능하여 협업에 용이하다.

🎯 2.2 FItemSpawnRow 구조체 설계

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; // 스폰 확률  
};

핵심 개념

  • FTableRowBase를 상속받아 데이터 테이블과 연동 가능하다.
  • TSubclassOf<AActor>를 사용하여 아이템 클래스를 저장한다.
  • SpawnChance 필드를 추가하여 아이템별 스폰 확률을 관리한다.

🛠 데이터 테이블 생성 방법

 

  1. Miscellaneous(기타) → Data Table을 선택
  2. Row Structure에서 FItemSpawnRow를 선택
  3. CSV 파일을 활용해 데이터를 입력 후 임포트

📌 실무 팁

  • 대량 데이터는 CSV로 관리하고,
  • 테스트 데이터는 에디터에서 직접 수정하는 것이 효율적이다.

RowName(행 이름)은 키 값이라 고유해야 한다. 중복될 수 없다. - 해당 값을 검색할 때 쓰인다.
확률은 전부 합쳐서 100이 되게 해야 한다.
경로를 보면 끝에 'C'가 붙는다. 참조를 하려면 꼭 붙어있어야 한다.
에디터에서 설정하는 중


🎯 스폰 확률 시스템 및 확률 기반 로직

📌 3.1 SpawnRandomItem()

void ASpawnVolume::SpawnRandomItem()  
{  
    if (FItemSpawnRow* SelectedRow = GetRandomItem())  
    {       
        if (UClass* ActualClass = SelectedRow->ItemClass.Get())  
        {  
            SpawnItem(ActualClass);  
        }    
    }    
}

핵심 개념

  • GetRandomItem()을 호출해 확률 기반으로 아이템을 선택한다.
  • 선택된 아이템이 있다면 SpawnItem()을 호출하여 스폰을 수행한다.

🎯 3.2 확률 기반 스폰 로직 (누적 랜덤 뽑기 알고리즘)

FItemSpawnRow* ASpawnVolume::GetRandomItem() const  
{  
    if (!ItemDataTable) return nullptr;  

    TArray<FItemSpawnRow*> AllRows;  
    ItemDataTable->GetAllRows(TEXT("ItemSpawnContext"), AllRows);  

    if (AllRows.IsEmpty()) return nullptr;  

    float TotalChance = 0.f;  
    for (const FItemSpawnRow* Row : AllRows)  
    {  
        TotalChance += Row->SpawnChance;  
    }    

    float RandValue = FMath::FRandRange(0.f, TotalChance);  
    float AccumulatedChance = 0.f;  

    for (FItemSpawnRow* Row : AllRows)  
    {       
        AccumulatedChance += Row->SpawnChance;  
        if (RandValue <= AccumulatedChance)  
        {  
            return Row;  
        }    
    }    
    return nullptr;  
}

누적 확률 알고리즘 핵심

  • 전체 확률(TotalChance)을 계산한다.
  • 랜덤 값(RandValue)을 생성하여 아이템을 선택한다.
  • 누적 확률(AccumulatedChance)과 비교하여 해당 구간에 속하는 아이템을 반환한다.

🎈결과

1. 디버깅

빌드 완료 후, SpawnVolume 블루프린트를 확인
블루프린트 설정을 통해 디버깅!

2. 완성!


🎯 레벨별 차별화 및 실무 팁

📌 레벨별 아이템 스폰 차별화 방법

  1. 각 레벨마다 다른 데이터 테이블 사용 (관리 용이)
  2. 하나의 테이블에서 레벨 필터링 (코드로 처리)

📌 최적화 팁

  • 스폰 시 충돌 체크를 추가하여 아이템이 겹치지 않도록 한다.
  • 오브젝트 풀링을 활용해 성능을 개선한다.
  • 데이터 테이블을 수정하면 반드시 리임포트해야 한다.

🟣오늘의 옵시디언 현황