본문 바로가기

개발 공부 기록/UnrealEngine5

이득우의 언리얼 C++ 게임 개발의 정석 UE5로 따라잡기 - 1

 

개요 

UE4 학습 목적으로 옛날에 구매하고 잊고있던 '이득우의 언리얼 C++ 게임 개발의 정석' 책을 언리얼 엔진5 학습차원에서
UE5로 학습하면 어떤 부분이 달라지는 지와 공부를 하며 주의할 내용들에 대한 글입니다.

책의 내용이 궁금하다면 다른 분이 정리한 블로그나 자료를 찾아보는 걸 권장합니다.

 

알라딘 책 구매 링크

 

이득우의 언리얼 C++ 게임 개발의 정석

언리얼 엔진 학습에 목말라하는 게임 개발자에게 단비 같은 언리얼 엔진 프로그래밍 책이다. 에픽게임즈 본사의 개발자 프로그램 언리얼 데브 그랜트를 수상한 저자의 책으로, 언리얼 엔진의

www.aladin.co.kr

이득우님 인프런 강의 링크

 

이득우님의 강의 - 인프런 | 온라인 강의 플랫폼

인프런 이득우님의 강의 페이지 입니다. - 이득우님 소개 | 인프런

www.inflearn.com

수민님이 정리한 velog

 

[이득우의 언리얼 C++ 게임 개발의 정석] Chapter 2. 액터의 설계

Chapter 2. 액터의 설계

velog.io

 

IDE: Visual Studio 2022
엔진 버전: Unreal Engine 5.4.4

 

본문

우선적으로 에디터의 디자인과 구조에서 많은 부분이 달라진 것을 볼 수 있었다.
개인적으론 훨씬 깔끔하고 세련되어 졌다고 생각한다. (UE4의 물빠진색감과 이해할 수 없는 얼룩말 줄무늬가 싫었었다.)

'새로운 C++ 클래스'를 생성하는 부분의 위치가 변경되었다.

UE4에서는 '파일 -> 새로운 C++ 클래스'의 경로로 접근했는데 UE5로 넘어오면서 '툴 -> 새로운 C++'로 옮겨졌다.

 

'프로젝트 패키지' 접근 위치가 변경되었다.

UE4에서는 '파일 -> 패키지 프로젝트'로 접근했는데 UE5에서는 상단 '플랫폼 -> '원하는 플랫폼' -> 패키지 프로젝트'로 바뀌었다.

 

이 외에도 당장 눈에 보이는 UI들의 위치도 많이 바뀌었다.(중앙에 있던 세팅도 우측 상단 끝으로 갔다던지)
혹시나 UE4 자료를 보면서 UE5를 학습하고자 하는 분들이라면 이런 부분들을 잘 찾아보고 접근해야 할 것 같다.

 

 

C++ 프로젝트의 빌드 바이너리 구성은 여러 곳에서 확인해볼 수 있는데 위의 프로젝트 패키지쪽에서 바이너리 구성으로 접근도 가능하고

우선 사용중인 Visual Studio의 상단에서 드롭다운 메뉴를 통해서 전환과 확인할 수 있다.

또한 언리얼 엔진 상단 툴바 '편집 -> 프로젝트 세팅 -> 프로젝트 -> 패키징 -> 프로젝트/빌드 환경설정'에서도 확인할 수 있다.

 

각 빌드 환경설정은 아래와 같다.

  • DebugGame: 디버깅을 위한 최적화가 되지 않은 결과물을 생성하는 빌드 구성. 실행을 위한 exe 파일을 생성한다.
  • DebugGame Editor: DebugGame과 동일한 수준의 에디터용 DLL 파일을 생성한다.
  • Development: 중간 수준의 최적화와 디버깅도 가능한 결과물을 생성하는 구성이다. exe 파일을 생성한다.
  • Development Editor: Development와 동일한 수준의 에디터용 DLL 파일을 생성한다. 설정의 기본값이다.
  • Shipping: 게임의 최종 배포를 위해 최적화된 코드를 만들어내는 구성이다. exe 파일을 생성한다.

각자 목적에 맞게 전환하여 사용해주면 되고 기본적으로 개발할 때는 Development Editor에 맞춰 사용한다.

 

 

언리얼 엔진에서 기능을 규격화하고 액터가 이들을 조합할 수 있게 설계한 것을 '컴포넌트'라고 한다.
컴포넌트는 월드 아웃라이너 창에서 액터를 선택한 후 디테일 창에서 확인해볼 수 있다.

 

엔진에서 제공하는 주요 컴포넌트로는 아래와 같이 존재한다.

  • 스태틱메시 컴포넌트: 애니메이션이 없는 모델링 애셋, 주로 배경 물체에 사용.
  • 스켈레탈메시 컴포넌트: 애니메이션이 있는 모델링 애셋, 주로 캐릭터에 사용.
  • 콜리전 컴포넌트: 구/박스/캡슐로 지정한 영역에 물리적 기능을 설정하는 모듈
  • 카메라 컴포넌트: 가상 세계에서 보여지는 현재 상황을 플레이어에게 보여주는 기능
  • 오디오 컴포넌트: 가상 세계에서 소리를 발생시키는 기능
  • 파티클 시스템 컴포넌트: 파티클 시스템으로 설계된 이펙트를 보여주는 기능
  • 라이트 컴포넌트: 전구, 헤드라이트 등과 같이 물체에 광원 효과를 부여하는 기능
  • 무브먼트 컴포넌트: 물체에 특정한 움직임을 부여하는 기능

액터는 여러 개의 컴포넌트를 소지할 수 있는데 반드시 그 중 대표하는 하나의 컴포넌트를 지정해야 한다.
이를 '루트 컴포넌트'라고 한다.

 

Fountain이라는 액터를 생성한 후 그 액터의 멤버로 2개의 UStaticMeshComponent를 선언하고 생성했다.

RootComponent = Body;
Water->SetupAttachment(Body);

위의 코드 구문같이 루트 컴포넌트를 Body로 설정해주고 그 자식으로 Water를 지정해줄 수 있다.

 

그러나 에디터 상에서 Body 컴포넌트는 보이지만 자식으로 지정한 Water 컴포넌트는 보이지 않는데

이와 같이 선언한 클래스 위에 UPROPERTY 매크로와 함께 인자로 'VisibleAnywhere'를 사용하고 컴파일하면 잘 나오게 된다.

UPROPERTY 매크로와 인자에 대한 정리는 아래 블로그가 가장 깔끔하게 정리되어 있어서 참고하면 좋을 것 같다.

CatDarkGame님의 UPROPERTY와 인자 정리

 

UE4 C++와 블루프린트 연동 기본 -1. UPROPERTY()

UPROPERTY() UPROPERTY()는 변수에 붙는 리플렉션 메크로입니다, 위 코드 처럼 UPROPERTY() 메크로에 인자 값을 넣어서 용도에 맞게 활용 할 수 있습니다. 내부에 들어가는 인자 스타일은 제가 자주 쓰는

darkcatgame.tistory.com

 

 

언리얼 엔진에서는 4.15버전부터 IWYU(Include What You Use)라는 새로운 개념과 방식을 도입했다.
이는 언리얼 엔진에서 소스 코드가 컴파일하는 데 필요한 종속성만 포함한다는 것을 의미한다.

목적은 기존에 사용되던 모놀리식 헤더 파일인 'Engine.h''UnrealEd.h'를 포함하지 않고 종속성을 완화한다는 것이다.

위의 파일들은 사전 컴파일된 헤더(PCH) 파일이며 이를 통해 빠른 컴파일 타임을 확보하고자 하는 방식이었지만 시간이 지나 엔진이 성장하면서 병목현상을 일으키게 된 것이다.

모놀리식 아키텍처(Monolithic Architecture)
하나의 통합된 코드 베이스로 여러가지 기능을 수행하는 소프트웨어 개발 모델을 의미한다.
즉 단일 애플리케이션 내에 서비스의 모든 로직이 통으로 들어가 있는 구조다.
이와 반대되는 개념으로는 마이크로서비스 아키텍터(Micro Service Architecture)가 존재한다.

모놀리식 vs 마이크로서비스 아키텍처 간의 설명과 비교

 

[INFCON Tech Series #5] 모놀리식 vs 마이크로서비스 아키텍처, 우리 팀에 맞는 선택은? - 인프런 | 스토

비즈니스에 적합한 아키텍처 찾기 #MSA #Monolithic #웹개발 #INFCON #인프콘 [사진] 개발자들이 함께 모여 서로의 경험과 인사이트를 나누는 축제, 인프콘! 8월 15일, 드디어 많은 분들이 기다려 주

www.inflearn.com

 

그러하여 IWYU개념은 'CoreMinimal.h'라는 최소 형태로 필요한 종속성만을 포함하는 헤더 파일을 기본적으로 include하고 나머지 필요한 기능들은 직접 추가하는 방식을 통해 최적화를 하고 컴파일과 빌드 시간을 최소화하게 되었다.

IWYU - 언리얼 엔진 공식문서

 

 

기존 C++에서 포인터는 선언 후 개발자가 명시적으로 직접 객체를 소멸시켜야 메모리 관리에 문제가 발생하지 않는다.
만약 잘못된 포인터를 사용하면 아래와 같은 문제가 발생한다.

  • 메모리 누수(leak): new를 통해 동적할당을 했지만 delete를 하지 않아 힙에 메모리가 남아있는 상황
  • 댕글링 포인터(Dangling): 이미 해제하여 무효화된 오브젝트의 주소를 외부에서 가리키는 포인터
  • 와일드 포인터(Wild): 값이 초기화되지 않아 엉뚱한 주소를 가리키는 포인터

C++ 11 이상에서는 '스마트 포인터'를 통해 이러한 문제를 해결하고자 하였지만 일반적인 포인터의 사용법과는 달라 학습이 필요하다. 언리얼 엔진에서는 이런 C++ 11의 스마트 포인터들을 커스텀 구현하여 활용할 수 있게 해주고 있다.

스마트 포인터에 대한 MS 공식문서

 

스마트 포인터(최신 C++)

자세한 정보: 스마트 포인터(최신 C++)

learn.microsoft.com

언리얼 스마트 포인터 라이브러리 - 언리얼 엔진 공식문서

 

언리얼 엔진에서는 기존 포인터를 선언하는 것처럼 선언하지만 UPROPERTY라는 매크로를 사용하면 UObject(언리얼 오브젝트)들에 대한 포인터에 대해서 포인터 문제가 발생하지 않게 '가비지 컬렉션'을 도입하여 실행 환경을 제공해주고 있다.

 

즉, 언리얼 엔진에서 메모리 관리는 '언리얼 스마트 포인터 라이브러리''가비지 컬렉션'을 통해 이루어지고 있다.

아쉽게도 모든 클래스에서 UPROPERTY 매크로를 붙인다고 자동으로 GC를 통해 메모리 관리가 되지 않는다.

그렇기 때문에 네이티브 C++을 사용하는 상황에서는 스마트 포인터를 활용하고 UObject에 대해서는 UPROPERTY를 통한 GC를 활용하는 것이 바람직하다.

 

하지만 특정 매크로와 규칙들을 통해 C++ 클래스를 UObject로 만들 수 있다.

해당 규칙들은 아래와 같다.

  • 클래스 선언 매크로: 해당 클래스가 언리얼 오브젝트임을 선언하기 위한 매크로. 클래스 선언 윗줄에 UCLASS라는 매크로를 선언하고 클래스 내부에는 GENERATED_BODY 매크로를 선언한다.
  • 클래스 이름 접두사: 언리얼 오브젝트에서는 항상 규칙에 맞게 접두사가 붙어야 한다.
    U와 A가 존재하는데 A는 액터 클래스에 사용하고 U는 액터가 아닌 클래스에 사용한다.
  • generated.h 헤더파일: 소스 코드를 컴파일하기 이전에 언리얼 헤더 툴이라는 도구를 사용해 클래스 선언을 분석하고 언리얼 실행 환경에 필요한 부가 정보를 별도의 파일에 생성한다. 이 때 자동으로 생성되는 파일이 generated.h이다. 컴파일 과정에서 필연적으로 생성되기 때문에 언리얼 오브젝트 선언의 마지막 #include 구문에 이 헤더 파일을 반드시 선언해야 한다.
  • 외부 모듈에의 공개 여부: 윈도우의 DLL 시스템은 DLL 내 클래스 정보를 외부에 공개할지 결정하는 declspec(dllexport)라는 키워드를 제공한다. 언리얼 엔진에서 이 키워드를 사용하려면 ‘모듈명_API’라는 키워드를 클래스 선언 앞에 추가한다.

이러한 규칙들이 적용된 예시

 

액터를 생성하면 위와 같이 UObject가 규칙이 적용되어 생성되니까 한번 확인해보는 것도 좋다.