카테고리 없음

Unreal C++ TIL — 3차 분반수업

view29579 2026. 9. 3. 19:59

1. UPrimitiveComponent

언리얼에서 Actor를 구성하는 Component를 공부하면서 UPrimitiveComponent에 대해 배웠다.

UPrimitiveComponent는 렌더링이나 충돌 등의 물리적 기능을 가질 수 있는 Component의 기반 클래스이다.

대표적으로 우리가 자주 사용하는

  • UStaticMeshComponent
  • USkeletalMeshComponent
  • UBoxComponent
  • USphereComponent
  • UCapsuleComponent

등이 UPrimitiveComponent를 상속한다.

대략적인 관계는 다음과 같다.

UObject
 └─ UActorComponent
     └─ USceneComponent
         └─ UPrimitiveComponent
             └─ UStaticMeshComponent
 

즉, UPrimitiveComponent는 단순히 화면에 보이는 Mesh만을 의미하는 것이 아니라 충돌, 렌더링 등 물리적인 표현이 가능한 Component의 기반이라고 이해하면 된다.


2. Scene Component와 Transform

이번 수업에서 중요하게 배운 내용 중 하나가 USceneComponent이다.

USceneComponent는 Transform 정보를 가지고 있는 Component이다.

Transform에는 크게

  • Location → 위치
  • Rotation → 회전
  • Scale → 크기

가 있다.

즉,

USceneComponent
 └─ Location
 └─ Rotation
 └─ Scale
 

을 가지고 있다고 이해했다.

Actor와 Root Component

Actor에 Scene Component가 여러 개 있다면 그중 하나를 Root Component로 지정할 수 있다.

Actor
 └─ RootComponent
      ├─ Mesh
      ├─ Camera
      └─ SpringArm
 

Root Component는 Actor 전체 Transform의 기준이 된다.

쉽게 말하면 Actor의 좌표 기준점이라고 이해했다.

언리얼의 Actor는 Transform을 Actor가 직접 가지고 있는 것처럼 보이지만 실제로는 Root Component가 Actor의 Transform 기준을 담당한다.


3. Component Attachment

Component끼리는 서로 부모-자식 관계를 만들 수 있다.

예를 들어 플레이어에게 카메라를 붙이면

Player
 └─ Capsule (Root)
      └─ Mesh
      └─ SpringArm
           └─ Camera
 

같은 구조를 만들 수 있다.

여기서 중요한 점은

Attachment는 상속이 아니다.

C++의 상속과 Component Attachment는 완전히 다른 개념이다.

C++ 상속

UStaticMeshComponent
        ↓
UPrimitiveComponent
        ↓
USceneComponent
 

이것은 클래스와 클래스 사이의 상속 관계이다.

반면

Root
 └─ Mesh
     └─ Camera
 

는 Component 사이의 부모-자식 관계이다.

따라서

UStaticMeshComponent → UPrimitiveComponent와
Root → Mesh는 완전히 별개의 개념이다.

이 부분은 앞으로 헷갈리지 않도록 구분해야 한다.


4. SetupAttachment와 AttachToComponent

SetupAttachment()

주로 생성자에서 Component의 부모 관계를 설정할 때 사용한다.

예를 들어

 
Camera->SetupAttachment(SpringArm);
 

이라고 하면

SpringArm
   └─ Camera
 

라는 부모-자식 관계를 설정한다.

즉,

Component 구조를 미리 만들어 놓는 느낌

이라고 이해했다.


AttachToComponent()

게임이 실행되는 도중에 Component의 Attachment 관계를 변경해야 한다면 AttachToComponent()를 사용할 수 있다.

예를 들어 플레이어가 무기를 교체한다면

Player
 └─ Hand
      └─ Weapon
 

처럼 새로운 무기를 손에 붙이는 작업에 사용할 수 있다.

정리하면

SetupAttachment()
→ 주로 생성자에서 초기 Attachment 설정

AttachToComponent()
→ 게임 실행 중 Attachment 변경
 

공식 문서에서도 이 용도로 구분해서 설명한다.


5. Movement Component

언리얼에서는 Actor의 이동이나 특정 움직임을 담당하는 Movement Component가 존재한다.

대표적으로

UCharacterMovementComponent

ACharacter의 이동을 담당한다.

걷기, 달리기, 점프, 중력 등의 캐릭터 이동과 관련된 기능을 처리한다.

Character
   ↓
CharacterMovementComponent
   ↓
이동 / 점프 / 중력 / 속도 등
 

URotatingMovementComponent

Actor를 지속적으로 회전시키는 Component이다.

예를 들어

아이템
  ↓
계속 빙글빙글 회전
 

같은 효과를 코드로 직접 매 프레임 회전시키지 않고 Movement Component를 이용해서 구현할 수 있다.

즉 Movement Component는 단순히 "캐릭터만 움직이는 것"이 아니라 Actor의 특정 움직임을 담당하는 Component라고 이해했다.


6. 전방 선언(Forward Declaration)

이번 수업에서 전방 선언도 배웠다.

예를 들어 헤더에서 어떤 클래스의 포인터나 참조만 사용하고 있다면 굳이 해당 클래스의 전체 헤더를 포함하지 않고

 
class UCameraComponent;
 

처럼 이름만 미리 알려줄 수 있다.

이것이 전방 선언이다.

그리고 실제 구현이 필요한 .cpp에서

 
#include "Camera/CameraComponent.h"
 

를 포함한다.

왜 사용하는가?

헤더 파일의 의존성을 줄이기 위해서이다.

특히 .h 파일은 다른 여러 .cpp에서 포함될 수 있다.

따라서 헤더에 불필요한 #include가 많아질수록

A.h
 ↓
B.h
 ↓
C.h
 ↓
D.h
 

처럼 의존성이 계속 늘어날 수 있다.

그래서 가능한 경우

 
// .h
class UCameraComponent;
 
 
// .cpp
#include "Camera/CameraComponent.h"
 

처럼 분리한다.


7. IWYU

언리얼 엔진은 IWYU(Include What You Use) 방식의 헤더 관리를 사용한다.

뜻 그대로

내가 사용하는 것에 필요한 헤더만 Include하자.

라는 개념이다.

따라서 무작정

 
#include "Everything.h"
 

식으로 가져오는 것이 아니라 해당 파일에서 실제로 필요한 헤더만 포함하는 습관이 중요하다.


8. 전방 선언을 사용할 수 있는 경우

여기가 아직 가장 헷갈리는 부분이다.

전방 선언이 가능한 경우

해당 타입의 이름만 알면 되는 경우.

대표적으로 포인터나 레퍼런스.

 
class UCameraComponent;

UCameraComponent* Camera;
 

이런 경우에는 클래스의 내부 구조까지 알 필요가 없기 때문에 전방 선언이 가능하다.


전방 선언이 안 되는 경우

클래스 자체의 실제 내용이 필요한 경우.

예를 들어 상속받는 경우

 
class AMyCharacter : public ACharacter
 

ACharacter가 실제로 어떤 클래스인지 알아야 하기 때문에 해당 헤더가 필요하다.

또한 클래스의 멤버를 직접 사용하거나 객체의 크기 등을 알아야 하는 상황에서는 완전한 정의가 필요하다.

그래서 아직은 다음 정도만 기억하기로 했다.

포인터/레퍼런스로 이름만 필요 → 전방 선언 고려
상속하거나 내부 내용을 실제로 사용 → 헤더 Include 필요


9. Spring Arm

USpringArmComponent도 배웠다.

Spring Arm은 자식 Component를 일정 거리만큼 떨어뜨려 유지하는 Component이다.

대표적인 사용 방법이 3인칭 카메라이다.

Player
 └─ SpringArm
      └─ Camera
 

Spring Arm의 길이를 설정하면 Camera가 플레이어와 일정 거리를 유지한다.

또한 카메라와 플레이어 사이에 벽 등의 충돌이 발생하면 Spring Arm이 길이를 줄여서 카메라가 벽을 뚫고 들어가는 것을 방지할 수 있다.

다만 내가 적어둔

"FPS에서 벽 바라보면 FOV가 늘어나는 것"

은 Spring Arm의 대표적인 기능이라고 보기보다는 다른 카메라 연출과 혼동한 부분이다.

Spring Arm의 핵심은

카메라와 캐릭터 사이의 거리 유지 + 충돌 시 길이 조절

이라고 기억하는 게 정확하다.


10. Camera Component

UCameraComponent는 실제 카메라를 담당한다.

단순히 위치와 회전만 담당하는 것이 아니라 카메라의 여러 설정을 가지고 있다.

대표적으로

  • Location
  • Rotation
  • Field of View(FOV)
  • Projection 관련 설정

등이 있다.

즉,

SpringArm = 카메라를 어떻게 따라오게 할지
CameraComponent = 실제 카메라 자체

라고 생각하면 이해하기 쉬웠다.


11. Raw Pointer

이번 수업에서 가장 머리가 아팠던 부분.

 
UCameraComponent* Camera;
 

여기서 *는 포인터를 의미한다.

포인터는 쉽게 말해서

어떤 객체가 메모리의 어디에 존재하는지를 가리키는 값

이다.

따라서

 
UCameraComponent* Camera;
 

를 보면

Camera
 ↓
UCameraComponent 객체가 있는 메모리 주소
 

라고 이해하면 된다.


T*의 의미

 
T* Pointer;
 

여기서 T는 특정한 하나의 자료형을 의미하는 게 아니라 타입을 대신 나타내는 표현이다.

예를 들어

 
int* Number;
float* Value;
UCameraComponent* Camera;
 

처럼 사용할 수 있다.

즉

T* 
 ↓
"어떤 타입 T를 가리키는 포인터"
 

라고 이해했다.


12. Raw Pointer와 Unreal UObject

일반적인 C++의 Raw Pointer는 언리얼의 UObject 시스템과는 별개이다.

예를 들어 일반 C++ 객체를

 
Weapon* WeaponPtr = new Weapon();
 

처럼 생성했다면 그 객체의 메모리 관리는 일반적인 C++ 방식으로 이루어진다.

반면 언리얼의 UObject는 Unreal의 Garbage Collection 시스템에 의해 관리된다.

그래서 단순히

 
UCameraComponent* Camera;
 

라고 포인터를 가지고 있는 것만으로는 Unreal GC가 해당 객체를 참조하고 있다는 사실을 제대로 추적하지 못할 수 있다.

그래서 Unreal에서는 UObject 계열 객체를 참조할 때 Unreal의 객체 시스템에 맞는 방식을 사용하는 것이 중요하다.


13. TObjectPtr

그래서 나온 것이

 
TObjectPtr<T>
 

이다.

예를 들어

 
UPROPERTY()
TObjectPtr<UCameraComponent> Camera;
 

처럼 사용할 수 있다.

TObjectPtr은 일반적인 C++ 스마트 포인터와 같은 개념이 아니라

Unreal의 UObject 시스템에서 UObject를 참조하기 위한 포인터 타입

이라고 이해했다.

특히 UPROPERTY()와 함께 사용하면 Unreal의 객체 시스템이 해당 참조를 추적할 수 있다.


14. Raw Pointer / TObjectPtr / Smart Pointer 구분

이번 수업에서 이것도 구분해야 했다.

Raw Pointer

 
T*
 

일반적인 C++ 포인터.

객체의 수명을 자동으로 관리해주는 스마트 포인터가 아니다.


TObjectPtr

 
TObjectPtr<UObject>
 

Unreal의 UObject 시스템에서 객체를 참조하기 위한 포인터.


TSharedPtr / TUniquePtr

 
TSharedPtr<T>
TUniquePtr<T>
 

일반적인 C++ 객체의 수명 관리에 사용하는 스마트 포인터.

따라서

Raw Pointer
→ 일반적인 C++ 포인터

TObjectPtr
→ Unreal UObject 참조

TSharedPtr / TUniquePtr
→ 일반 C++ 객체의 스마트 포인터
 

로 구분하면 된다.


15. Unreal에서 포인터를 사용할 때

이번 수업에서 느낀 점은

"C++ 포인터라고 다 같은 포인터가 아니다."

라는 것이다.

특히 Unreal에서는 UObject와 Garbage Collection이 있기 때문에

 
UObject*
 

를 그냥 일반 C++ 포인터처럼 생각하면 안 된다.

그래서 Unreal의 UObject를 멤버 변수로 가지고 있을 때는 상황에 따라

 
UPROPERTY()
TObjectPtr<...>
 

형태를 사용하는 것이 권장된다.


16. Unreal의 기본 방향

회전을 이해하기 전에 언리얼의 좌표축부터 확실하게 외워야 한다.

X = Forward = 앞
Y = Right   = 오른쪽
Z = Up      = 위
 

즉,

 
FVector::ForwardVector
 

는

(1, 0, 0)
 

이고,

 
FVector::RightVector
 

는

(0, 1, 0)
 

이다.

위쪽은

(0, 0, 1)
 

이다.


17. FVector와 FRotator

이번 수업에서 특히 중요하게 구분해야 하는 부분.

FVector

위치 또는 방향을 표현한다.

 
FVector Location;
 

하면 위치를 표현할 수 있고,

 
FVector Direction;
 

하면 방향을 표현할 수도 있다.

즉 FVector는

3차원 공간에서의 X/Y/Z 값

이라고 생각하면 된다.


FRotator

회전 값을 표현한다.

 
FRotator Rotation(0.0f, 90.0f, 0.0f);
 

여기서

Pitch = 상하 회전
Yaw   = 좌우 회전
Roll  = 기울기
 

이다.

따라서

 
FRotator(Pitch, Yaw, Roll)
 

순서로 기억한다.


18. FVector vs FRotator vs 실제 회전

이번 수업에서 가장 중요한 구분 중 하나.

FVector

어디에 있는가?
어느 방향인가?
 

를 표현할 수 있다.

FRotator

얼마나 회전되어 있는가?
 

를 표현한다.

SetActorRotation()

 
SetActorRotation(Rotation);
 

실제로 Actor의 회전을 변경한다.

즉,

FVector
→ 방향

FRotator
→ 회전값

SetActorRotation()
→ 실제 Actor 회전 적용
 

이라고 정리했다.


19. FRotationMatrix

FRotationMatrix는 처음 보면 굉장히 수학적으로 보여서 겁먹기 쉬운데 일단은

특정 회전을 기준으로 현재 앞/오른쪽/위 방향이 어디인지 계산해주는 도구

라고 이해하기로 했다.

예를 들어 Actor가

Yaw = 90°
 

만큼 회전했다면 기존의

X = 앞
Y = 오른쪽
 

기준이 그대로 유지되는 것이 아니라 Actor가 바라보는 방향에 맞게 새로운 방향을 계산해야 한다.

이때 FRotationMatrix 등을 이용해서 회전된 기준에서의 방향을 구할 수 있다.


20. GetUnitAxis()

회전된 기준에서 특정 축의 방향을 가져올 때 사용할 수 있다.

예를 들어

 
FRotationMatrix(Rotation).GetUnitAxis(EAxis::X);
 

라면

현재 Rotation을 기준으로 X축, 즉 Forward 방향이 어디를 가리키는지

구하는 것으로 이해했다.

마찬가지로

 
EAxis::Y
 

는 Right 방향,

 
EAxis::Z
 

는 Up 방향을 구하는 데 사용할 수 있다.

그래서 나중에 캐릭터가 바라보는 방향으로 총알을 발사하거나 이동 방향을 계산할 때 이런 코드가 등장할 수 있다.


21. 캐릭터 회전에서 자주 나오는 개념

캐릭터를 만들다 보면 다음과 같은 코드가 자주 등장한다.

 
FRotator
FRotationMatrix
GetControlRotation()
YawRotation
GetUnitAxis(EAxis::X)
 

처음에는 각각 따로 노는 것처럼 보이지만 결국

"캐릭터가 어디를 바라보고 있는가?"
        ↓
회전값(FRotator)
        ↓
회전된 방향 계산
        ↓
Forward / Right 방향(FVector)
        ↓
이동 / 공격 / 회전 등에 사용
 

이라는 흐름으로 연결된다.


22. 이번 수업에서 헷갈렸던 것

이번 수업에서 특히 헷갈렸던 부분은 포인터와 전방 선언이었다.

포인터

 
UCameraComponent* Camera;
 

에서 *가 단순히 이상한 기호가 아니라

UCameraComponent 객체를 직접 저장하는 것이 아니라 그 객체를 가리키는 주소를 저장한다.

는 의미라는 것을 이해했다.

그리고 Unreal에서는 UObject가 Garbage Collection의 영향을 받기 때문에 일반적인 C++ 포인터와 Unreal의 객체 참조를 구분해야 한다.


전방 선언

전방 선언은

 
class UCameraComponent;
 

처럼 클래스의 존재만 미리 알려주는 것이다.

따라서

 
UCameraComponent* Camera;
 

처럼 포인터를 선언하는 데는 사용할 수 있지만,

클래스의 내부 내용이 필요한 경우에는 실제 헤더가 필요하다.

아직 이 부분은 경험이 부족해서 "언제 전방 선언을 쓰고 언제 Include해야 하는지"를 바로 판단하는 짬은 부족하다.

앞으로 코드를 작성하면서 경험을 쌓아야 할 것 같다.


23. 오늘 배운 내용 전체 연결

오늘 배운 내용을 하나의 구조로 연결해보면 다음과 같다.

Actor
 │
 └─ Root Component
      │
      ├─ Mesh
      │
      ├─ SpringArm
      │    └─ Camera
      │
      └─ 기타 Component
 

Component는 USceneComponent를 기반으로 Transform을 가질 수 있고,

Location
Rotation
Scale
 

을 기준으로 서로 Attachment 관계를 만들 수 있다.

그리고 캐릭터의 움직임은

Movement Component
 

가 담당한다.

카메라를 구성할 때는

SpringArm
   ↓
Camera
 

구조를 사용할 수 있다.

코드에서는

FVector
→ 위치 / 방향

FRotator
→ 회전값

FRotationMatrix
→ 회전 기준의 방향 계산

SetActorRotation()
→ 실제 회전 적용
 

으로 연결된다.


📝 과제 / 복습

이번 수업의 과제 및 복습 내용.

필수 복습

  • UPrimitiveComponent
  • USceneComponent
  • Root Component
  • Component Attachment
  • SetupAttachment()
  • AttachToComponent()
  • Movement Component
  • Forward Declaration
  • IWYU
  • Raw Pointer
  • TObjectPtr
  • TSharedPtr / TUniquePtr
  • FVector
  • FRotator
  • FRotationMatrix

실습

  • Player Mesh 지정
  • Player Camera 세팅

암기

Unreal 기본 방향

X = Forward = 앞
Y = Right   = 오른쪽
Z = Up      = 위
 

그리고

FVector  = 위치 / 방향
FRotator = 회전값
 

이 두 가지는 확실하게 익히기.


💡 오늘의 핵심 한 줄 정리

Actor는 Component들의 집합으로 구성되고, Root Component가 Transform의 기준이 된다. Component 간 Attachment는 C++ 상속과 다른 개념이며, Unreal에서는 UObject의 생명주기와 GC를 고려해 포인터를 사용해야 한다. 또한 FVector는 위치/방향, FRotator는 회전값을 나타내며 회전된 방향을 계산할 때 FRotationMatrix 등을 활용한다.