[TIL] 2026-01-19 | UE 아이템 시스템 구현하기 (2), Timer, SpawnActor

2026. 1. 19. 18:05·내배캠Unreal_TIL/UE
C++와 Unreal Engine으로 3D 게임 개발 3-2,3

 

지난 글에 이어집니다,,,

 

[TIL] 2025-01-16 | UE 아이템 시스템 구현하기 (1), 아이템 인터페이

C++와 Unreal Engine으로 3D 게임 개발 3-1,21. WHAT인터페이스란?반드시 구현해야 할 함수 목록만을 미리 정의해두고 실제 함수의 동작은 해당 클래스를 상속받거나 구현하는 쪽에서 자유롭게 작성할

zzageuli.tistory.com

 

1. WHAT

- Timer

 

타이머(Timer) 는 Tick()을 사용하지 않고도 일정 시간 후 함수 실행 또는 주기적인 로직 처리를 가능하게 해주는 시스템

TimerManager

  • 타이머를 실제로 관리하는 객체
  • UWorld가 소유
GetWorld()->GetTimerManager()

타이머 매니절~

FTimerHandle

  • 타이머를 식별하기 위한 핸들
  • 중지, 재시작, 상태 확인 시 사용
FTimerHandle ExplosionTimerHandle;

 

SetTimer() 기본 문법

GetWorld()->GetTimerManager().SetTimer(
	TimerHandle, // FTimerHandle
	Object, // this 포인터
	&Class::Function, // 호출할 함수
	Delay, // 지연 시간 (초)
	bLoop // 반복 여부
);

 

 

- SpawnActor

게임 실행 중 월드(World)에 새로운 액터를 생성하는 함수

기본 구조

GetWorld()->SpawnActor<AActor>(
	ItemClass, // 생성할 액터 클래스
	SpawnLocation, // 위치 (FVector)
	SpawnRotation // 회전 (FRotator)
);

 

 

2. HOW

- 지뢰 아이템 충돌 처리 (MineItem)

 

1. 플레이어가 지뢰와 충돌

2. 타이머 실행

3. 5초 후 폭발 범위 내 액터를 확인

4. 범위 내 액터에게 데미지를 입힘

5. 폭발 후 지뢰는 사라짐

 

먼저 폭발 범위를 지정하기 위해 충돌 스피어 컴포넌트를 추가해주고 반경을 저장할 변수를 선언한다.

public:

	USphereComponent* ExplosionCollision;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
	float ExplosionRadius;
AMineItem::AMineItem()
{
    ItemType = "Mine";
    ExplosionDelay = 5.0f;
    ExplosionDamage = 30;
    ExplosionRadius = 300.0f;

    ExplosionCollision = CreateDefaultSubobject<USphereComponent>(TEXT("ExplosionCollision"));
    ExplosionCollision->InitSphereRadius(ExplosionRadius);
    ExplosionCollision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
    ExplosionCollision->SetupAttachment(Scene);
}

 

이제 타이머를 통해 5초 뒤 폭발을 제어한다.

public:
    FTimerHandle ExplosionTimerHandle;
	virtual void ActivateItem(AActor* Activator) override;
	void Explode();
void AMineItem::ActivateItem(AActor* Activator)
{
	// 타이머 설정, 폭발 함수 스케줄링
    GetWorld()->GetTimerManager().SetTimer(
        ExplosionTimerHandle,
        this,
        &AMineItem::Explode,
        ExplosionDelay,
        false
    );
}

void AMineItem::Explode()
{
	// 실제 폭발 처리 담당
    TArray<AActor*> OverlappingActors;
    ExplosionCollision->GetOverlappingActors(OverlappingActors);

    for (AActor* Actor : OverlappingActors)
    {
        if (Actor && Actor->ActorHasTag("Player"))
        {
            // 데미지
        }
    }
    DestroyItem();
}

 

- 아이템 랜덤 스폰

박스 컴포넌트를 생성하여 박스 컴포넌트 영역 안에서 위치를 랜덤으로 지정해 아이템을 스폰한다.

 

SpawnVolume 클래스

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SpawnVolume.generated.h"

class UBoxComponent;
UCLASS()
class SPARTAPJ_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);

};
#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
{
	// 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
	);
}

 

- 데이터 테이블을 사용하여 스폰 로직 구현

None으로 클래스를 생성해서 구조체를 만든다.

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

데이터 테이블을 생성하고 값을 입력한다.

 

데이터 테이블로 쉽게 확률을 제어할 수 있다!

 

SpawnVolume 클래스를 수정하여 확률 로직을 구현한다.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemSpawnRow.h"       // 우리가 정의한 구조체
#include "SpawnVolume.generated.h"

class UBoxComponent;

UCLASS()
class SPARTAPROJECT_API ASpawnVolume : public AActor
{
    GENERATED_BODY()

public:
    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;

    FVector GetRandomPointInVolume() const;
    FItemSpawnRow* GetRandomItem() const;
    void SpawnItem(TSubclassOf<AActor> ItemClass);
};
#include "SpawnVolume.h"
#include "Components/BoxComponent.h"
#include "Engine/World.h"

ASpawnVolume::ASpawnVolume()
{
    PrimaryActorTick.bCanEverTick = false;

    Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
    SetRootComponent(Scene);
    
    SpawningBox = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawningBox"));
    SpawningBox->SetupAttachment(Scene);
    
    ItemDataTable = nullptr;
}

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

FVector ASpawnVolume::GetRandomPointInVolume() const
{
    const FVector BoxExtent = SpawningBox->GetScaledBoxExtent();
    const 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)
    );
}

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;

    // 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에서 for loop 돌려 SpawnRandomItem 함수 호출로 테스트!

랜덤으로 잘 생성된다 bb

'내배캠Unreal_TIL > UE' 카테고리의 다른 글

[TIL] 2026-01-21 | UE 게임 기초 시스템 구현  (0) 2026.01.21
[TIL] 2026-01-20 | 과제 6에 타이머로 자동문 추가  (2) 2026.01.20
[TIL] 2026-01-16 | UE 아이템 시스템 구현하기 (1), 아이템 인터페이스  (1) 2026.01.16
[TIL] 2026-01-16 | UE 애니메이션 리타겟팅  (0) 2026.01.16
[TIL] 2026-01-15 | UE 회전 발판과 움직이는 장애물  (0) 2026.01.15
'내배캠Unreal_TIL/UE' 카테고리의 다른 글
  • [TIL] 2026-01-21 | UE 게임 기초 시스템 구현
  • [TIL] 2026-01-20 | 과제 6에 타이머로 자동문 추가
  • [TIL] 2026-01-16 | UE 아이템 시스템 구현하기 (1), 아이템 인터페이스
  • [TIL] 2026-01-16 | UE 애니메이션 리타겟팅
윤윤씨
윤윤씨
🎮 내일배움캠프 Unreal 7기
  • 윤윤씨
    컴퓨터온열맛사지
    윤윤씨
  • 전체
    오늘
    어제
    • 분류 전체보기 (62)
      • 내배캠Unreal_TIL (62)
        • C++ (23)
        • UE (31)
        • 팀프로젝트 (7)
      • etc (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
    • Solved.ac
    • YouTube
  • 태그

    코드카타
    디자인패턴
    gas
    ta
    STL
    머티리얼
    언리얼과제
    프로그래머스
    오토마타
    스테이트머신
    챌린지
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.6
윤윤씨
[TIL] 2026-01-19 | UE 아이템 시스템 구현하기 (2), Timer, SpawnActor
상단으로

티스토리툴바