언리얼 엔진 본캠프 6주차-4 언리얼 엔진 C++ : Actor 클래스 + 컴포넌트

정재훈·2025년 1월 23일
0

unreal engine

목록 보기
16/45

Actor 클래스 코드의 기본 구조

.h 파일

  • 상단부
    • #pragma once : 헤더 파일의 중복 포함 방지를 위한 지시문
    • #include "CoreMinimal.h" : 언리얼 엔진의 주요 기능과 클래스를 제공하는 경량화된 헤더 파일
    • #include "GameFramework/Actor.h" : AActor를 상속받기 위한 헤더 파일
    • #include "Item.generated.h" : 언리얼 엔진의 리플렉션 시스템을 지원하기 위해 필요한 헤더 파일
      • UHT에 의해 생성됨
      • UCLASS(), GENERATED_BODY() 매크로들도 리플렉션 시스템과 관련된 코드
  • 선언부
    • 접두어 : 클래스 앞에 A가 붙어있는 걸 볼 수 있는데 Actor 클래스 계열을 나타냄
    • BASIC_API : 클래스(AItem)를 외부로 Export하기 위한 매크로. 빌드할 때 필요한 매크로
  • 라이프 사이클
    • AItem() : 생성자
    • BeginPlay() : 객체 스폰 시 한 번만 호출되는 함수
    • Tick() : 매 프레임마다 호출되는 함수

.cpp 파일

  • 각 멤버 함수에 대한 구현부

Actor 클래스에 컴포넌트 추가하기

1. 루트 컴포넌트

  • 모든 Actor는 최상위 컴포넌트루트 컴포넌트(Root Component)를 가져야 한다
  • Root Component는 액터의 트랜스폼 (위치, 회전, 크기)를 정의하며, 모든 하위 컴포넌트가 이를 기준으로 동작
    • 다른 컴포넌트들의 기준점 역할을 한다

루트 컴포넌트(Scene Component)는 리플렉션 시스템에 무조건 등록되게 되어 있어 추가 작업을 하지 않아도 에디터에서 볼 수 있음

  • 하지만 하위 Component들은 리플렉션 시스템에 등록하지 않았기 때문에 에디터 Details 창에서 보이지 않음

현재 코드는 Static Mesh Component를 생성만 했지 시각적으로 보여지는 모델이 할당된 것은 아니기 때문에 언리얼 엔진 뷰포트에서도 아무것도 보이지 않는다

  • USceneComponent* SceneRoot : 트랜스폼 정보를 갖는 USceneComponent의 포인터 SceneRoot
  • UStaticMeshComponent* StaticMeshComp : Static Mesh를 렌더링하는 UStaticMeshComponent의 포인터 StaticMeshComp
  • CreateDefaultSubobject<T>(TEXT("name")) : 액터의 컴포넌트(T)를 생성할 때 사용하는 함수
    • name은 에디터에서 식별할 이름
  • SetRootComponent(SceneRoot) : 루트 컴포넌트를 지정하기 위한 함수로 루트 컴포넌트를 SceneRoot로 지정
  • SetupAttachment(SceneRoot) : 컴포넌트를 루트 컴포넌트에 붙이기 위한 함수
    • SetupAttachment()는 매개변수로 USceneComponent*를 받는다

2. Mesh 및 Material 할당

Static Mesh : 3D 모델(정적 메쉬 데이터)
Material : Mesh의 표면 속성을 설정

  • Static Mesh에는 여러 개의 Material 슬롯이 있어, 이 슬롯에 따른 특정 부분마다 다른 머티리얼을 적용할 수 있다

Item 액터에 Static Mesh와 Material이 잘 적용된 것을 볼 수 있다.
그런데 1번 슬롯에 적용한 Material에 대해서는 아무런 변화가 없어 0번 슬롯을 주석으로 처리하고 1번 슬롯만 적용해보았다

1번 슬롯에 황금 Material을 설정 했는데(노란 박스) 아무런 변화가 없었다

에디터에서 원본 실린더 Static Mesh를 더블클릭해서 켜보니 Material slot이 하나만 있는 걸 볼 수 있었다
(아마 현재 코드는 Static Mesh의 Material slot에 "Debuff Material과 황금 Material"을 설정한 것이고 누굴 선택할지 정하지 않은 상태라서 default로 0번 슬롯이 선택되서 Debuff Material만 잘 적용되는 것 같다)

  • ConstructorHelper::FObjectFinder<T> : 게임 에셋을 로드할 때 사용하는 유틸리티 클래스
    • 에셋 경로를 문자열로 제공받아 리소스를 찾음
    • 경로는 에디터에서 리소스 우클릭 → Copy Reference를 하면 가져올 수 있다
    • 경로의 /Game == 프로젝트 폴더의 Content 폴더
  • .Succeeded() : 에셋 로드 성공 여부 확인
  • SetStaticMesh(), SetMaterial() : 성공적으로 로드한 Static Mesh와 Material을 Static Mesh Component에 설정

Material 슬롯이 2개인 Skeletal Mesh를 로드해서 0번 슬롯과 1번 슬롯에 각각 다른 Material을 적용해보니 잘 적용되는 걸 볼 수 있었다

0번 슬롯에는 황금 Material, 1번 슬롯에는 녹슬어버린 철 Material을 적용하니 각 부위마다 다르게 적용된 것을 볼 수 있었다

그럼 저 슬롯 개수를 C++ 코드로 늘리거나 줄이는 그런게 있나?

=> 슬롯을 늘릴 수 있음. 에디터에서 늘려도 되고 C++ 코드로도 늘릴 수 있음. 하지만 이게 잘 적용되고 안되고는 다른 문제임
슬롯을 추가해서 하나를 늘렸다고 해도 해당 슬롯의 머티리얼이 적용될 부분에 대한 작업을 하지 않았기 때문에 원하는 데로 작동하지 않음
그래서 위에 static mesh에 머티리얼 2개를 넣었을 때 0번 슬롯에 들어간 머티리얼만 적용된 것

3. 추가 구현

  • 루트 컴포넌트를 UCapsuleComponent로 지정
  • UAudioComponent 추가
  • USpringArmComponent와 UCameraComponent 추가
  • 위치, 회전, 크기 등을 조절

추가하면서 알아야 할 것들에는 주석을 추가



그리고 헤더파일의 상단부에 #include "---.h"를 추가하면 GENERATED_BODY() 매크로에 빨간 줄이 생기는데 그냥 빌드하고 실행해도 잘 동작한다. 하다보면 빨간 줄도 알아서 사라져있음



언리얼에서 클래스를 생성하는 함수

  1. 메타 데이터 클래스를 반환하는 StaticClass()
  2. UObject와 그 하위 클래스를 동적으로 생성(클래스 인스턴스 생성)하는 NewObject<t>()
  3. AActor기반 클래스를 스폰(월드에 실제로 존재)하는 SpawnActor<T>()
  4. NewObject와 비슷한 역할을 한 이전 언리얼 버전에서 사용된 ConstructObject<T>()
  5. UObject의 서브 오브젝트를 생성하는 CreateDefaultSubobject<T>()
  6. UGameplayStatics::SpawnEmitterAtLocation()

이렇게 Unreal Engine에서 제공하는 생성 함수를 사용해서 생성된 [객체]들은 모두 Unreal Engine의 Garbage Collection에 의해 관리된다

여기서 CreateDefaultSubobject<T>()는 객체를 정적으로 생성시켜주는 함수

  • 액터의 컴포넌트들을 생성할 때 사용되며, 이렇게 생성된 컴포넌트는 액터의 라이프 사이클을 따른다 => 액터는 SpawnActor<T>()로 동적 할당이 되기 때문에 이 컴포넌트들도 힙 메모리에 저장되고 액터가 제거될 때 함께 제거됨
=> 일단 CreateDefaultSubobject<T>() 함수 내부 코드를 따라가보면 FMemory::Malloc() 하는 부분을 찾을 수 있었다. 여기를 참고해서 실제로 어떤 식으로 메모리 할당이 되는지 확인해보자

컴포넌트 생성 함수 메모리 할당

파란박스는 컴포넌트, 빨간 박스는 동적 배열, 연두 박스는 일반 int형 변수

  • 메모리 주소를 보면 컴포넌트 주소(0x00000544EC703200)와 동적 배열의 주소(0x00000544EC5B5CE0)가 비슷하고
  • 일반 int형 변수의 주소는 0x0000001C7E7794BC로 다른 걸 볼 수 있다

=> CreateDefaultSubobject<T>()는 컴포넌트는 생성할 때 동적 메모리를 할당된다.

언리얼에서의 정적 생성, 동적 생성라는 말이 메모리를 스택 메모리, 힙 메모리에 할당한다는 말이 아니라 객체가 world나 level에 언제 배치되는가에 대한 것

  • 정적 생성 : 레벨에 미리 배치되어 월드 초기화 시 자동으로 생성되는 액터를 의미
    • 레벨 디자인할 때 에디터에 배치한 오브젝트들
  • 동적 생성 : 런타임 중에 생성되는 액터를 의미
    • 특정 로직에 의해 게임 플레이 도중 생성되는 액터들






참조 사이트
1. https://rhksgml78.tistory.com/698
2. https://velog.io/@singery00/UE5-C-C%EB%A1%9C-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%BA%90%EB%A6%AD%ED%84%B0-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0-%E5%AE%8C
3. https://forums.unrealengine.com/t/how-to-change-character-capsule-component-location/366247
4. https://velog.io/@sobokii/UE5-Roll-Pitch-Yaw

profile
드가자

0개의 댓글