카테고리 없음

TIL-2nd. 팀프로젝트 DreamProtector (7/21) SpawnVolume / WaveManager 수

view29579 2026. 9. 15. 20:55

1. SpawnVolume과 WaveManager의 역할 확인

현재 프로젝트에서는 ASpawnVolume이 몬스터를 직접 생성하는 것이 아니라, 스폰 영역 안의 랜덤한 위치를 반환하는 역할을 담당하고 있다.

FVector ASpawnVolume::GetRandomSpawnLocation() const
{
    const FVector Origin = SpawnBox->GetComponentLocation();
    const FVector Extent = SpawnBox->GetScaledBoxExtent();

    return Origin + FVector(
        FMath::FRandRange(-Extent.X, Extent.X),
        FMath::FRandRange(-Extent.Y, Extent.Y),
        FMath::FRandRange(-Extent.Z, Extent.Z)
    );
}

실제 몬스터 생성은 AWaveManager::SpawnCurrentWave()에서 담당하고 있다.

const FVector SpawnLocation = SpawnVolume->GetRandomSpawnLocation();

GetWorld()->SpawnActor<AActor>(
    MonsterData.MonsterClass,
    SpawnLocation,
    FRotator::ZeroRotator
);

따라서 스폰되는 몬스터의 개별 이동속도를 설정하는 기능은 SpawnVolume보다 WaveManager에서 처리하는 것이 적절하다.


2. 같은 종류의 몬스터도 개체마다 다른 이동속도 적용

하나의 몬스터 BP를 여러 번 스폰하면서 각각 다른 이동속도를 적용하기로 했다.

예를 들어 같은 BP_Melee를 스폰하더라도:

Melee 1 → 215
Melee 2 → 387
Melee 3 → 452
Melee 4 → 301

처럼 개체마다 다른 이동속도를 가질 수 있도록 구현했다.

기존에는:

GetWorld()->SpawnActor<AActor>(
    MonsterData.MonsterClass,
    SpawnLocation,
    FRotator::ZeroRotator
);

형태로 생성했지만, 생성된 몬스터를 ABaseMonster*로 받아서 속도를 설정하도록 변경했다.

ABaseMonster* SpawnedMonster = GetWorld()->SpawnActor<ABaseMonster>(
    MonsterData.MonsterClass,
    SpawnLocation,
    FRotator::ZeroRotator
);

그리고 ABaseMonster에 이동속도를 변경하는 함수를 추가했다.

BaseMonster.h

void SetMoveSpeed(float NewSpeed);

BaseMonster.cpp

void ABaseMonster::SetMoveSpeed(float NewSpeed)
{
    MoveSpeed = NewSpeed;

    if (GetCharacterMovement())
    {
        GetCharacterMovement()->MaxWalkSpeed = MoveSpeed;
    }
}

이후 WaveManager에서 스폰될 때마다 랜덤한 속도를 적용했다.

if (SpawnedMonster)
{
    SpawnedMonster->SetMoveSpeed(
        FMath::FRandRange(200.0f, 500.0f)
    );
}

이를 통해 같은 몬스터 클래스라도 생성된 각각의 몬스터가 200~500 사이의 랜덤한 이동속도를 가지도록 했다.


3. MoveSpeed와 MaxWalkSpeed의 차이 확인

처음에는 MoveSpeed 값을 랜덤하게 변경했는데도 실제 몬스터의 이동속도가 변하지 않는 문제가 발생했다.

ABaseMonster의 생성자에서는 기본 이동속도를 다음과 같이 설정하고 있었다.

MoveSpeed = 300.0f;

또한 BeginPlay()에서 MoveSpeed를 실제 캐릭터 이동속도에 적용하고 있었다.

void ABaseMonster::BeginPlay()
{
    Super::BeginPlay();

    CurrentHealth = MaxHealth;

    if (GetCharacterMovement())
    {
        GetCharacterMovement()->MaxWalkSpeed = MoveSpeed;
    }
}

문제는 실행 순서였다.

몬스터 생성
↓
생성자에서 MoveSpeed = 300
↓
BeginPlay()
↓
MaxWalkSpeed = 300
↓
WaveManager에서 MoveSpeed를 랜덤값으로 변경

따라서 WaveManager에서 MoveSpeed만 변경하면 CharacterMovementComponent의 MaxWalkSpeed는 이미 300으로 설정된 상태였다.

이 문제를 해결하기 위해 SetMoveSpeed()에서 스탯 변수와 실제 이동속도를 동시에 변경하도록 했다.

void ABaseMonster::SetMoveSpeed(float NewSpeed)
{
    MoveSpeed = NewSpeed;

    if (GetCharacterMovement())
    {
        GetCharacterMovement()->MaxWalkSpeed = MoveSpeed;
    }
}

이를 통해 스폰 이후 변경된 랜덤 속도가 실제 이동속도에도 적용되도록 했다.


4. 랜덤 이동속도 적용 여부 디버깅

실제로 랜덤값이 적용되고 있는지 확인하기 위해 로그를 추가했다.

UE_LOG(
    LogTemp,
    Warning,
    TEXT("AFTER RANDOM Speed: %f"),
    SpawnedMonster->MoveSpeed
);

처음에는 300만 출력되는 것으로 확인하여 원인을 추적했지만, 이후 확인 결과 실제로는 랜덤한 값이 정상적으로 들어가고 있는 것을 확인했다.

따라서 문제의 원인은 랜덤값 생성이나 WaveManager의 스폰 로직이 아니라, MoveSpeed가 실제 이동 시스템의 MaxWalkSpeed에 적용되는 시점에 있었다.


5. WaveManager의 웨이브 스폰 구조 확인

현재 WaveManager는 DataTable의 FWaveData를 이용해 웨이브 정보를 관리한다.

FWaveMonsterData에서는:

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> MonsterClass;

UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 SpawnCount = 0;

를 통해 몬스터 클래스와 스폰 개수를 관리한다.

그리고 SpawnCurrentWave()에서:

for (const FWaveMonsterData& MonsterData : WaveData->Monsters)

를 통해 웨이브에 등록된 몬스터 종류를 확인하고,

for (int32 i = 0; i < MonsterData.SpawnCount; ++i)

를 통해 설정된 개수만큼 반복해서 스폰한다.

또한:

ASpawnVolume* SpawnVolume =
    SpawnVolumes[i % WaveData->SpawnVolumeCount];

를 사용해 여러 SpawnVolume에 몬스터를 분배하고 있다.

이를 통해 현재 프로젝트의 DataTable → WaveManager → SpawnVolume → Monster 구조를 다시 확인할 수 있었다.


Git 작업

6. GitHub Push 시 대용량 파일 오류 발생

GitHub에 Push하는 과정에서 다음 오류가 발생했다.

The push operation includes a file which exceeds GitHub's file size restriction of 100MB.

원인을 확인한 결과, 배포받은 약 8GB 크기의 압축파일을 프로젝트 폴더에 삭제하지 않은 상태에서 Git 작업을 진행한 것이 원인이었다.

GitHub에는 일반 Git 저장소에서 개별 파일을 업로드할 때 100MB 제한이 있기 때문에 프로젝트에 불필요한 대용량 압축파일이 포함되지 않도록 주의해야 한다.


7. Git lock 파일 오류 확인

이후 다음 오류도 발생했다.

A lock file already exists in the repository, which blocks this operation from completing.

Git 작업 중 생성된 .git/index.lock 파일을 확인하고 삭제했다.

이 과정에서 대용량 파일 문제와 Git lock 문제는 서로 다른 문제라는 것도 확인했다.


오늘 배운 점

  • SpawnVolume은 스폰 위치를 제공하고, 실제 몬스터 생성은 WaveManager에서 담당한다.
  • 같은 몬스터 BP를 사용해도 생성된 개체마다 다른 스탯을 적용할 수 있다.
  • MoveSpeed 같은 스탯 변수와 실제 이동에 사용되는 MaxWalkSpeed는 별개의 값이다.
  • BeginPlay()에서 MoveSpeed를 MaxWalkSpeed에 적용한 뒤 MoveSpeed만 변경하면 실제 이동속도에는 반영되지 않을 수 있다.
  • 따라서 스폰 이후 이동속도를 변경할 때는 MoveSpeed와 MaxWalkSpeed를 함께 변경해야 한다.
  • SetMoveSpeed() 같은 함수를 만들어 스탯 변경과 실제 시스템 적용을 한 번에 처리하는 방법을 익혔다.
  • DataTable을 이용한 웨이브 구조에서 몬스터 종류와 스폰 개수를 관리하고, 실제 개체별 추가 설정은 SpawnCurrentWave()에서 처리할 수 있다.
  • GitHub에 Push하기 전에 프로젝트 폴더에 대용량 압축파일이나 불필요한 파일이 포함되어 있지 않은지 확인해야 한다.
  • Git의 index.lock은 Git 작업을 막을 수 있는 잠금 파일이며, Git 프로세스가 종료된 것을 확인한 후 제거할 수 있다.