언리얼 - 엔진 6 : C++ 컴포넌트

김정환·2025년 4월 9일
0

Unreal Engine

목록 보기
6/24

Actor 클래스 구조 이해

  • 언리얼 엔진에서 C++ 클래스를 만들면 자동으로 .h, .cpp 2개의 파일이 생성됨.

헤더 파일 구성

1. 상단

#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" : 액터 클래스에 대한 헤더 파일
  • #include "Item.generated.h" : 리플렉션 시스템 관련

2. 클래스 선언부

UCLASS()
class STUDY_API AItem : public AActor	
{
	GENERATED_BODY()
    
public:	
	AItem();	// 클래스 생성자

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;		// 액터 라이프 사이클 함수
	// Called every frame
	virtual void Tick(float DeltaTime) override;	// 액터 라이프 사이클 함수
};
  • UCLASS()
    • 언리얼 엔진의 리플렉션 시스템에 등록하는 매크로
    • 에디터에서 이 클래스를 BP로 확장할 수 있게하고, 에디터의 여러 기능과 연동 지원.
  • class STUDY_API AItem : public AActor
    • AActor 상속받아 AItem 클래스 정의
      • Item이라고 클래스를 만들었지만, 액터를 상속하면서 AItem으로 생성됨.
      • 언리얼에서는 접두어를 붙이도록 강조함.
      • 언리얼 c++의 코딩 컨벤션의 접두어 종류
        • U : UObject 상속, Object 계열 의미
        • A : AActor 상속, Actor 계열 의미
        • E : 열거형
        • T : 템플릿 의미
        • F : 일반 구조체
    • STUDY_API
      • 이 클래스를 모듈 밖으로 Export하기 위한 매크로.
      • DLL 빌드 시 필요한 선언.
        • [프로젝트명]_API 형태로 작성.
  • GENERATED_BODY()
    • UCLASS()와 짝으로, 엔진 리플렉션에 필요한 코드를 자동 생성해주는 매크로.
  • Actor 라이프 사이클 함수
    • AItem() : 생성자
      • Actor 객체가 메모리에 생성될 때 한 번 호출.
      • 이는 월드에 배치되기 전 단계일 수도 있음.
    • BeginPlay()
      • 액터가 월드에 완전히 배치된 뒤, 게임이 플레이 상태로 시작할 때 한 번 호출.
    • Tick(float DeltaTime)
      • 매 프레임마다 자동으로 호출
      • 주로 매 프레임 단위의 업데이트가 필요한 로직을 넣음.

C++ 파일 클래스 구현부

#include "Item.h"

// Sets default values
AItem::AItem()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;	// tick 함수 사용에 대한 설정

}

// Called when the game starts or when spawned
void AItem::BeginPlay()
{
	// 부모 클래스의 BeginPlay()를 먼저 호출
	Super::BeginPlay();	
}

// Called every frame
void AItem::Tick(float DeltaTime)
{
    // 부모 클래스의 Tick(DeltaTime)를 먼저 호출
	Super::Tick(DeltaTime);

}
  • #include "Item.h"
    • .cpp 파일에서 가장 먼저, 짝이 되는 헤더 파일을 포함해야 함.
    • 그렇지 않으면 언리얼의 자동 생성 매크로 (리플랙션) 순서가 뒤섞여 컴파일 에러가 날 수 있음.

컴포넌트 Component

Component란 각각의 기능이 있는 부품들.

  • Actor만으로는 원하는 객체를 만들기 어려움.
  • Component라는 부품Actor에 붙여줌으로써 어떤 역할이나 특정 속성을 갖도록 할 수 있음.
    • 하나의 Actor가 여러 종류의 컴포넌트를 조합하여 다양한 기능을 구현할 수 있음.
      • ex) StaticMeshComponent 모델을 보여주기 + AudioComponent 효과음 출력하기 + CollisionComponent 충돌 인식하기
      • 이렇게 여러 컴포넌트를 조합하여 충돌하면 소리를 내는 아이템을 만들어줄 수 있음.
  • 앞서 만든 AItem 클래스를 월드에 배치해도 0,0,0 에서 안 움직인 이유
    1. Actor를 만들었다고 끝이 아니라 컴포넌트를 붙여줘야 제대로 동작할 수 있음.
    2. Actor가 좌표계를 인식하지 못함.
      • RootComponent가 없어서 좌표계를 인식하지 못한 것.
      • Actor는 반드시 하나 이상의 RootComponent가 있어야 제 기능을 할 수 있음.

Root Component와 Scene Component

  • AItem 클래스를 BP로 만들어서 배치하니까 그제서야 월드에서 움직일 수 있음.
    • 디테일에서 살펴보니 DefaultSceneComponent가 내부에 생겼음.
    • BP를 만드니 자동으로 Root가 생성됨.

Root Component

  • 모든 Actor는 최상위 컴포넌트인 루트 컴포넌트를 가져야함.
  • 액터의 트랜스폼(위치, 회전, 크기)를 정의하며, 모든 하위 컴포넌트가 이를 기준으로 동작함.
  • 일반적으로 Scene Component를 루트로 설정

Scene Component

  • 시각적인 출력이 없고 모든 트랜스폼 기능을 포함하는 기본 컴포넌트.
  • 다른 컴포넌트들의 계층적 트랜스폼을 정의하는 기준점 역할을 함.
  • 공식에서도 root로 SceneComponent를 쓰는 것을 권장함.
  • Scene Component를 루트로 설정하면, 다른 시각적 컴포넌트를 아래에 Attach해서 관리할 수 있음.

Static Mesh Component

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

헤더 코드

다음과 같이 사용할 컴포넌트들의 포인터 멤버 변수를 작성.

  • BeginPlay(), Tick() 함수들은 필요없어서 제거함.
#pragma once

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

UCLASS()
class STUDY_API AItem : public AActor	
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AItem();

protected:
	USceneComponent* SceneRoot;	// 포인터를 형태로 선언
	UStaticMeshComponent* Mesh;
};
  • USceneComponent* SceneRoot;
    • root로 사용할 SceneComponent
    • 포인터를 형태로 선언
  • UStaticMeshComponent* Mesh;
    • 시각적으로 표현할 메쉬 컴포넌트
    • 이 상태로는 리플랙션에 등록이 안돼서 에디터에선 보이지 않음.
      => 그래서 매크로를 적용함 UPROPERTY
    • 루트는 기본적으로 설정되면 리플랙션에 바로 등록되도록 반영됨.

cpp 코드

#include "Item.h"

// Sets default values
AItem::AItem()
{
	SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	SetRootComponent(SceneRoot);

	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Model"));
	Mesh->SetupAttachment(SceneRoot);

	// 메쉬와 재질 설정하기
	// 1. 메쉬 불러오기 
	static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"));
    
	// 확인 작업
	if (MeshAsset.Succeeded()) // 로드 성공 확인
		Mesh->SetStaticMesh(MeshAsset.Object);

	// 2. 재질 불러오기 
	static ConstructorHelpers::FObjectFinder<UMaterialInstance> MatAsset(TEXT("/Game/Resources/Materials/M_Gem_C.M_Gem_C"));
	
	if (MatAsset.Succeeded()) // 로드 성공 확인
        Mesh->SetMaterial(0, MatAsset.Object);
}

Component 생성 및 Attach

  • SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
    • CreateDefaultSubobject<T>(TEXT("컴포넌트 이름"))
      • 컴포넌트 생성 함수
        • Unreal Engine에서 컴포넌트를 생성하고 초기화할 때 사용하는 함수
        • 템플릿 형태이므로 원하는 컴포넌트를 넣어서 받을 수 있음.
        • 매개변수는 컴포넌트의 이름을 요구함
    • 생성한 컴포넌트를 멤버 포인터 변수 SceneRoot에 할당.
  • SetRootComponent(SceneRoot);
    • 매개변수로 넣은 컴포넌트를 루트 컴포넌트로 설정
    • 액터의 최상위 부모 컴포넌트로 설정한 것
  • Mesh->SetupAttachment(SceneRoot);
    • SceneRoot의 자식으로 부착 Attach하는 함수.
  • 이 상태에서 C++ 클래스를 월드에 배치하면 아이템 인스턴스에 RootComponent가 생긴 것을 확인할 수 있음.
    • 이때 디테일에서 선언한 변수들이 안보이는 이유 리플렉션에 등록이 안됐기 때문.
    • RootComponent가 보이는 이유는 자동으로 리플렉션에 등록됨.

StaticMesh 및 Material 불러오기

  • static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"));
    • 변수 선언 + 리소스를 로드
    • static ConstructorHelpers::FObjectFinder<T>
      • 특정 리소스를 경로 기반으로 로드하는 클래스
    • TEXT("/Game/Resources/Props/SM_Chair.SM_Chair")
      • 리소스 경로.
      • 언리얼에서는 에셋의 경로를 쉽게 받아올 수 있음.
        • 콘텐츠 브라우저에서 원하는 에셋 우클릭 > Copy Reference
        • 경로 출력 /Script/Engine.StaticMesh'/Game/Resources/Props/SM_Chair.SM_Chair'
        • 필요한 '/Game/Resources/Props/SM_Chair.SM_Chair' 경로 부분만 사용.
      • /Game은 언리얼 엔진에서 프로젝트의 Content 폴더를 표시
    • 사용 시 주의 사항
      • 템플릿으로 원하는 클래스 형태로 불러올 것인데, 에셋의 클래스를 잘 확인할 것.
      • 클래스가 다르면 false 반환.
      • UMaterial, UMaterialInstance와 같은 기능을 하는데 아예 다른 클래스가 있음.
  • .Succeeded()
    • 지정된 경로에소 리소스를 성공적으로 찾았는지 확인.
    • 찾지 못했거나 하는 이유로 실패하면 false 반환.
  • Mesh->SetStaticMesh(MeshAsset.Object);
    • 메쉬 할당하는 기능
  • Mesh->SetMaterial(0, MatAsset.Object);
    • 재질은 여러개를 불러올 수도 있음.
    • 앞에 인덱스가 필요
      • 예시에서는 가장 첫번째에 넣어줌.
profile
만성피로 개발자

0개의 댓글