[내일배움캠프/UE] 아이템 랜덤 스폰

김세희·2025년 7월 3일

✍️Today I Learned

  1. 아이템 랜덤 스폰
  2. 스폰 영역 지정
  3. 아이템 확률 지정

아이템 랜덤 스폰

지정된 범위 내의 랜덤한 좌표에서 아이템 클래스를 랜덤으로 선택하여 스폰한다.

아이템 클래스 랜덤 선택

TSubclassOf<>

- 하드 레퍼런스
클래스가 항상 메모리에 로드된 상태에서 바로 접근

TSoftclassPtr<>

- 소프트 레퍼런스
클래스의 경로만 유지. 클래스가 필요한 상황에 메모리에 로드해서 사용

둘 다 포인터라기 보다는 클래스를 참조하기 위한 데이터 구조이다.
보통 TSoftclassPtr 을 사용하는 걸 더 권장하지만 사용하려면 다른 조치를 더 해야해서 일단 TSubclassOf를 사용한다.

void ASpawnVolume::SpawnItem(TSubclassOf<AActor> ItemClass) 
{
	if (!ItemClass) return;
	GetWorld()->SpawnActor<AActor>(
		ItemClass,	// 스폰할 액터 클래스
		GetRandomPointInVolume(),	// 스폰 위치
		FRotator::ZeroRotator	// 회전
	);
}

스폰 영역 지정

Actor 블루프린트 생성 -> Box Collision 추가 -> 영역 내 랜덤한 좌표에 아이템 스폰

스폰에 콜리전 컴포넌트 사용하는 이유

- 시각적으로 범위 확인
- 레벨 디자인에서 쉽게 조정 가능
- Box 컴포넌트가 위치 계산이 편리함
- 충돌이나 트리거를 계산할 수 있는 컴포넌트

랜덤 좌표 출력

FVector ASpawnVolume::GetRandomPointInVolume() const
{
	// 박스 크기
	// GetScaledBoxExtent(): 중심부터 끝까지 거리를 반환-> 절반 크기를 벡터로 출력
	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)
	);
}

아이템 확률

아이템 스폰 확률을 데이터 테이블에 저장하여 관리한다. 데이터 테이블의 row는 구조체로 되어있다. 스폰 영역을 관리하는 클래스에서 이 테이블을 이용해 아이템을 랜덤하게 스폰한다.

구조체 생성

  1. none으로 C++클래스 생성
  2. 구조체에 맞게 코드 수정
#pragma once

#include "CoreMinimal.h"
#include "ItemSpawnRow.generated.h"

// 구조체 선언
// BlueprintType: 블루프린트에서 인식 가능해야한다. 이 구조체를 변수로 만들 수 있다.
// FTableRow를 상속받아 해당 구조체를 데이터 테이블 row로 사용
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;
};
  1. Datatable 생성
    Miscellaneuos -> Datatable -> 구조체 선택
    각 행에 값 입력. RowName이 키값이다.

랜덤 아이템 스폰

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

	TArray<FItemSpawnRow*> AllRows;
	// ItemSpawnContext 를 FString으로 저장
	// 데이터테이블에서 행을 가져올때 디버깅 정보를 제공하는 데 사용된다.
	// 데이터테이블에서 오류 발생 시 이 문자열이 오류 메시지에 포함된다.
	static const FString ContextString(TEXT("ItemSpawnContext"));
	ItemDataTable->GetAllRows(ContextString, AllRows);

	if (AllRows.IsEmpty()) return nullptr;

	float TotalChance = 0;
	for (const FItemSpawnRow* Row : AllRows) 
	{
		if (Row) 
		{
			TotalChance += Row->SpawnChance;
		}
	}
	// 누적 확률로 구현
	const float RandValue = FMath::FRandRange(0.0f, TotalChance);
	float AccumulateChance = 0.0f;
	for (FItemSpawnRow* Row : AllRows) 
	{
		AccumulateChance += Row->SpawnChance;
		if (RandValue <= AccumulateChance) return Row;
	}

	return nullptr;
}

출처: 스파르타코딩 내일배움캠프

0개의 댓글