언리얼 공부/C++

C++클래스로 액터 생성하고 컴포넌트 추가하기.

jeongchanhyo 2025. 1. 23. 20:52

1. 헤더파일

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

일단 이 3개를 가져와준다

CoreMinimal.h : 코어 미니멀 이란 이름에 맞게  언리얼 엔진의 기본 클래스를 포함한다. 필수적인 기본 기능들을 제공.

GameFramework/Actor.h : 이 클래스가 Actor기반의 클래스라는 걸 나타낸다.

Item.generated.h : Unreal Tool에서 만들어준것으로 언리얼 엔진의 매크로와 기능을 활용하기 위해 필요 그리고 무조건 무조건 #include의 맨 마지막에 있어야된다 안그러면 버그가난다.

UCLASS()
class SETTING_API AItem : public AActor
{
	GENERATED_BODY()

UCLASS() : 이건 언리얼 엔진에서 이 클래스를 하나의 객체로 받아들일 수 있게해주는 녀석이다. 

SETTING_API: 이 매크로는 DLL의 API를 정의하는 데 사용됩니다. 이 클래스가 다른 모듈에서 접근 가능하다는 것을 나타냅니다.

AItem : public AActor: AItem 클래스가 언리얼의 기본 액터 클래스인 AActor를 상속받고 있음을 나타냅니다. 따라서 AItem은 액터로서의 모든 기능을 가진다. 언리얼은 강제로 이름 앞에 타입을 넣게 만들어놨다.

GENERATED_BODY(): 언리얼의 매크로로, 클래스의 생성자 및 기타 필수적인 기능을 자동으로 생성.

public:	
	AItem();

protected:
	USceneComponent* SceneRoot;
	UStaticMeshComponent* StaticMeshComp;

AItem() : Item 클래스 생성자이다. 위에서 GENERATED_BODY():가 분명히 클래스의 생성자 및 필수적인 기능들을 자동으로 생성한다고 했는데 이게 왜 필요한지 궁금한사람이 있을 수 있다. 이게 헷갈렸었다.

GENERATED_BODY()의 역할

  • 자동 코드 생성: GENERATED_BODY()는 Unreal Engine의 매크로로, 클래스의 기본적인 기능을 자동으로 생성합니다. 여기에는 다음과 같은 것들이 포함됩니다:
    • 리플렉션 시스템을 위한 메타데이터.
    • 가비지 컬렉션을 위한 기본 설정.
    • 기본 생성자 및 복사 생성자와 같은 필수적인 메서드의 선언.

AItem() 생성자의 역할

  • 사용자 정의 초기화: AItem() 생성자는 클래스 인스턴스가 생성될 때 호출되는 사용자 정의 생성자입니다. 이 생성자는 다음과 같은 작업을 수행하는 데 사용됩니다:
    • 멤버 변수 초기화.
    • 컴포넌트 추가 및 설정.
    • 기타 초기화 로직(예: 이벤트 바인딩, 상태 설정 등).

이렇게 GENERATED_BODY()는 클래스의 기본적인 기능을 제공하는 반면, AItem() 생성자는 개발자가 원하는 초기화 로직을 추가하는 데 사용된다.

USceneComponent* SceneRoot : 씬의 루트 컴포넌트를 나타내는 포인터다. 이 컴포넌트는 액터의 위치, 회전 및 스케일을 관리. 이게 없으면 위치를 잡을 수 없다. 세계를 기준으로 액터에 위치 개념을 넣는다고 생각하면 된다.

UStaticMeshComponent* StaticMeshComp : 정적 메시를 렌더링하는 컴포넌트를 나타내는 포인터. 이 컴포넌트를 통해 3D 모델이 씬에 표시된다. Static인걸 보면 알겠지만 정적이라 변하지 않는 액션이 없는 액터를 만들거기 때문에 사용해줬다.

그래서 완성된 헤더파일은

#pragma once

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

UCLASS()
class SETTING_API AItem : public AActor
{
	GENERATED_BODY()
	
public:	
	AItem();

protected:
	USceneComponent* SceneRoot;
	UStaticMeshComponent* StaticMeshComp;
};

 

2. CPP파일.

PrimaryActorTick.bCanEverTick = true;

 PrimaryActorTick :언리얼에서 게임의 각 프레임마다 특정 작업을 수행하도록 설정할 수 있는 시스템

 bCanEverTick = true : 불리언(bool) 타입의 속성 멤버를 true로 설정합니다.

대충 요약해서 보면 나오자 마자 프레임에 맞춰 업데이트를 해준다고 보면되는데 이러면 무거워질까봐 걱정이 됐었다.

하지만 언리얼엔진에서는 필요한 액터만 틱을 활성화해주는 기능이 있는데 그게  bCanEverTick이다.

SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);

 

SceneRoot : 헤더파일에서 선언한 USceneComponent*타입의 포인터 변수 이름
Create Default Sub object : 가독성을 위해 띄워썼다. 이건 액터의 기본 구성 요소를 생성하는 함수다. 

SceneComponent : 3D공간에서 위치와 회전을 갖고 있는 기본적인 구성 요소.

TEXT("SceneRoot") : 생성되는 서브 오브젝트의 이름 선언

SetRootComponent(SceneRoot) : 현재 액터의 루트 구성 요소를 설정하는 함수. 이 호출을 통해 생성된 SceneRoot를 액터의 루트로 설정하기 때문에 이후에 추가하는 다른 구성요소들은 이 루트에 종속된다. 이걸 기준으로 이동하거나 한다는 뜻

한마디로 SceneRoot라는 변수는 3D공간에서 위치와 회전등의 여러 기본 요소들이 들어가게 되고 SetRootComponent를 통해 이 액터에서 사용되는 모든게 이녀석에 종속되게된다. 

StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);

StaticMeshComp : UStaticMeshComponent* 타입의 포인터 변수 이름

Create Default Sub object : 위에서 설명했던거랑 같은 액터의 기본 구성 요소를 생성하는 함수다. 

UStaticMeshComponent : 정적 메시를 표시하는 구성요소.

TEXT("StaticMesh") : 구성요소의 이름

SetupAttachment->SetupAttachment(SceneRoot) : 생성된 StaticMeshComp를 SceneRoot에 부착하는거다. 즉 액터가 회전을 하면 스태틱 매시도 같이 회전하고 이동하면 같이 이동하는거라고 보면 된다.

 

그러면 이제 내가 만든 액터에 매시를 넣어주면 된다.

일단 매시의 위치가 필요하다. 위치를 알아야 갖다 붙힐 수 있기 때문에

내가 원하는 매시에 우클릭을 하여 레퍼런스 복사를 해주자.

그리고 붙여넣으면 이렇게 보일텐데 붉은색 말고는 다 지워주면 된다.

그리고 코드를 만들면 이렇게되는데

static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT(" / Game / Resources / Props / Pillar_50x500.Pillar_50x500"));

static ConstructorHelpers : 언리얼에서 자산을 로드하는데 사용되는 유틸리티 클래스를 제공하는 네임스페이스다.

FObjectFinder<UStaticMesh> : 오브젝트 찾기. StaticMesh타입 이라고 생각하면 된다.

MeshAsset(TEXT(" / Game / Resources / Props / Pillar_50x500.Pillar_50x500")) : 위치를 넣어준거다.

그냥 경로에서 에셋을 찾는 코드다.

	if (MeshAsset.Succeeded())
	{
		StaticMeshComp->SetStaticMesh(MeshAsset.Object);
	}

if (MeshAsset.Succeeded()) : 자산의 로드 성공여부 확인

StaticMeshComp->SetStaticMesh(MeshAsset.Object) : 스태틱 매시에 내가 불러온 자산 집어넣기

만약 파일의 위치가 바뀌거나 삭제된 경우 심각한 오류가 생길 수 있으니 불러오는데 성공하였다면 집어넣어라~ 라는 일종의 안전장치이다.

#include "Item.h"

AItem::AItem()
{
	PrimaryActorTick.bCanEverTick = true;
	SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	SetRootComponent(SceneRoot);

	StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
	StaticMeshComp->SetupAttachment(SceneRoot);


	static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT(" / Game / Resources / Props / Pillar_50x500.Pillar_50x500"));
	if (MeshAsset.Succeeded())
	{
		StaticMeshComp->SetStaticMesh(MeshAsset.Object);
	}
	static ConstructorHelpers::FObjectFinder<UMaterial>MaterialAsset(TEXT("/ Game / Resources / Materials / M_Armor.M_Armor"));
	if (MaterialAsset.Succeeded())
	{
		StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
	}
}

이렇게 CPP파일이 나온다.

개념이 중요해서 하나하나 무슨일 하는지 한번 되짚어봤다. 

'언리얼 공부 > C++' 카테고리의 다른 글

순수가상함수, 추상클래스 그리고 인스턴스  (0) 2025.02.20
플로우 차트  (0) 2025.02.18
FPS무기 구조 생각  (0) 2025.02.17
일정 주기로 나왔다 사라지는 발판  (0) 2025.02.04
액터 회전, 반복 이동  (0) 2025.02.03