지정된 범위 내의 랜덤한 좌표에서 아이템 클래스를 랜덤으로 선택하여 스폰한다.
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는 구조체로 되어있다. 스폰 영역을 관리하는 클래스에서 이 테이블을 이용해 아이템을 랜덤하게 스폰한다.
#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;
};

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;
}
출처: 스파르타코딩 내일배움캠프