내일배움캠프 34일차 - 언리얼 엔진 C++ 심화 1 : 리플렉션/UHT/UBT/CDO
UPROPERTY() / UFUNCTION() / UCLASS() 매크로
리플렉션을 위한 매크로.
해당 매크로를 등록함으로써 언리얼 리플렉션 시스템에 해당 변수/함수/클래스를 등록하여 블루프린트와 C++의 다리 역할을 해준다.
※ 리플렉션이란?
: 실행 중에 프로그램이 자기 자신의 구조를 인지하고 조작할 수 있는 능력
→ C++로 작성된 코드를 언리얼 엔진이 인지하고 조작할 수 있는 기능
리플렉션 핵심 매크로
- UPROPERTY() : 변수를 에디터에 노출
- UFUNCTION() : 함수를 블루프린트에 연결
- UCLASS() : 클래스를 언리얼에 등록
메타데이터 목록 (매크로 안에 넣는 매개변수)
- 공통: Category=""
- 함수: BlueprintCallable
- 변수: BlueprintReadWrite, BlueprintReadOnly,
EditAnywhere, VisibleAnywhere
| 메타데이터: 사진의 촬영정보처럼 숨겨진 세부정보를 보관 |
리플렉션의 주요 역할
- 런타임 타입 정보 확인: IsA() 함수로 객체 타입 실시간 확인
- 에디터 연동: UPROPERTY 변수가 디테일 창에 자동 표시
- 가비지 컬렉션 지원: 객체 참조 관계 추적으로 메모리 자동 관리
※ 리플렉션이 없다면?
: 언리얼 내에서 C++로 작성된 변수/함수/클래스에 접근할 수 없다.
UPROPERTY를 쓰지 않아도 되는 경우
1) UObject가 아닌 타입→ GC는 UObject계열만 관리함, 이런 경우 주로 C++에서 서로 주고받음
2) 월드나 액터차원에서 해당 실제 오브젝트를 소유할 경우
→ 배치되었거나 스폰되었으면 GC에 회수되지 않음
포인터로 가리킨 UObject제거방법
가비지 컬렉션(GC)의 동작 원리
표준 C++ vs 언리얼 엔진
표준 C++: new/delete 직접 관리 필요
- 메모리 누수 위험
- dangling 포인터 문제
언리얼 엔진: GC가 자동 관리
- 안전한 메모리 해제
- 개발자 실수 방지
| GC의 작동 매커니즘. UPROPERTY, UCLASS 로 연결되어있지 않은 고립객체는 삭제한다. |
| UPROPERTY()를 붙이지 않은 변수는 원치 않아도 GC 작동주기에 삭제된다. |
GC 작동 시 주의점
이때, 삭제된 객체를 참조하는 포인터에서 문제가 발생한다.
| Danger객체가 GC에 의해 삭제되었음에도, IsValid()로 존재유무를 확인 시 True로 뜬다. |
| IsValidLowLevel()를 통한 체크. GC에 삭제된 객체는 False를 반환한다. |
UObject용 스마트 포인터
- TObjectPtr: UObject에 강한참조를 하는 스마트 포인터
- TWeakObjectPtr: 약한참조를 하는 스마트 포인터 ← 댕글링 포인터 방지
: 적 유닛(Target)을 저장할 때, 적 유닛이 소멸될 때 null 초기화를 위해 사용한다.
- TSubclassOf: <지정한 클래스>의 파생클래스만을 참조함
※ 언리얼 내에서 GC가 관리하는 클래스는 UObject 파생클래스 한정이다.UBT (언리얼 빌더 툴)의 기능
빌드 프로세스의 첫 번째 관문.
UBT의 핵심 역할
- 가장 먼저 실행되는 빌드 도구
- 전체 프로젝트 구조 파악
- 플랫폼별 환경 설정
주요 기능들
- 플러그인 및 모듈 검색
- .Target.cs 파일 분석
- .Build.cs 파일 처리
- 플랫폼 호환성 확인
- 개발 환경 자동 구성
| UBT는 실행 시 플랫폼에 맞는 환경설정을 적용한다. |
UHT(언리얼 헤더 툴)와 메타데이터
컴파일 전 마법사 - 코드를 에디터가 이해할 수 있게 번역한다.
| UHT는 메타데이터를 에디터에 적용시킨다. |
- UCLASS, UPROPERTY, UFUNTION 메타데이터 수집
: 에디터 디테일 창 연동 정보 생성 (리플렉션)
: 블루프린트 노드 연결 정보 준비 (리플렉션)
- MyObject.generated.h 파일 자동 생성
→ 결과물: MyObject.generated.h
전체 빌드 타임라인과 라이브코딩
클래스 내의 GENERATED_BODY()를 통해 일련의 과정을 거친다.이때, 빌드 시에만 UHT(언리얼 헤더 툴)이 작용하여 메타데이터를 수집하므로,
변경된 헤더의 메타데이터를 에디터에 적용하려면 에디터를 재시작하는 것이 좋다.
| 빌드 시에 UHT를 처리하므로, 헤더파일의 변경으로 메타데이터가 수정되었다면 리빌드가 필요하다. |
CDO (Class Default Object)의 개념
UObject 클래스마다 하나씩 존재하는 기본값 보관소를 CDO라 한다.
한 UObject 클래스의 모든 인스턴스의 기본값은 해당 클래스의 CDO로부터 비롯된다.
| 붕어빵 틀(CDO)내에서 정해진 값의 붕어빵(인스턴스 오브젝트)이 생산된다. |
생성 시점
: 모듈 로딩 단계에서 생성
: UBT → UHT → 컴파일 → 엔진 실행(CDO 실행)
: 생성자 딱 한번만 호출
CDO 초기화 및 복제(Serialization)
생성자에서 초기화하는 이유
: 헤더 수정 = 전체 리빌드 필요 (충돌우려)
: CDO는 모듈 로딩 시 한번만 생성
: 기본값 설정의 중요성
| 생성자에서 변수값들을 초기화한다. |
| CDO는 객체를 직접 생성하는 대신, 메모리째로 복사하여 최적화하는 방법을 택한다. |
CDO를 사용한 생성: 클래스째로 메모리 복사 → 빠른 생성
CDO를 활용한 최적화 원리
성능과 효율성을 극대화하는 세 가지 핵심 원리
1) 메모리 절약
: 공통 기본 값 CDO 하나에만 저장
→ 여러 인스턴스가 같은 CDO 참조
2) 빠른 초기화
: 런타임 타임 체크 즉각 가능
→ 생성자 호출 없이 메모리 복사
3) 델타 직렬화
→ 파일 용량, 네트워크 효율 극대화
※ CDO가 없다면?
: 생성자 매번 실행
: 전체 데이터 저장/전송
→ 성능 저하
댓글
댓글 쓰기