아이템 스폰 및 레벨 데이터 관리하기
랜덤 위치에 아이템 스폰하기
1️⃣ 레벨 셋팅하기
- Resources 폴더에 Maps 폴더에는 3개의 레벨이 이미 존재합니다. 각각 난이도에 따라서 크기가 다른 맵이고, BasicLevel- Intermediate Level- Advanced Level 순서로 크기가 작아집니다.

- 위 3개의 레벨을 Content - Maps 폴더로 이동 시킨 후, 기존에 사용하던 MainLevel에서 BasicLevel을 기본 레벨로 지정합니다.
- Edit - Project Settings - Maps & Modes에서 Defaults Maps를 BasicLevel로 변경을 해줍니다.
- MainLevel은 삭제를 해줍니다.
2️⃣ 콜리전 컴포넌트로 스폰 영역 지정하기
- 스폰 영역을 지정할 수 있는 방법은 여러 방식이 있고, 가장 정교한 방법중 하나인 수학적 알고리즘으로 정의 할 수도 있지만 여기서는 조금더 간단하게 지정하겠습니다.
- 언리얼 엔진에서는 직접 지정한 박스(상자), 구체, 캡슐 등 원하는 형태의 ‘콜리전 컴포넌트’ 내부에서 임의의 좌표를 뽑아 아이템(액터)을 스폰할 수 있습니다.
- 이번에는 우리가 만든 아이템들을 레벨 상에서 자연스럽게 랜덤 배치하려고 합니다. 가장 간단한 방법으로, 콜리전 컴포넌트인 UBoxComponent를 활용해보겠습니다.
- 우선, 새로운 C++ 클래스 Actor를 상속한 클랫스로, SpawnVolume 이라는 이름으로 클래스를 하나 생성합니다.


UBoxComponent* SpawningBox;
- 이 컴포넌트가 박스 형태의 콜리전 영역을 나타냅니다.
- 언리얼 엔진의 UBoxComponent는 박스 내부에서 오버랩(Overlap)이나 충돌(Collision)을 감지할 수 있는 컴포넌트입니다.
- 실제로 눈에 보이는 3D 메시는 아니며, “박스 형태의 충돌 범위”만 제공합니다.
GetRandomPointInVolume()
- SpawningBox 범위 내부에서 랜덤 좌표(FVector)를 리턴합니다.
- 이 좌표를 이용해 아이템을 생성하면 “직육면체 형태 범위 안에서” 임의의 위치에 아이템이 뜨는 효과를 낼 수 있습니다.
SpawnItem(TSubclassOf<AActor> ItemClass);
- 파라미터로 받은 아이템 클래스를 SpawnVolume 내부의 랜덤 위치에 생성(SpawnActor)하는 함수입니다.
TSubclassOf<AActor>: 하위 클래스까지도 전부 다 포함해서 지정해 줄 수 있는 템플릿.
- 하드 레퍼런스: 클래스가 항상 메모리에 로드된 상태에서 바로 접근
- 반대로 소프트 레퍼런스인: TSoftClassPtr 도 존재합니다
- 소프트 레퍼런스: 클래스의 경로만 유지합니다. 그래서 만약에 해단되는 어떤 클래스가 필요한 상황이 되면 그때 가서 메모리에 로드를 하게 됩니다.
GetScaledBoxExtent()
- 박스 컴포넌트의 실제 “가로/세로/높이 절반 길이(Extent)”를 반환합니다 (중심에서 끝부분 까지의 길이이기 때문에 절반 길이입니다). 에디터에서 Scale을 조절하면 여기에도 반영됩니다.
GetComponentLocation()
- 컴포넌트의 위치 = 현재 우리가 사용하려는 BoxOrigin의 위치
FMath::FRandRange(a, b)
- a ~ b 사이 임의의 float 값을 반환합니다.
- 여기서는 X, Y, Z 좌표마다 랜덤 값을 생성하여 BoxOrigin에 더해줍니다.
SpawnActor
- AKAsset* SpawnedActor1 = (AKAsset*) GetWorld()->SpawnActor(AKAsset::StaticClass(), NAME_None, &Location);
AActor* UWorld::SpawnActor
(
UClass* Class,
FName InName,
FVector const* Location,
FRotator const* Rotation,
AActor* Template,
bool bNoCollisionFail,
bool bRemoteOwned,
AActor* Owner,
APawn* Instigator,
bool bNoFail,
ULevel* OverrideLevel,
bool bDeferConstruction
)
SpawnActor 관련 UE 공식문서: https://dev.epicgames.com/documentation/unreal-engine/spawning-actors-in-unreal-engine?lang=ko
Spawning Actors in Unreal Engine | Unreal Engine 5.7 Documentation | Epic Developer Community
Methods of creating new instances of Actors in gameplay code.
dev.epicgames.com
SpawnVolume.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SpawnVolume.generated.h"
class UBoxComponent;
UCLASS()
class BC_CH3_ASSIGNMENT_5_API ASpawnVolume : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASpawnVolume();
// 우선 테스트라 public으로
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);
virtual void Tick(float DeltaTime) override;
};
SpawnVolume .cpp
#include "SpawnVolume.h"
#include "Components/BoxComponent.h"
#include "Engine/World.h"
#include "GameFramework/Actor.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
{
// 1) 박스 컴포넌트의 스케일된 Extent, 즉 x/y/z 방향으로 반지름(절반 길이)을 구함
FVector BoxExtent = SpawningBox->GetScaledBoxExtent();
// 2) 박스 중심 위치
FVector BoxOrigin = SpawningBox->GetComponentLocation();
// 3) 각 축별로 -Extent ~ +Extent 범위의 무작위 값 생성
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
);
}
3️⃣ 아이템 랜덤 스폰 테스트하기
- 빌드를 하고 언리얼 에디터로 돌아와서 이 SpawnVolume 클래스를 상속받은 BP_SpawnVolume Blueprint 클래스를 생성합니다.

- BP_SpawnVolume을 레벨에 드래그해 놓고, 적절히 위치와 스케일을 조정합니다.

- 3개의 레벨 모두 이렇게 BP_SpawnVolume 을 맵의 크기에 맞게 적절하게 셋팅을 해줍니다.
2. 아이템 스폰 확률 데이터 테이블 만들기
1️⃣Item Data 구조체 만들기
- 어떤 아이템이 몇 % 확률로 스폰되는지를 코드로 직접 하드코딩하면, 매번 수정할 때마다 빌드를 해줘야 해서 번거롭습니다.
- 언리얼 엔진의 데이터 테이블을 사용하면, 이를 엑셀 (CSV)이나 JSON 파일로 관리해서 엔진 안으로 임포트하고, 코드나 블루프린트에서 쉽게 불러 쓸 수 있습니다. 기획자나 디자이너도 엑셀에서 숫자만 바꾸면 되므로 매우 편리합니다.
- 우선, 데이터 테이블의 각 “행(Row)”을 C++ 구조체로 매핑해야 합니다. 언리얼 엔진은 FTableRowBase라는 기본 구조체를 제공하며, 이를 상속한 구조체를 만들면, 각 CSV (또는 JSON) 행을 FItemSpawnRow 구조체에서 정해준 형태로 받아올 수 있게 됩니다.
- 이러한 구조체는 클래스가 아니기 때문에, 부모 클래스를 선택할 수 있는 옵션에 나타나지 않습니다. 따라서 None을 선택해서 ItemSpawnRow C++ 파일을 만들어줍니다.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h" // FTableRowBase 정의가 들어있는 헤더
#include "ItemSpawnRow.generated.h"
USTRUCT(BlueprintType)
struct FItemSpawnRow : public FTableRowBase
{
GENERATED_BODY()
public:
// 아이템 이름
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName ItemName;
// 어떤 아이템 클래스를 스폰할지
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> ItemClass; // 원래는 소프트 레퍼런스인 TSoftClassPtr 로 하는게 맞습니다.
// 이 아이템의 스폰 확률
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float SpawnChance;
};
- FTableRowBase
- 언리얼 엔진에서 “이 구조체는 데이터 테이블로 쓸 수 있다”라고 인식하게 해주는 베이스 구조체입니다.
- TSoftClassPtr<AActor>
- 소프트 레퍼런스로, 클래스를 바로 로드하지 않고도 경로만 기억해둘 수 있습니다.
- 필요할 때 Get()을 통해 실제 UClass*를 얻어 인스턴스를 생성할 수 있습니다.
2️⃣ CSV 파일 작성 및 임포트하기 (방법 1)
- 엑셀을 열고 2열부터 Column 이름을 작성합니다.
- 1열은 “RowName”으로 인식됩니다. Key 값으로 사용할 값을 입력해줘야 합니다.
- 2열부터는 각 아이템 (데이터) 정보를 한 줄씩 기록합니다.

- 경로는 각 BP 클래스 우클릭 → Copy Reference 로 정확히 가져옵니다. 그리고 블루프린트의 경우 뒤에 "_C"가 붙어야 BP 클래스로 인식되니까 마지막에 반드시 붙여주세요.
- Copy Reference는 언리얼 엔진 런타임에서 사용되는 “클래스/오브젝트” 경로입니다.
- 블루프린트/C++로 자산 로드 시 ConstructorHelpers::FClassFinder 등에서 활용.
- Copy Reference는 언리얼 엔진 런타임에서 사용되는 “클래스/오브젝트” 경로입니다.

- CSV로 저장
- 언리얼 콘텐츠 폴더가 아니라 본인이 편한 로컬 위치에 파일명 ItemSpawnTable.csv 로 **CSV UTF-8 (쉼표로 분리)**로 저장합니다.
- 언리얼 에디터로 돌아와, Content Browser에서 Blueprints 폴더에서 우클릭 → Import to /Game/Blueprints 를 선택, 아래와 같이 옵션을 설정하고 Apply를 클릭해줍니다.

- 그러면 다음과 같이 성공적으로 DatTable이 만들어진 것을 볼 수 있습니다.
- 만약 CSV 파일에서 데이터가 업데이트되었을 경우 Reimport를 해주면 됩니다.

3️⃣ 언리얼 에디터에서 직접 데이터 테이블 수정하기 (방법 2)
- CSV를 쓰는 대신, 언리얼 에디터 내에서 직접 편집하는 방법도 있습니다.
- Content Browser 창에서 우클릭 → Miscellaneous → Data Table 선택
- 팝업창에서 Row Structure로 FItemSpawnRow를 선택하고 OK
- 새로 생성된 DataTable 에셋을 열면, 에디터 UI 상에서 아래처럼 Row 정보를 바로 편집 가능합니다.
- Add Row 버튼을 눌러 행을 추가하고, Row Name, ItemName, ItemClass, SpawnChance 등을 원하는 값으로 넣습니다.
- 하지만 이런 방식은 Excel처럼 대량의 데이터를 한 번에 편집하기는 불편할 수 있습니다.
- 상황에 따라 에디터 직접 편집 / CSV 임포트 모두 필요에 따라 병행할 수도 있습니다. </aside><aside>
아이템 스폰 확률 데이터 테이블 만들기
1️⃣Item Data 구조체 만들기
- 어떤 아이템이 몇 % 확률로 스폰되는지를 코드로 직접 하드코딩하면, 매번 수정할 때마다 빌드를 해줘야 해서 번거롭습니다.
- 언리얼 엔진의 데이터 테이블을 사용하면, 이를 엑셀 (CSV)이나 JSON 파일로 관리해서 엔진 안으로 임포트하고, 코드나 블루프린트에서 쉽게 불러 쓸 수 있습니다. 기획자나 디자이너도 엑셀에서 숫자만 바꾸면 되므로 매우 편리합니다.
- 우선, 데이터 테이블의 각 “행(Row)”을 C++ 구조체로 매핑해야 합니다. 언리얼 엔진은 FTableRowBase라는 기본 구조체를 제공하며, 이를 상속한 구조체를 만들면, 각 CSV (또는 JSON) 행을 FItemSpawnRow 구조체에서 정해준 형태로 받아올 수 있게 됩니다.
- 이러한 구조체는 클래스가 아니기 때문에, 부모 클래스를 선택할 수 있는 옵션에 나타나지 않습니다. 따라서 None을 선택해서 ItemSpawnRow C++ 파일을 만들어줍니다.


- FTableRowBase
- 언리얼 엔진에서 “이 구조체는 데이터 테이블로 쓸 수 있다”라고 인식하게 해주는 베이스 구조체입니다.
- TSoftClassPtr<AActor>
- 소프트 레퍼런스로, 클래스를 바로 로드하지 않고도 경로만 기억해둘 수 있습니다.
- 필요할 때 Get()을 통해 실제 UClass*를 얻어 인스턴스를 생성할 수 있습니다.
ItemSpawnRow.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h" // FTableRowBase 정의가 들어있는 헤더
#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 파일로)
2️⃣언리얼 에디터에서 직접 데이터 테이블 수정하기
- Content Browser 창에서 우클릭 → Miscellaneous → Data Table 선택
(Content Browser 에서 우클릭 -> Miscellaneous -> Data Table-> Pick Row Structure -> 위에서 만든 ItemSpawnRow 선택)

팝업창에서 Row Structure로 FItemSpawnRow를 선택하고 OK


- 새로 생성된 DataTable 에셋을 열면, 에디터 UI 상에서 아래처럼 Row 정보를 바로 편집 가능합니다.
- Add Row 버튼을 눌러 행을 추가하고, Row Name, Item Name, ItemClass, SpawnChance 등을 원하는 값으로 넣습니다.
- Row Name: Key 값으로 생각하시면 됩니다.고유 한 이름. !절대 중복될수 없습니다.(찾을 때 이 이름으로 찾습니다)
- Item Name: 일종의 참고 사항 같은 것. FName 형태


- Iteam Class 의 경로 끝에 보면 .C 가 붙는데 참조를 하려면 이게 꼭 붙어 있어야 한다는 특징이 있습니다.

완성된 데이터 테이블의 모습:

- 하지만 이런 방식은 Excel처럼 대량의 데이터를 한 번에 편집하기는 불편할 수 있습니다.
- 상황에 따라 에디터 직접 편집 / CSV 임포트 모두 필요에 따라 병행할 수도 있습니다.
3️⃣CSV 파일 작성 및 임포트하기
- 엑셀을 열고 2열부터 Column 이름을 작성합니다.
- 1열은 “RowName”으로 인식됩니다. Key 값으로 사용할 값을 입력해줘야 합니다.
- 2열부터는 각 아이템 (데이터) 정보를 한 줄씩 기록합니다.
- 경로는 각 BP 클래스 우클릭 → Copy Reference 로 정확히 가져옵니다. 그리고 블루프린트의 경우 뒤에 "_C"가 붙어야 BP 클래스로 인식되니까 마지막에 반드시 붙여주세요.
- /Script/Engine.Blueprint'/Game/Blueprint/BP_BigCoinItem.BP_BigCoinItem'
- 그냥 복붙이후는 "_C" 가 안붙기 때문에 따로 꼭 붙여줘야 합니다.

- Copy Reference는 언리얼 엔진 런타임에서 사용되는 “클래스/오브젝트” 경로입니다.
- 블루프린트/C++로 자산 로드 시 ConstructorHelpers::FClassFinder 등에서 활용.

- CSV로 저장
- 언리얼 콘텐츠 폴더가 아니라 본인이 편한 로컬 위치에 파일명 ItemSpawnTable.csv 로 CSV UTF-8 (쉼표로 분리)로 저장합니다.

- 언리얼 에디터로 돌아와, Content Browser에서 Blueprints 폴더에서 우클릭 → Import to /Game/Blueprints 를 선택, 아래와 같이 옵션을 설정하고 Apply를 클릭해줍니다.
- 폴더 내에서 우클릭 후 Import to Current Folder 해도 괜찮습니다.



데이터 테이블 관련 UE 공식 문서: https://dev.epicgames.com/documentation/unreal-engine/coder-05-manage-item-and-data-in-an-unreal-engine-game?lang=ko
Coder 05 Manage Item and Data in an Unreal Engine Game | Unreal Engine 5.7 Documentation | Epic Developer Community
Learn to use Item Data Structs, Data Assets, and Data Tables to define items and store and organize item data for scalability in Unreal Engine.
dev.epicgames.com
스폰에 확률 적용하기
1️⃣ 데이터 테이블을 사용하여 스폰 로직 구현하기
- SpawnVolume 클래스로 돌아와서, 확률 로직으로 “어떤 아이템을 스폰할지” 결정하는 코드로 다시 수정을 해봅시다.
- SpawnRandomItem()
- “데이터 테이블에서 확률 계산으로 Row 하나 선택” + “스폰”을 분리해놓아 코드가 깔끔해집니다.
- GetRandomItem()
- 테이블의 모든 Row를 가져와 확률합을 구하고, 확률에 따라 한 개를 뽑아 리턴합니다.
- Algo::Accumulate
- 언리얼 엔진에서 추가한 C++ 템플릿 함수로, STL의 std::accumulate와 유사하게 작동합니다.
- 각 Row의 SpawnChance를 계속 더해 총 합을 구합니다.
- 이것을 이용해서 누적형태로 랜덤 뽑기를 실행합니다.
- 컴퓨터 친화적 계산: 컴퓨터는 0과 1 사이의 소수나 0~100 사이의 난수를 만드는 데 최적화되어 있습니다.
- 단 한 번의 난수 생성: 주사위를 여러 번 굴릴 필요 없이, 단 하나의 숫자로 당첨자를 바로 판별합니다.
- 유연한 확률 제어: 소수점 단위의 아주 미세한 확률(예: 0.001%)도 완벽하게 구현할 수 있습니다.
- 빠른 속도 (성능 우수): 데이터를 정렬해 두면 이진 탐색(Binary Search) 알고리즘을 통해 수만 개의 아이템 중에서도 당첨 항목을 눈 깜짝할 사이에 찾습니다.
- 확률 수정의 용이성: 특정 아이템의 가중치만 바꾸면 전체 누적 합산만 다시 계산하면 되므로 기획 변경에 유연합니다.
- 정확성: 모든 확률의 총합이 100%가 되지 않더라도(예: 가중치 합이 450), 전체 가중치 합을 기준으로 난수를 만들면 비율에 맞춰 정확히 작동합니다.
- 데이터 관리 비용: 아이템이 추가되거나 삭제될 때마다 누적 확률 배열을 매번 새로 계산(업데이트)해야 합니다.
- 직관성 부족: 코드를 처음 보는 개발자나 기획자에게는 단순 주사위 굴리기 방식보다 직관적으로 이해하기 어려울 수 있습니다.
이 로직을 이용해서 천장 시스템(Pity System 혹은 보정 확률) 사용자가 일정 횟수 이상 실패했을 때 최고 등급 아이템의 당첨 확률을 강제로 높이거나 확정 지급하는 안전장치를 구현하기도 합니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemSpawnRow.h" // 우리가 정의한 구조체
#include "SpawnVolume.generated.h"
class UBoxComponent;
UCLASS()
class BC_CH3_ASSIGNMENT_5_API ASpawnVolume : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASpawnVolume();
UFUNCTION(BlueprintCallable, Category = "Spawning")
void SpawnRandomItem();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Spawning")
USceneComponent* Scene;
// 스폰 영역을 담당할 박스 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Spawning")
UBoxComponent* SpawningBox;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawning")
UDataTable* ItemDataTable;
//
FItemSpawnRow* GetRandomItem() const;
// 특정 아이템 클래스를 스폰하는 함수
UFUNCTION(BlueprintCallable, Category="Spawning")
void SpawnItem(TSubclassOf<AActor> ItemClass);
// 스폰 볼륨 내부에서 무작위 좌표를 얻어오는 함수
UFUNCTION(BlueprintCallable, Category="Spawning")
FVector GetRandomPointInVolume() const;
};
#include "SpawnVolume.h"
#include "Components/BoxComponent.h"
#include "Engine/World.h"
#include "GameFramework/Actor.h"
ASpawnVolume::ASpawnVolume()
{
PrimaryActorTick.bCanEverTick = false;
// 박스 컴포넌트를 생성하고, 이 액터의 루트로 설정
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
SetRootComponent(Scene);
SpawningBox = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawningBox"));
SpawningBox->SetupAttachment(Scene);
}
void ASpawnVolume::SpawnRandomItem()
{
if (FItemSpawnRow* SelectedRow = GetRandomItem())
{
if (UClass* ActualClass = SelectedRow->ItemClass.Get())
{
SpawnItem(ActualClass);
}
}
}
FVector ASpawnVolume::GetRandomPointInVolume() const
{
// 1) 박스 컴포넌트의 스케일된 Extent, 즉 x/y/z 방향으로 반지름(절반 길이)을 구함
FVector BoxExtent = SpawningBox->GetScaledBoxExtent();
// 2) 박스 중심 위치
FVector BoxOrigin = SpawningBox->GetComponentLocation();
// 3) 각 축별로 -Extent ~ +Extent 범위의 무작위 값 생성
return BoxOrigin + FVector(
FMath::FRandRange(-BoxExtent.X, BoxExtent.X),
FMath::FRandRange(-BoxExtent.Y, BoxExtent.Y),
FMath::FRandRange(-BoxExtent.Z, BoxExtent.Z)
);
}
FItemSpawnRow* ASpawnVolume::GetRandomItem() const
{
if (!ItemDataTable)
return nullptr;
// 1) 모든 Row(행) 가져오기
TArray<FItemSpawnRow*> AllRows;
static const FString ContextString(TEXT("ItemSpawnContext")); // 디버깅 용도
ItemDataTable->GetAllRows(ContextString, AllRows);
if (AllRows.IsEmpty()) return nullptr;
// 2) 전체 확률 합 구하기
float TotalChance = 0.0f; // 초기화
for (const FItemSpawnRow* Row : AllRows) // AllRows 배열의 각 Row를 순회
{
if (Row) // Row가 유효한지 확인
{
TotalChance += Row->SpawnChance; // SpawnChance 값을 TotalChance에 더하기
}
}
// 3) 0 ~ TotalChance 사이 랜덤 값
const float RandValue = FMath::FRandRange(0.0f, TotalChance);
float AccumulateChance = 0.0f; // 랜덤 값 보정
//Algo::Accumulate( AllRows, AccumulateChance)
// 4) 누적 확률로 아이템 선택
for (FItemSpawnRow* Row : AllRows)
{
AccumulateChance += Row->SpawnChance;
if (RandValue <= AccumulateChance)
{
return Row;
}
}
return nullptr;
}
void ASpawnVolume::SpawnItem(TSubclassOf<AActor> ItemClass)
{
if (!ItemClass) return;
GetWorld()->SpawnActor<AActor>(
ItemClass,
GetRandomPointInVolume(),
FRotator::ZeroRotator
);
}
빌드 후, BP_SpawnVolume 로 돌아가서, 속성에서 Item Data Table에도 기존에 만들어둔 데이터테이블인 ItemSpawnTable 에셋을 할당해줍니다.

이벤트 그래프 SpawnRandomItem() 함수를 스크린샷과 같이 10번의 호출을 하도록 로직 노드를 생성합니다.

이후 플레이를 해보면 확률에 따라 5개의 아이템이 스폰되는 것을 확인할 수 있습니다.

2️⃣ 레벨마다 다른 확률 데이터 적용하기
- 원래 기획 취지에 의하면 레벨별로 다른 확률을 가져야 합니다. 이를 구현하는 방법은 여러 가지가 있습니다.
1.레벨별로 서로 다른 DataTable 에셋을 사용
- CSV(또는 JSON)에 각각 다른 확률을 넣고, 레벨1 맵에서는 BP_SpawnVolume가 ItemSpawnTable_Level1을 바라보도록 설정.
- 레벨2 맵에서는 ItemSpawnTable_Level2 할당… 등등.
- 관리가 직관적이며 간단합니다.
2.단일 DataTable에 “레벨 구분” 컬럼 추가
- 예: CSV에 LevelIndex (또는 LevelName) 같은 컬럼을 하나 더 두고, 각각 어느 레벨에 해당하는지 적습니다.
- 코드를 수정하여, 현재 레벨이 1인지 2인지 확인 후 “필터링된 Row만” 확률 계산.
- 한 DataTable에서 모든 레벨 정보를 한 번에 관리할 수 있는 장점이 있으나, 코드가 약간 복잡해집니다.
번외 | GetWorld() 관련 찾아본 내용
-
- 액터 생성 (SpawnActor): 월드에 새로운 액터나 캐릭터를 동적으로 스폰할 때 사용합니다.코드를 사용할 때는 주의가 필요합니다.
- GetWorld()->SpawnActor<AMyActor>(ActorClass, Location, Rotation);
- 타이머 매니저 (TimerManager): 일정 시간 뒤에 함수를 실행하거나 주기적으로 반복하는 타이머를 설정/해제합니다.코드를 사용할 때는 주의가 필요합니다.
- GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AMyClass::MyFunction, 2.0f, false);
- 게임 인스턴스 접근 (GetGameInstance): 씬 전환 시에도 유지되는 전역 데이터나 서브시스템을 가져올 때 사용합니다.코드를 사용할 때는 주의가 필요합니다.
- UGameInstance* GameInst = GetWorld()->GetGameInstance();GetWorld()를 통해 게임 내 가상 공간의 인스턴스에 접근하고, 월드와 상호작용하는 다양한 관리자 및 시스템 함수를 사용할 수 있습니다. 주로 액터(Actor)나 컴포넌트(Component) 내부에서 사용됩니다.GetWorld()-> 뒤에 포인터 접근 연산자(->)를 붙여 아래와 같은 기능들을 주로 호출합니다.
- 액터 생성 (SpawnActor): 월드에 새로운 액터나 캐릭터를 동적으로 스폰할 때 사용합니다.코드를 사용할 때는 주의가 필요합니다.
주의사항
-
- nullptr 반환 가능성: 월드에 아직 배치되지 않은 순수 UObject나 일부 애니메이션 노티파이(NotifyState) 등에서는 GetWorld()가 유효하지 않아 nullptr을 반환할 수 있습니다. 호출 전 if (GetWorld() != nullptr) 검사를 하거나, 유효한 액터를 통해 간접 참조해야 합니다.
- 오브젝트에서 사용: 기본 UObject 클래스에서 GetWorld()를 사용하려면 클래스 기본 생성자 설정에서 bWantsInitializeOnBeginPlay를 활성화하거나 가상 함수를 오버라이딩해야 정상적으로 월드를 반환받을 수 있습니다.
주요 활용 기능
- 언리얼 엔진(UE)에서 GetWorld()->는 현재 게임 월드(UWorld) 객체에 접근하기 위해 호출하는 핵심 함수입니다.
추천
SpawnActor 관련 UE 공식문서: https://dev.epicgames.com/documentation/unreal-engine/spawning-actors-in-unreal-engine?lang=ko
Spawning Actors in Unreal Engine | Unreal Engine 5.7 Documentation | Epic Developer Community
Methods of creating new instances of Actors in gameplay code.
dev.epicgames.com
데이터 테이블 관련 UE 공식 문서: https://dev.epicgames.com/documentation/unreal-engine/coder-05-manage-item-and-data-in-an-unreal-engine-game?lang=ko
Coder 05 Manage Item and Data in an Unreal Engine Game | Unreal Engine 5.7 Documentation | Epic Developer Community
Learn to use Item Data Structs, Data Assets, and Data Tables to define items and store and organize item data for scalability in Unreal Engine.
dev.epicgames.com
[페이지] Unreal Engine | 언리얼 엔진