Actor클래스 생성 및 삭제, 컴포넌트 추가하기

주상돈·2025년 1월 21일

TIL

목록 보기
18/53

언리얼 엔진에서 Actor vs. Object 이해하기


언리얼 엔진을 학습하다 보면, 가장 먼저 접하게 되는 개념 중 하나가 UObjectAActor이다. 둘 다 엔진에서 매우 중요한 클래스 계층을 이루고 있으므로, 차이점을 확실히 알아두면 좋다.

1️⃣ Object (UObject)의 개념

  • UObject는 언리얼 엔진에서 모든 클래스의 최상위 부모이다.
  • 스스로 월드(레벨)에 배치될 수 없습니다. 주로 데이터나 로직만 담당한다.
    • 예: 플레이어 능력치(HP, 경험치), AI 정보, 게임 설정 값, 임시 계산 로직 등
  • 화면에 보이지 않는 추상적인 부분을 처리하는 역할이라고 보면 된다.

2️⃣ Actor (AActor)의 개념

  • AActorUObject를 확장(상속)한 클래스로, 월드에 배치(Spawn) 할 수 있다.
  • 위치 (트랜스폼), 회전, 크기 등 공간적 정보를 가지고 있고, 여러 컴포넌트 (메시, 파티클, 사운드 등)를 추가로 붙일 수 있다.
  • 실제 게임 세계에서 보이고 상호작용하는 캐릭터, 적 몬스터, 무기, 조명, 파티클 효과 등은 주로 AActor를 기반으로 제작한다.

새로운 Actor 클래스 생성하기


이제 Actor를 상속받은 새로운 C++ 클래스를 만들어, 월드에 배치해 보는 예시를 살펴보자.

1️⃣ Actor 클래스 생성

  • 언리얼 에디터 상단 메뉴에서 “Tools” → “New C++ Class”를 선택한다.
    • 팝업 창이 뜨면 Common Classes 탭에서 Actor를 선택한 뒤 Next 버튼을 누른다.
    • 만약 Actor가 안 보인다면, “All Classes” 탭에서 직접 검색해보자.
  • 클래스 이름Item으로 하고, Class Type (폴더 위치)은 기본값인 Public으로 둔 뒤 “Create Class”를 누른다.
  • 잠시 후 자동 빌드가 진행된다. 빌드가 완료되면 Visual Studio에서 프로젝트가 변경되었다는 메시지가 뜰 수 있는데, Reload All을 선택한다.

2️⃣ 생성된 클래스 확인

  • Visual Studio에서 Solution Explorer를 보면, Source/SpartaProjectPublic 폴더 아래에 Item.h, Private 폴더 아래에 Item.cpp 파일이 생성된 것을 확인할 수 있다.
  • 동시에 Public 폴더와 Private 폴더도 생성된 것을 확인할 수 있다.
  • Class Type 옵션에 대한 추가 설명
    • Public으로 생성할 경우:
      • .h가 Public 폴더에, .cpp가 Private 폴더에 생긴다.
      • 프로젝트의 다른 모듈에서 쉽게 #include할 수 있어 편리하다.
    • Private으로 생성할 경우:
      • .h.cpp 모두 Private 폴더에 저장된다.
      • 해당 모듈에서만 접근 가능하게 되며 특정 로직이나 구현을 캡슐화해서 외부에 노출하고 싶지 않을 때 사용한다.
    • 규모가 커질수록 이러한 구조를 잘 활용하여 모듈 간 경계를 명확하게 하고 코드 접근 범위를 정리하는 것이 중요하다. 그러나 학습 단계에서는 보통 Public 방식을 추천한다.
  • 언리얼 에디터 Content BrowserC++ Classes → SpartaProject 폴더 아래에도 Item 클래스가 나타나면 정상이다.

3️⃣ Actor 배치

  • Public 폴더 안에 Item 클래스를 레벨 뷰포트드래그 & 드롭하여 배치하자.
  • 월드에 배치했는데, 아직 3D 모델이 눈에 보이지는 않는다. 왜 그럴까요? 이것은 아래에서 알아보도록 하자.
  • 그러나 Outliner 창을 보면 Item 액터를 확인할 수 있습니다.

Actor 클래스 삭제하기


작업 중에 사용하지 않게 된 클래스는 삭제해야 할 때가 있다. 단순히 언리얼 에디터 Content Browser에서 Delete만 하는 것으로는 완전히 제거되지 않으니, 아래 절차를 따라야 안전하게 삭제할 수 있다.

1️⃣ Visual Studio 솔루션에서 파일 제거

  • 언리얼 에디터를 종료한다.
  • Visual Studio를 열어, Solution Explorer에서 삭제할 클래스를 찾는다.
  • .h.cpp 파일을 각각 우클릭 → Remove를 선택하자.
  • Visual Studio 상단 메뉴에서 File → Save All로 저장하자.
  • 이 작업은 솔루션 (프로젝트 구성 정보)에서만 제거하는 것이므로, 실제 디스크의 파일은 아직 남아 있다.

2️⃣ 프로젝트 폴더에서 물리적 파일 삭제

  • 윈도우 탐색기로 Source/SpartaProject 아래의 Public 혹은 Private 폴더로 이동하자.
  • 방금 Remove했던 .h, .cpp 파일을 직접 삭제하자.
  • 그리고 Visual Studio로 돌아가서 빌드를 한 후, 다시 언리얼 에디터를 실행하면, 해당 클래스가 Content Browser에서도 사라진 것을 확인할 수 있다.
  • 만약 이 과정을 무시하고 언리얼 에디터에서만 Delete하거나, 솔루션에서 Remove하지 않고 폴더에서만 파일을 삭제하면 빌드 에러 혹은 클래스 미삭제 같은 문제가 생길 수 있다.
  • 항상 에디터 종료 → 솔루션 Remove → 물리 파일 삭제 → (Visual Studio 빌드) → 에디터 재실행 순서를 지키면 안전하다.

Actor 클래스의 코드 구조 이해하기


언리얼 엔진에서 C++ 클래스를 새로 만들면, 일반적으로 헤더(.h) 파일과 구현(.cpp) 파일이 한 쌍으로 자동 생성된다. 여기서는 지난 강의에서 만들었던 Item 클래스를 예로 들어, 헤더 파일의 구조와 각 부분에서 어떤 역할을 하는지를 자세히 살펴보자.

1️⃣ .h 파일 상단 코드

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"
  • #pragma once
    • 이 헤더 파일이 여러 번 포함되더라도, 컴파일 시 단 한 번만 처리하도록 해주는 지시어이다.
    • 과거에는 #ifndef ~ #define ~ #endif 방식을 사용했지만, 대부분의 최신 C++ 프로젝트에서 #pragma once를 권장한다.
  • #include "CoreMinimal.h"
    • 언리얼 엔진에서 자주 사용하는 기본 타입(FString, TArray 등)과 매크로(UE_LOG 등), 각종 유틸 함수들이 정의되어 있다.
  • #include "GameFramework/Actor.h"
    • AActor를 상속받기 위해 필요한 헤더 파일이다.
  • #include "Item.generated.h"
    • 언리얼 엔진의 리플렉션 시스템에서 필요한 코드를 자동 생성하기 위한 매크로이다. (이후 강의에서 배우는 내용)

2️⃣ .h 파일 클래스 선언부 코드

UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
		GENERATED_BODY()
	
public:
		// 생성자
		AItem();

protected:
	  // 액터가 월드에 '배치된 직후' 한 번만 호출
		virtual void BeginPlay() override;
		// 매 프레임마다 자동으로 호출
		virtual void Tick(float DeltaTime) override;
};
  • UCLASS()
    • 이 클래스를 언리얼 엔진의 리플렉션 시스템에서 인식하도록 하는 매크로이다. (이후 강의에서 배우는 내용)
    • 언리얼 에디터에서 이 클래스를 블루프린트로 확장할 수 있게 하고, 에디터의 여러 기능과 연동하도록 한다.
  • class SPARTAPROJECT_API AItem : public AActor
    • AActor를 상속받아 AItem 클래스를 정의한다는 의미이다.
    • AItem
      • 언리얼 엔진 C++에서는 클래스 이름에 접두사를 붙이는 컨벤션이 있다.
        • A (Actor 계열), U (Object 계열), F (일반 구조체), T (템플릿), E (열거형) 등
        • 우리는 Item 이라는 이름으로 클래스를 만들었지만, 실제 클래스 이름은 Actor 클래스를 상속받았으므로, AItem 으로 생성된다.
        • 언리얼 엔진 코드 컨벤션 문서
    • SPARTAPROJECT_API 는 이 클래스를 모듈 (여기서는 SpartaProject) 외부로 Export하기 위한 매크로이다. DLL 등으로 빌드할 때 필요한 선언이다.
  • GENERATED_BODY()
    • UCLASS()와 짝을 이루어, 엔진 리플렉션에 필요한 코드를 자동 생성해 주는 매크로이다. (이후 강의에서 배우는 내용)
  • Actor 라이프사이클 관련 함수 (이후 강의에서 배우는 내용)
    • AItem() - 생성자
      • Actor 객체가 메모리에 생성될 때 한 번 호출된다. 월드에 배치되기 전 단계일 수도 있다.
    • BeginPlay()
      • 액터가 월드에 완전히 배치된 뒤, 게임이 플레이(Play) 상태로 시작할 때 한 번 호출된다.
    • Tick(float DeltaTime)
      • 매 프레임마다 자동으로 불린다. 주로 매 프레임 단위의 업데이트가 필요한 로직을 넣는다.

3️⃣ .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"
    • .cpp 파일에서는 가장 먼저, 짝이 되는 헤더 파일을 포함해야 한다.
    • 그렇지 않으면 언리얼의 자동 생성 매크로 (리플렉션) 순서가 뒤섞여 컴파일 에러가 날 수 있다.
  • Actor 라이프사이클 관련 함수 구현부 (이후 강의에서 배우는 내용)
    • 생성자 AItem(), BeginPlay(), Tick(float DeltaTime)
      • 여기서 구현 로직을 자유롭게 추가할 수 있다.

Actor 클래스에 컴포넌트 멤버 변수 추가하고 초기화하기


1️⃣ 컴포넌트란?

  • 위에서 언리얼 에디터 Item 클래스를 뷰포트에 배치하였음에도 불구하고, 월드 상에서 보이지 않았다. 그 이유는 3D 메시를 설정하지 않았기 때문에 보이지 않기 때문이다.
  • 3D 메시를 할당해주기 위해서는 액터에 추가적인 컴포넌트 (Component)를 붙여줘야 한다.
  • 컴포넌트는 언리얼 엔진에서 Actor어떤 역할을 하거나 특정 속성을 갖도록 만들어주는 부품 (파츠) 개념이다.
  • 하나의 Actor가 여러 종류의 컴포넌트를 조합하여 다양한 기능을 구현할 수 있다.
    • 예를 들어, Static Mesh Component + Audio Component + Collision Component를 모두 사용해, 충돌 시 소리가 나는 아이템을 만들 수도 있다.

2️⃣ 루트 컴포넌트와 Scene Component

  • 모든 Actor는 최상위 컴포넌트루트 컴포넌트 (Root Component)를 가져야 한다.
  • Root Component는 액터의 트랜스폼 (위치, 회전, 크기)를 정의하며, 모든 하위 컴포넌트가 이를 기준으로 동작한다.
  • 일반적으로, Scene Component를 루트로 설정하여 액터의 트랜스폼을 관리하고, 그 아래에 다양한 컴포넌트를 계층적으로 붙인다.
    • Scene Component는 모든 트랜스폼 속성을 가지는 기본 컴포넌트입니다.
    • Scene Component는 직접적인 시각적 출력 (3D 모델, 빛 등)을 가지지 않지만, 다른 컴포넌트들의 계층적 트랜스폼을 정의하는 기준점 역할을 한다.
    • Scene Component를 루트로 설정하면, 다른 시각적 컴포넌트를 아래에 Attach하여 관리할 수 있다.

3️⃣ Static Mesh Component란?

  • 애니메이션 없이 움직임이 없거나 단순 이동·회전만 하는 고정된 (Static) 3D 모델을 표시하는 컴포넌트이다.
    • 건물, 바위, 아이템, 환경 오브젝트 등 움직임이 없거나 단순한 오브젝트에 주로 사용된다.
    • 3D 모델을 표현하고, 물리 충돌과 관련된 기능도 제공한다.

4️⃣ Static Mesh Component와 Scene Component 연결하기

  • Item.h 파일에서 다음과 같이 포인터 멤버 변수를 추가한다.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

UCLASS()
class SPARTAPROJECT_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)
    • StaticMeshCompSceneRoot에 부착 (Attach)한다.
    • StaticMeshCompSceneRoot의 하위 컴포넌트로 동작하며, SceneRoot의 트랜스폼 변화에 따라 움직인다.
    • 에디터 상에 Component들이 노출되도록 리플렉션 시스템에 등록하지 않았기 때문에 보이지 않는 것을 확인할 수 있다. 사진에서도 보이듯 내가 설정한 StaticMeshComponent가 안보이는것을 볼 수 있다.
    • 그러나 RootComponent는 설정하지 않아도 기본적으로 리플렉션 시스템에 등록되어 Details 창에서 볼 수가 있다.

메시 및 머티리얼 할당하고 Actor 배치하기


1️⃣ Static Mesh Component의 주요 구성 요소

  • Static Mesh (스태틱 메시)
    • 3D 모델 (정적 메쉬 데이터)를 정의한다. 이는 모델 파일이며, 언리얼 엔진에서는 .uasset 파일로 관리된다.
  • Material (머티리얼)
    • 메쉬의 표면 시각적 속성을 정의한다. 색상, 질감, 반사율, 투명도 등의 속성을 설정할 수 있다.
    • Static Mesh에는 여러 개의 머티리얼 슬롯이 있을 수 있다. 슬롯은 메쉬의 특정 부분에 다른 머티리얼을 적용할 수 있도록 한다.

2️⃣ Static Mesh 및 Material 설정


이런식으로 내가원하는 Mesh와 Material을 레퍼런스 복사로 가져와서

주소를 넣어두자.
그러고 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);

	// Static Mesh를 코드에서 설정
	static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Star_B.SM_Star_B"));
	if (MeshAsset.Succeeded())
	{
		StaticMeshComp->SetStaticMesh(MeshAsset.Object);
	}
	
	// Material을 코드에서 설정
	static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("/Game/Resources/Materials/M_Coin_A.M_Coin_A"));
	if (MaterialAsset.Succeeded())
	{
		StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
	}
}
  • ConstructorHelpers::FObjectFinder<T>

    • Unreal Engine에서 특정 리소스를 경로 기반으로 로드하는 클래스이다.
  • TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"):

    • 리소스의 경로를 나타낸다. 아까 위에서 레퍼런스 복사로 가져온 경로이다.
    • 단, /Game 여기서부터만 입력하면 되고, 앞에 경로는 삭제를 해준다.
    • /Game은 Unreal Engine에서 프로젝트의 Content 폴더를 나타낸다.
  • .Succeeded()

    • 지정된 경로에서 리소스를 성공적으로 찾았는지 확인한다.
    • 경로가 잘못되었거나 리소스 파일이 누락된 경우 실패하며, 이후 설정 함수가 호출되지 않는다.
  • SetStaticMesh(), SetMaterial()

    • 성공적으로 로드된 Static Mesh를 StaticMeshComp에 설정한다.
    • 로드된 Material을 StaticMeshComp의 특정 머티리얼 슬롯에 적용한다. 여기서는 첫 번째 슬롯 (Index 0)에 Material이 설정된다.

3️⃣ 언리얼 에디터에서 Actor 배치 및 테스트

  • 다시Item 클래스를 레벨 뷰포트에 배치해보자.
  • 현재 Details 창에서는 Component가 보이지 않고 에디터 상에서 할당은 불가능하지만, 뷰포트 상에서 메시랑 머티리얼이 잘 할당되었다는 것을 확인할 수가 있다.

    벌써부터 Item액터의 이미지와 물질이 적용되어있는 것을 확인할 수 있다.
    아래는 뷰포트에 배치한 사진이다.

0개의 댓글