2026. 4. 11. 11:01ㆍTIL
이번 과제의 목표는 언리얼 C++의 기본 구조를 익히는 것이었다.
블루프린트로는 쉽게 구현했던 회전 발판과 이동 장애물을 언리얼 C++로 직접 구현해보면서 게임 내 모든 동적 오브젝트의 출발점이 되는 핵심 개념들을 체험해볼 수 있었다.
구현한 기능은 크게 3가지이다.
- RotateItem - 제자리에서 회전하는 액터 (Pitch, Yaw, Roll)
- MoveItem - 왕복 이동하는 액터
- MyGameModeBase - 게임 시작시 두 아이템을 랜덤 위치에 스폰하는 GameModeBase
RotateItem
Tick을 이용해서 매 프레임 회전하도록 했다.
핵심 개념은 [ Tick과 DeltaTime ] 이다.
언리얼 에진은 매 프레임마다 Tick() 함수를 자동으로 호출하는데, 이때 DeltaTime은 이전 프레임과 현재 프레임 사이의 경과 시간이다.
DeltaTime을 사용하지 않으면 고사양 컴퓨터와 저사양 컴퓨터에서의 환경에 따라 게임의 성능이 달라지기 때문이다. 그래서 DeltaTime을 곱해서 하드웨어 성능에 상관없이 항상 일정한 속도를 보장하도록 하는 것이다.
구현 코드
RotateItem.h

RotateItem.cpp


FRotator()는 Pitch, Yaw, Roll 즉, X, Y, Z축의 회전값을 담는 구조체이다.
RotationAxis 에 방향을 저장하고 RotationSpeed * DeltaTime을 곱해 매 프레임 회전하도록 설정했다.
방향은 랜덤값으로 지정해 각 액터마다 다른 방향으로 회전할 수 있도록 설정했다.
MoveItem
이 액터의 핵심은 Tick() 대신 타이머(FTimerHandle)를 사용했다.
Tick은 매 프레임마다 호출되지만 타이머는 내가 지정한 간격으로 호출되므로 성능면에서 Tick보다 유리하다.
타이머 설정

BeginPlay() 안에 설정해서 시작 시에 실행되도록 설정했다.
타이머의 기본 틀은 다음과 같다.
GetWorld()->GetTimerManager().SetTimer(
타이머 변수명
this,
함수
타이머 시간
반복 유무
)
왕복 이동 로직

StartLocation을 GetActorLocation으로 BeginPlay()에 저장해두고, MaxRange를 기준으로 이동 범위를 제한한다.
끝에 닿으면 MoveDirection을 반전시켜 이동 방향을 반대 방향으로 돌려 왕복 운동을 구현했다.
랜덤 속성 부여

BeginPlay()에 랜덤 속성을 부여하는 로직을 작성했다. 저 부분은 에디터 내에서 수정하거나, 생성자 부분에서 수치를 조정할 수 있다.
MyGameModeBase
GameModeBase는 게임의 규칙을 관리하는 클래스이다.
이 클래스에서는 게임 시작 시 MoveItem과 RotateItem을 랜덤한 위치에 스폰하는 역할을 담당한다.
TSubclassOf

오브젝트가 아닌 설계도 자체를 매개변수로 받는다.
SpawnItems

이 클래스의 가장 핵심인 랜덤 스폰 함수이다.
어려웠던 부분
아래 부분의 연보라색 코드가 조금 생소하고 어려운 코드였다.
FActorSpawnParameters Params;
Params.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
이 코드는 스폰하려는 위치에 다른 오브젝트가 있을 때 어떻게 할지 정하는 코드이다.
AdjustIfPossibleButAlwaysSpawn 대신 사용할 수 있는 옵션이 있다.
- AlwaysSpawn - 겹쳐도 그냥 스폰 ( 관통 )
- AdjustIfPossibleButAlwaysSpawn - 겹치면 위치 조금 조정, 조정이 어려우면 그냥 스폰
- DontSpawnIfColliding - 겹치면 스폰 안 함
트러블슈팅 - GameMode 설정을 했는데 설정이 적용되지 않는 문제
문제 상황
GameMode 코드를 완성한 후 컴파일까지 성공했는데 게임을 실행해도 아이템이 전혀 스폰되지 않는 문제가 발생했다.

직접 레벨에 배치한 오브젝트만 보일 뿐, 랜덤 스폰이 전혀 일어나지 않았다.
원인 파악
World Settings에 정상적으로 GameModeBase가 적용이 되어 있었지만, 코드를 작성하며 클래스의 설계를 넣는 칸은 보이지 않았다는 것을 알게 되었고 이 부분을 찾아보려고 했다.
그래서 코드 안에

EditDefalustsOnly 키워드를 확인할 수 있었다.
이는 블루프린트의 기본값 패널에서만 편집 가능하다는 의미이다.
C++클래스 자체는 에디터에서 값을 입력할 창이 없어 클래스를 넣는 칸이 계속 비어있는 상태였고, 이 상태로 코드가 실행되어

이 조건에 막혀 스폰이 일어나지 않았던 것이다.
해결 방법
이 GameModeBase를 블루프린트로 상속해 게임모드오버라이드에 넣었다.

그리고 블루프린트로 상속받은 BP_GameModeBase에 들어가서

기본 세팅을 완료한 후에는 정상적으로 실행되는 모습을 볼 수 있었다.
배운 점
블루프린트는 C++ 클래스를 에디터에서 편집 가능한 형태로 감싸주는 껍데기 역할을 한다.
또한 TSubclassOf처럼 에디터에서 직접 지정해야 하는 변수가 있을 때는 반드시 [ 블루프린트 자식 클래스 ]를 만들어서 사용해야 한다는 것을 알았다.
정리
- Tick과 DeltaTime을 활용하면 하드웨어 성능에 관계없이 일정한 움직임을 구현할 수 있다.
- FTimerHandle을 사용해 Tick보다 효율적으로 반복 로직을 처리할 수 있다.
- UPROPERTY로 변수를 에디터에 노출하면 코드 수정 없이 게임플레이를 빠르게 조정할 수 있다.
- GameMode에서 SpawnActor()로 오브젝트를 동적 생성하면 매번 다른 랜덤 스테이지를 만들 수 있다.
- EditDefaultsOnly 변수를 에디터에서 설정하려면 반드시 블루프린트 자식 클래스를 만들어야 한다.
- TSubclassof<AActor>는 오브젝트가 아닌 클래스 자체를 담는 변수이다. 스폰할 타입을 유연하게 지정할 수 있다.
'TIL' 카테고리의 다른 글
| 게임 멀티플레이 기초 - 서버와 클라이언트 <NetMode, NetRole> (0) | 2026.06.03 |
|---|---|
| < 언리얼 C++ > GameMode (0) | 2026.04.12 |
| [TIL 46일차] <언리얼 C++> 리플렉션 시스템 (2) | 2026.04.09 |
| [TIL 45일차] <언리얼 C++> 액터의 라이프 사이클 (0) | 2026.04.08 |
| [TIL 44일차]언리얼C++ 액터 기본세팅 (1) | 2026.04.07 |