C++와 Unreal Engine으로 3D 게임 개발 과제 6

1. WHY
- 이번 과제는 언리얼 C++과 엔진 기능 (Actor, Tick, 리플렉션 시스템 등)을 활용해 간단한 퍼즐용 오브젝트들을 준비하는 과제입니다.
- 회전 발판, 이동 플랫폼 등 동적으로 움직이는 오브젝트를 만들고, 이를 C++ 로직과 Tick 함수로 제어하여 언리얼 C++ 개발 흐름을 체득하는 것이 목표입니다.
- 리플렉션 시스템을 통해 변수 (속도, 범위, 각도 등)를 에디터에서 수정할 수 있도록 노출하여 효율적인 게임플레이 튜닝 과정을 경험합니다.
2. HOW
- 1단계 : 퍼즐 오브젝트 설계

이집트 컨셉으로 피라미드 안에 들어가서 보물을 찾는 스토리로 레벨 디자인 했다.

돌아가는 벽과 랜덤 스폰되는 보물, 피라미드까지 가는 길에 움직이는 발판을 구현할 계획이다.
- 2단계 : C++ Actor 클래스 구현


- 3단계 : Tick 함수 활용하여 회전 및 이동 로직 작성
- 4단계 : 리플렉션 시스템 적용
1. 돌아가는 벽
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Rotating.generated.h"
UCLASS()
class NBC_UE_P6_API ARotating : public AActor
{
GENERATED_BODY()
public:
ARotating();
protected:
virtual void BeginPlay() override;
USceneComponent* SceneRoot;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Comp")
UStaticMeshComponent* StaticMeshComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Custom")
float RotationSpeed;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Custom")
bool Reverse;
virtual void Tick(float DeltaTime) override;
};
#include "Rotating.h"
ARotating::ARotating()
{
PrimaryActorTick.bCanEverTick = true;
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
RotationSpeed = 90.f;
Reverse = false;
}
void ARotating::BeginPlay()
{
Super::BeginPlay();
}
void ARotating::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(RotationSpeed))
{
if(Reverse)
{
AddActorLocalRotation(FRotator(0.f, -RotationSpeed * DeltaTime, 0.f));
}
else
{
AddActorLocalRotation(FRotator(0.f, RotationSpeed * DeltaTime, 0.f));
}
}
}
스태틱 매쉬 및 속도, 반전을 리플렉션 시스템으로 수정할 수 있도록 했다.
2. 움직이는 발판
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Moving.generated.h"
UCLASS()
class NBC_UE_P6_API AMoving : public AActor
{
GENERATED_BODY()
public:
AMoving();
protected:
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
USceneComponent* SceneRoot;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Comp")
UStaticMeshComponent* StaticMeshComp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Custom")
float MovingSpeed;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Custom")
bool Reverse;
FVector StartPosition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Custom")
float Range;
};
#include "Moving.h"
AMoving::AMoving()
{
PrimaryActorTick.bCanEverTick = true;
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
MovingSpeed = 200.f;
Reverse = false;
Range = 500.f;
}
void AMoving::BeginPlay()
{
Super::BeginPlay();
StartPosition = GetActorLocation();
}
void AMoving::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(MovingSpeed))
{
if (Reverse)
{
AddActorLocalOffset(FVector(0.f, -MovingSpeed * DeltaTime, 0.f));
}
else
{
AddActorLocalOffset(FVector(0.f, MovingSpeed * DeltaTime, 0.f));
}
if (FVector::Dist(GetActorLocation(), StartPosition) >= Range)
{
Reverse = !Reverse;
}
}
}
스태틱 매쉬 및 범위, 스피드, 반전을 리플렉션 시스템을 사용했다.
BeginPlay()에서 시작위치값을 저장하고 Tick()에서 시작위치와 현재위치 거리 계산을 통해 방향 전환을 할 수 있다.
- 5단계 : 오브젝트 배치 및 테스트

하나하나 스피드랑 각도 조절해서 배치하려고 했는데
그냥 랜덤으로 생성되도록 코드를 수정해야겠다.

움직이는 발판은 각각의 범위와 속도, 방향을 조절해줬다.
- 도전 과제 1번
[TIL] 2025-01-20 | 과제 6에 타이머로 자동문 추가
C++와 Unreal Engine으로 3D 게임 개발 과제 6 [TIL] 2025-01-15 | UE 회전 발판과 움직이는 장애물C++와 Unreal Engine으로 3D 게임 개발 과제 61. WHY이번 과제는 언리얼 C++과 엔진 기능 (Actor, Tick, 리플렉션 시스템
zzageuli.tistory.com
- 도전 과제 2번
1. 동적 스폰
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameModeBase.generated.h"
class ATreasure;
UCLASS()
class NBC_UE_P6_API AMyGameModeBase : public AGameModeBase
{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
UPROPERTY(EditDefaultsOnly, Category="Spawn")
TSubclassOf<ATreasure> TreasureClass;
UPROPERTY(EditAnywhere, Category = "Spawn")
TArray<FVector> SpawnLocations;
void SpawnRandom();
};
게임모드 클래스를 생성하고 동적 스폰할 클래스와 위치를 지정하도록 했다.

#include "MyGameModeBase.h"
#include "Engine/World.h"
#include "Treasure.h"
void AMyGameModeBase::BeginPlay()
{
SpawnRandom();
}
void AMyGameModeBase::SpawnRandom()
{
if (!TreasureClass)
return;
UWorld* World = GetWorld();
if (!World)
return;
int32 idx = FMath::RandRange(0, SpawnLocations.Num() - 1);
FActorSpawnParameters Params;
Params.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
if (!World->SpawnActor<ATreasure>(
TreasureClass,
SpawnLocations[idx],
FRotator::ZeroRotator,
Params
))
{
UE_LOG(LogTemp, Warning, TEXT("Treasure Spawn Failed."));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Treasure Spawn."));
}
}
SpawnActor을 통해 동적 스폰되도록한다.
랜덤으로 TArray값을 가져와서 위치를 정해 스폰되도록 했다.
아직 충돌 관련 로직은 없어서 무조건 생성되도록 AlwaysSpawn으로 지정했다.

생성 여부를 포인터로 반환하기 때문에 생성 성공/실패를 로그로 확인했다.


랜덤한 위치에 잘 생성된 것을 확인할 수 있다.
2. 랜덤 속성 부여

void ARotating::BeginPlay()
{
Super::BeginPlay();
SetActorRotation(FRotator(0.0f, FMath::RandRange(0.0f, 180.0f), 0.0f));
if (FMath::RandRange(0, 1))
{
Reverse = true;
}
else
{
Reverse = false;
}
RotationSpeed = FMath::RandRange(80.f, 100.f);
}
BeginPlay()에서 랜덤으로 값을 지정해준다.
시작 회전값 0~180도,
방향 50% 확률,
회전 스피드 80~100.
3. RESULT
https://github.com/yoonseo4343/NBC_UE_P6
GitHub - yoonseo4343/NBC_UE_P6: 내일배움캠프 언리얼7기 과제 6
내일배움캠프 언리얼7기 과제 6. Contribute to yoonseo4343/NBC_UE_P6 development by creating an account on GitHub.
github.com
4. 참고
Spawning Actors in Unreal Engine | Unreal Engine 5.5 Documentation | Epic Developer Community
Methods of creating new instances of Actors in gameplay code.
dev.epicgames.com

'내배캠Unreal_TIL > UE' 카테고리의 다른 글
| [TIL] 2026-01-16 | UE 아이템 시스템 구현하기 (1), 아이템 인터페이스 (1) | 2026.01.16 |
|---|---|
| [TIL] 2026-01-16 | UE 애니메이션 리타겟팅 (0) | 2026.01.16 |
| [TIL] 2026-01-14 | UE Pawn 클래스로 3D 캐릭터 만들기 (2) | 2026.01.14 |
| [TIL] 2026-01-13 | State Machine 설계를 통한 캐릭터 애니메이션 적용 (0) | 2026.01.13 |
| [TIL] 2026-01-12 | UE C++ 캐릭터 컨트롤 (0) | 2026.01.12 |