Actor 클래스 코드 구조
Actor 클래스 코드 구조
- 언리얼 엔진은 C++ 클래스를 만들 때 헤더 (.h)와 구현 (.cpp) 두 파일을 자동으로 생성합니다.
- 그중에서 자동으로 생성되는 (.h) 를 알아보도록 하겠습니다.
.h 코드
```cpp
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"
```
- #pragma once
- 동일 헤더가 프로젝트 곳곳에서 여러 번 포함되더라도 컴파일러가 한 번만 처리하도록 합니다.
- 과거에는 #ifndef / #define / #endif 패턴보다 간결하고 안전해서, 언리얼을 포함한 모던 C++에서 사실상 표준으로 사용합니다.
- #include "CoreMinimal.h"
- 언리얼에서 가장 기본이 되는 헤더로, 엔진 전역 타입, 매크로, 함수 등을 가져옵니다.
- 필수 헤더이므로 거의 모든 .h 파일의 맨 위에 포함됩니다.
- #include "GameFramework/Actor.h"
- AActor 클래스 선언을 사용 가능하게 합니다.
Item.h 의 경우/ ("Item" 부분은 내가 생성한 C++ 클래스의 이름)
- #include "Item.generated.h"
- 언리얼 헤더툴 (UHT)이 자동 생성하는 코드를 포함하는 것으로, 항상 헤더의 마지막 줄에 위치해야 해요. 다른 곳으로 이동하면 컴파일 오류가 납니다.
.h 파일 클래스 선언부 코드
UCLASS()
class PROJECTNAME_API AItem : public AActor
{
GENERATED_BODY()
public:
// 생성자
AItem();
protected:
// 액터가 월드에 스폰 (배치된) 직후 한 번만 호출
virtual void BeginPlay() override;
// 매 프레임마다 호출
virtual void Tick(float DeltaTime) override;
};
- UCLASS()
- 이 클래스를 리플렉션 시스템에 등록하는 매크로입니다.
- 언리얼 에디터에서 이 클래스를 블루프린트로 확장할 수 있게 하고, 에디터의 여러 기능과 연동하도록 합니다.
- class PROJECTNAME_API AItem : public AActor
- AActor를 상속받아 AItem 클래스를 정의한다는 의미입니다.
- AItem
- 언리얼 엔진 C++에서는 클래스 이름에 접두사를 붙이는 접두사 규칙이 있습니다.
- A (Actor 계열), U (Object 계열), F (구조체), T (템플릿), E (열거형) 등
- 우리는 Item 이라는 이름으로 클래스를 만들었지만, 실제 클래스 이름은 Actor 클래스를 상속받았으므로, AItem 으로 생성됩니다.
- 언리얼 엔진 코드 컨벤션 문서
- 언리얼 엔진 C++에서는 클래스 이름에 접두사를 붙이는 접두사 규칙이 있습니다.
- PROJECTNAME_API 는 이 클래스를 모듈 (현재 모듈은 ProjectName) 외부에서도 클래스를 사용할 수 있게 하기 위한 매크로입니다.
- GENERATED_BODY()
- UCLASS()와 짝을 이루어, 언리얼 헤더툴 (UHT)이 자동 생성한 코드를 삽입해 주는 매크로입니다.
- Actor 라이프사이클 관련 함수
- AItem() - C++ 생성자
- Actor 객체가 메모리에 생성될 때 한 번 호출됩니다.
- BeginPlay()
- 액터가 월드에 완전히 배치되고, 게임 플레이 (Play)가 시작할 때 한 번 호출됩니다.
- Tick(float DeltaTime)
- 매 프레임마다 자동으로 호출됩니다.
- AItem() - C++ 생성자
.cpp 파일 클래스 구현부 코드
#include "Item.h" // 항상 첫 줄에! 자신과 짝이 되는 헤더를 가장 먼저 include해야 함.
// 생성자 구현부
AItem::AItem()
{
PrimaryActorTick.bCanEverTick = true; // (이후 강의에서 배우는 내용)
}
// BeginPlay() 구현부
void AItem::BeginPlay()
{
// 부모 클래스(AActor)의 BeginPlay()를 먼저 호출
Super::BeginPlay();
}
// Tick() 구현부
void AItem::Tick(float DeltaTime)
{
// 부모 클래스(AActor)의 Tick() 먼저 호출
Super::Tick(DeltaTime);
}
- #include "Item.h"
- 자동 생성 매크로가 올바르게 확장되려면 짝이 되는 헤더를 가장 먼저 include해야 합니다.
- .cpp 파일에서는 가장 첫 줄에 짝이 되는 헤더 파일을 포함해야 합니다. 그렇지 않으면 예기치 않은 링커 오류가 발생할 수 있습니다.
- Actor 라이프사이클 관련 함수 구현부
- 생성자 AItem(), BeginPlay(), Tick(float DeltaTime)
- 부모 구현을 호출해 엔진 내부 동작을 보장해준 후, 여기서 구현 로직을 자유롭게 추가할 수 있습니다.
- 생성자 AItem(), BeginPlay(), Tick(float DeltaTime)
Actor 클래스에 컴포넌트 멤버 변수 추가하고 초기화하기
컴포넌트 (Component)
- 컴포넌트는 언리얼 엔진에서 Actor가 어떤 역할을 하거나 특정 속성을 갖도록 만들어주는 부품 (파츠) 개념입니다.
- 하나의 Actor가 여러 종류의 컴포넌트를 조합하여 다양한 기능을 구현할 수 있습니다.
- 예를 들어, Static Mesh Component + Audio Component + Collision Component를 모두 사용해, 충돌 시 소리가 나는 아이템을 만들 수도 있습니다.
- 한 Actor 내부에 여러 종류의 컴포넌트를 계층적으로 조합해 복합 기능을 구현합니다.
루트 컴포넌트와 Scene Component
- 모든 Actor는 루트 컴포넌트 (Root Component)를 가져야 합니다.
- 루트 컴포넌트는 액터의 트랜스폼 (위치, 회전, 크기)을 정의하는 최상위 컴포넌트이며, 모든 하위 컴포넌트가 이를 기준으로 트랜스폼이 계산됩니다.
- 보통 Scene Component를 루트 컴포넌트로 설정하여 액터의 트랜스폼을 관리하고, 그 아래에 다양한 컴포넌트를 계층적으로 붙입니다.
- Scene Component는 모든 트랜스폼 속성만 가지는 비시각적인 컴포넌트입니다.
- Scene Component는 직접적인 시각적 출력을 가지지 않지만, 다른 하위 컴포넌트들을 관리하는 기준점 역할을 할 수 있습니다.
Static Mesh Component
- 애니메이션이나 스켈레탈 본 없이 (즉, 움직임이 없고 단순 이동·회전만 하는) 정적 (Static) 3D 모델을 그리는 컴포넌트입니다.
- 환경 오브젝트 (건물, 바위), 아이템, 단순 기믹 오브젝트 등 “움직임이 단순하거나 없는” 오브젝트에 주로 적합합니다.
- 3D 모델을 표현하고, 물리 충돌과 관련된 기능도 제공합니다.
Static Mesh Component와 Scene Component 연결하기
- Item.h 파일에서 다음과 같이 포인터 멤버 변수를 추가합니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"
UCLASS()
class PROJECTNAME_API AItem : public AActor
{
GENERATED_BODY()
public:
AItem();
protected:
// 루트 컴포넌트를 나타내는 Scene Component 포인터
USceneComponent* SceneRoot;
// Static Mesh Component 포인터
UStaticMeshComponent* StaticMeshComp;
};
- Item.cpp
#include "Item.h"
AItem::AItem()
{
// Scene Component를 생성하고 루트로 설정
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
// Static Mesh Component를 생성하고 Scene Component에 Attach
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
}
- 필요한 함수 (BeginPlay, Tick)가 없거나 미사용 상태라면, 헤더와 .cpp 파일에서 제거해도 무방합니다. 필요할 때 다시 추가할 수 있습니다.
- USceneComponent* SceneRoot
- SceneRoot는 눈에 보이지 않는 논리적 컴포넌트로, 실제 3D 모델이나 시각적 요소를 가지지 않습니다.
- UStaticMeshComponent* StaticMeshComp
- StaticMeshComp는 Static Mesh (정적 메시)를 렌더링하는 역할을 합니다.
- CreateDefaultSubobject<T>(TEXT(""))
- Unreal Engine에서 컴포넌트를 생성하고 초기화할 때 사용하는 함수입니다.
- 템플릿 타입 <T>로 생성할 컴포넌트의 유형을 지정합니다.
- "StaticMesh", "SceneRoot"는 각 컴포넌트의 식별 이름이 됩니다.
- TEXT() 매크로는 문자열을 유니코드로 처리하기 위한 것으로, 언리얼 엔진 코드 표준에서 권장합니다.
- SetRootComponent(SceneRoot)
- 루트 컴포넌트를 SceneRoot로 설정합니다.
- 루트 컴포넌트는 액터의 기본 위치, 회전, 크기를 정의하며, 다른 모든 하위 컴포넌트가 이를 기준으로 동작합니다.
- SetupAttachment(SceneRoot)
- StaticMeshComp를 SceneRoot에 부착 (Attach)합니다.
- StaticMeshComp는 SceneRoot의 하위 컴포넌트로 동작하며, SceneRoot의 트랜스폼 변화에 따라 움직입니다.
언리얼 에디터에서 컴포넌트 확인하기
- 언리얼 에디터 Content Browser에서 C++ Classes → SpartaProject → Public 폴더 안에 Item 클래스를 레벨 뷰포트에 다시 배치해봅시다.
- 현재 월드 상에서는 Item이 보이지는 않지만 Outliner에서 Item 액터가 생성되어 있는 것을 확인할 수 있습니다.
- Details 창에 보면 아래와 같이 Component가 보이는 것을 볼 수 있습니다.
- 에디터 상에 Component들이 노출되도록 리플렉션 시스템에 등록하지 않았기 때문에 보이지 않는 것을 확인할 수 있습니다.
- 그러나 RootComponent는 설정하지 않아도 기본적으로 리플렉션 시스템에 등록되어 Details 창에서 볼 수가 있습니다.
메시 및 머티리얼 할당하고 Actor 배치하기
Static Mesh Component의 주요 구성 요소
- Static Mesh (스태틱 메시)
- 3D 모델 (정적 메쉬 데이터)를 정의합니다. 이는 모델 파일이며, 언리얼 엔진에서는 .uasset 파일로 관리됩니다.
- Material (머티리얼)
- 메쉬의 표면 시각적 속성을 정의합니다. 색상, 질감, 반사율, 투명도 등의 속성을 설정할 수 있습니다.
- Static Mesh에는 여러 개의 머티리얼 슬롯이 있을 수 있습니다. 슬롯은 메쉬의 특정 부분에 다른 머티리얼을 적용할 수 있도록 합니다.
Static Mesh 및 Material 설정
#include "Item.h"
AItem::AItem()
{
// Scene Component를 생성하고 루트로 설정
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
// Static Mesh Component를 생성하고 Scene Component에 Attach
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
// Static Mesh를 코드에서 설정
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"));
if (MeshAsset.Succeeded())
{
StaticMeshComp->SetStaticMesh(MeshAsset.Object);
}
// Material을 코드에서 설정
static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("/Game/Resources/Materials/M_Metal_Gold.M_Metal_Gold"));
if (MaterialAsset.Succeeded())
{
StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
}
}
- ConstructorHelpers::FObjectFinder<T>
- Unreal Engine에서 특정 리소스를 경로 기반으로 로드하는 클래스입니다.
- TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"):
- 리소스의 경로를 나타냅니다. 리소스의 경로를 해당하는 에셋을 우클릭하고 Copy Reference를 해서 붙여넣기를 합니다.
- 단, /Game 여기서부터만 입력하면 되고, 앞에 경로는 삭제를 해줍니다.
- /Game은 Unreal Engine에서 프로젝트의 Content 폴더를 나타냅니다.
- .Succeeded()
- 지정된 경로에서 리소스를 성공적으로 찾았는지 확인합니다.
- 경로가 잘못되었거나 리소스 파일이 누락된 경우 실패하며, 이후 설정 함수가 호출되지 않습니다.
- SetStaticMesh(), SetMaterial()
- 성공적으로 로드된 Static Mesh를 StaticMeshComp에 설정합니다.
- 로드된 Material을 StaticMeshComp의 특정 머티리얼 슬롯에 적용합니다. 여기서는 첫 번째 머티리얼 슬롯 (Index 0)에 Material이 설정됩니다.
언리얼 에디터에서 Actor 배치 및 테스트
- 다시Item 클래스를 레벨 뷰포트에 배치해봅시다.
- 현재 Details 창에서는 Component가 보이지 않고 에디터 상에서 할당은 불가능하지만, 뷰포트 상에서 메시랑 머티리얼이 잘 할당되었다는 것을 확인할 수가 있습니다.
추천
[페이지] Unreal Engine | 언리얼 엔진
Epic C++ Coding Standard for Unreal Engine | Unreal Engine 5.7 Documentation | Epic Developer Community
Write maintainable code by adhering to established standards and best practices.
dev.epicgames.com
