UE - 언리얼 C++ 공부하며 배운 것들
C++
오늘 공부하면서 배운 것들.
모듈
특정 기능으로 독립적으로 구현하고 관리할 수 있도록 설계된 코드 단위
특징
- 독립성 : 특정 기능을 독립적으로 처리.
- 재사용성 : 한 번 작성된 모듈은 다른 프로젝트나 시스템에서도 재사용 가능.
- 의존성 관리 : 각 모듈은 필요한 다른 모듈과의 관계를 정의하여 관리.
- 확장성 : 새로운 기능을 추가할 때 기존 코드와 충돌하지 않고 손쉽게 확장 가능.
- 빌드 최적화 : 독립적인 빌드가 가능해, 빌드 시간이 단축.
- 의존성
- 소프트웨어 개발에서 하나의 모듈, 라이브러리, 코드가 다른 코드나 모듈에 의존하는 관계
언리얼 엔진에선 모듈 간 의존성을 정의하지 않으면 다른 모듈의 기능이나 클래스에 접근할 수 없기에, 모듈 의존성을 정의해야한다.
의존성을 추가하지 않은 경우 헤더 파일을 찾을 수 없거나, 링킹 문제가 발생할 수 도 있다.
모듈은 독립적인데 왜 의존성이 필요하나?
모듈의 독립성은 다른 모듈과 상호작용하지 않는다는 의미가 아닌, 모듈이 자신의 역할에 집중하며 별도로 관리 가능하다는 뜻이다.
여기서 의존성은 모듈 간의 협력 관계를 정의하는 것으로, 필요한 명시적으로 설정해 독립성을 유지하면 상호작용을 가능하게 한다.
물론 의존성이 있기에 모든 모듈이 독립적으로 호출 가능하지 않다.
예를 들어 Core와 같은 기본 모듈은 독립적으로 호출이 가능하지만, Engine 모듈은 Core 와 CoreUObject 모듈에 의존한다. Physics 모듈은 Engine 모듈을 필요로 한다.
PCH : Precompiled Headers
명시적으로 지정된 pch를 사용하거나 모듈 간 공유 가능한 pch를 활성화한다.
PublicDependencyModuleNames 함수
이 모듈을 다른 모듈에서 사용할 때, 명시된 의존성을 자동으로 상속한다. Basis를 사용하는 모듈도 Core와 Begine의 기능을 사용 가능
PrivateDependencyModuleNames 함수
이 모듈 내부에서만 사용하는 의존성 Slate를 private에 의존성을 선언하면 Basis 내부에선 Slate UI를 사용 가능하지만 Basis를 사용하는 모듈은 Slate 기능을 사용 못한다.
객체 생성
CreateDefualtSubObject<>()
언리얼 엔진의 AActor 클래스에서 하위 객체를 생성하는 데 사용되는 유틸리티 함수
팩토리 메서드 패턴과 유사한 점
- 객체 생성 캡슐화
- 하위 클래스의 타입을 기준으로 객체 생성
- 사용자가 객체 생성 방식을 몰라도 동작.
하지만 런타임에 객체를 생성하기 위한 함수가 아니기에, 생성자에서만 호출이 가능하다.
NewObject<>()
이 함수는 어디서든 호출이 가능하다.
UObject를 기반으로 한 객체 생성 방식이다.
SpawnActor<>()
게임 월드에서 액터를 동적으로 생성할 때 사용한다.
런타임에 AActor 파생 클래스의 객체를 생성한다.
월드에 등록되고, 트랜스폼을 설정해야한다.
함수 용도 호출 시점 등록 여부 CreateDefaultSubobject<> 기본 서브오브젝트 생성 (액터 생성 시 항상 존재해야 하는 컴포넌트) 생성자에서 호출 자동으로 하위 객체로 등록 NewObject<> 런타임에 동적으로 UObject 기반 객체 생성 어디서든 호출 가능 Outer를 설정하면 생명 주기 연동 SpawnActor<> 런타임에 AActor를 동적으로 생성 런타임 월드에 자동 등록
Kismet
UE3에서 사용되던 비주얼 스크립팅 도구의 이름이었다. 지금은 블루프린트가 그 자리를 대체했고, 지금은 Kismet의 라이브러리만 남아 있다.
Kismet Math Library, kismet system library 등 다양한 라이브러리가 있다.
TObjectPtr
기존의 UObject* 포인터를 대체하기 위한 포인터 래퍼 클래스다. 가비지 컬렉션과 연동되어 객체 참조를 보다 안전하게 관리한다.
기존의 UObject는 포인터를 추적하기 위해 TArray<UObject*>로 구성된 포인터 풀을 사용했는데, 포인터가 많은 경우 성능 문제가 있다.
또한 가비지 컬렉션에게 처리된 UObject 포인터를 참조할 수 있는 문제도 있다.
TObjectPtr은 내부적으로 간접 참조를 통해 포인터를 관리한다.
포인터가 직접적으로 객체를 가리키는 대신, 인덱스와 내부 테이블을 사용하여 객체를 간접적으로 참조한다.
또한 삭제된 객체에 대한 참조를 자동으로 무효화한다.
기존의 스마트 포인터처럼 동작한다고 생각한다. 대신 스마트 포인터처럼 참조 수를 관리하거나 객체를 자동으로 삭제하지는 않는다.
TSubclassOf<>
선언한 클래스 및 자식 클래스를 참조할 수 있는 템플릿 클래스다. 즉, 실제 인스턴스를 생성하는 것이 아니라 특정 클래스를 참조할 때 사용하는 타입이다.
1
2
3
4
TSubclassOf<AWeapon> WeaponClass;
WeaponClass = ARifle::StaticClass(); // ARifle을 참조
AWeapon* WeaponActor = GetWorld()->SpawnActor<AWeapon>(WeaponClass, SpawnLocation, SpawnRotation);
// WeaponClass를 바탕으로 SpawnActor가 실제 인스턴스를 생성.
블루프린트와 연동하기 좋다.
generate.h
헤더 파일에 include 할 때, 무조건 generate.h 파일을 마지막에 배치해야 된다.
언리얼은 리플렉션 시스템을 사용해 클래스 메타 데이터, 프로퍼티, 함수 등을 관리하고,
이를 통해 블루프린트와 에디터에서 클래스와 객체를 동적으로 다룰 수 있다.
이 generate 파일이 메타데이터, 리플렉션 정보, 시리얼라이제이션 등을 처리하는 데 사용하고, 이 과정은 클래스 선언이 끝난 후여야 하기 때문에 마지막에 배치해야 한다.