언리얼 엔진의 근간 : 리플렉션 시스템
언리얼 오브젝트 특징 : 리플렉션
언리얼은 크게 2가지 오브젝트로 나뉜다고 했다. C++ 오브젝트, 언리얼 오브젝트
그 중, 언리얼 오브젝트는 기존 C++에는 없는 모던 객체 지향 시스템의 특징인 생산성을 추가한 기능이다.
그게 바로 리플렉션 (Reflection)
리플렉션 (Reflection)
리플렉션은 프로퍼티 시스템 이라고도 불리며, 프로그램이 실행시간에 자기 자신을 조사하는 기능.
('자기자신' = 에디터, 가비지 콜렉션, 클래스, 구조체, 함수, 멤버 변수, 열거형 등)
리플렉션 설정 방법
(설정방법 이라기 보단 리플렉션 기능을 수행, 리플렉션을 보장한다고 알려준다는게 더 이해하기 쉬울 것 같다.)
리플렉션은 Unreal Header Tool (UDT) 를 통해 해당 프로젝트가 컴파일될 때 정보를 수집해서 조사하게 된다.
그럼 해당 프로젝트가 리플렉션 시스템이 필요하다는 걸 어떻게 알려주냐,
위 같은 헤더를 추가하면 된다. 그러면,
USTRUCT() // 구조체
UCLASS() // 클래스
UFUNCTION() // 함수
UENUM() // 열거형
UPROPERTY() // 멤버변수
이런 매크로를 사용할 수 있게되고, 이를 통해 리플렉션 관련 소스코드를 알아서 추가해준다.
리플렉션 작동 순서
1. UBT가 리플렉션을 탐색
2. UHT가 탐색된 리플렉션이 있는 cpp를 파싱(parsing : 코드 분석 = 코드에 맞춰 구조를 결정, 설계)
3. 리플렉션 데이터 정보를 수집
4. 수집한 정보를 새로운 cpp, generated.h에 저장
5. 빌스 시 기존 코드에 generated.h코드를 추가해서 컴파일
Unreal Build Tool(UBT)는 헤더를 스캔해서 리플렉션 시스템이 있는 프로젝트를 기억한다.
그 후, 변경된 프로젝트인 경우, UHT를 이용해 리플렉션 데이터를 업데이트한다.
UHT는 헤더파싱, 리플렉션 데이터가 있는 cpp코드 생성, 다양한 헬퍼, 함수를 생성한다.
언리얼 오브젝트 구성
언리얼 엔진에서는 2가지 객체를 사용한다. 그 중, 언리얼c++객체는
- U 혹은 그 외 접두사를 붙여준다.
- 언리얼 오브젝트에는 항상 클래스 정보를 담은 UClass가 매칭되어 있다.
- UClass로부터 언리얼 오브젝트의 정보를 파악할 수 있음 → 리플렉션 시스템 구성됨 → UProperty, UFunction등의 매크로를 사용할 수 있다.
- GetClass() : UCLASS를 사용해서 자신이 가진 프로퍼티와 함수 정보를 런타임 타임에서 조회
- StaticClass() : UCLASS를 사용해서 자신이 가진 프로퍼티와 함수 정보를 컴파일 타임에서 조회
- CDO(Class Default Object) : 언리얼 오브젝트가 가진 기본값을 저장하는 템플릿 객체, UCLASS당 1개의 CDO가 생성된다.
- GetDefaultObject() : CDO에 접근하는 함수 (기본값을 가져올 때 사용한다.)
캐릭터 구조 구현
언리얼오브젝트를 이용해서 캐릭터클래스안에 있는 직업군을 구조화해보려고 한다.
캐릭터 클래스, (GameInstance)
- public 과 private 선언은 가장먼저, 기본적으로 해주기
- 18줄 UMyGameInstance()로 생성자를 만들어준다. 이게 곧 CDO가 된다.
- 23줄 UPROPERTY() 매크로로 매개변수 CharacterClassName을 언리얼엔진이 관리할 수 있도록 만든다.
- 6줄 UMyGameInstance::UMyGameInstance() : CDO가 될 내용으로 기본값을 넣는다.
- 17, 18줄 : 각각 GetClass와 StaticClass를 통해 런타임, 컴파일타임동안 UCLASS 정보를 가져온다.
- 20줄~ 검증코드 : 약간 파이썬에서 어디서 오류나는지 print문 쓰는것 처럼 문제 발생시 작동하는 코드
- check() : 문제 발생 시 엔진 꺼짐
- ensure() : 문제 발생 시 엔진 안꺼짐, 에디터에 원인 알려줌
- ensureMsgf() : 문제 발생 시 엔진 안꺼짐, 에디터에 원인 알려줌, TEXT로 문자열 호출 가능
- 28줄 : CDO에 접근하기 *GetClass()→GetDefaultObject<클래스>()→멤버변수
GameCharacter(UObject)
Character은 기본적으로 오브젝트에 있을거같긴했는데 진짜있어서.. GameCharacter로 변경
- Getter Setter함수 구조 유의
- protected레벨에서 각각의 매개변수에는 각각 UPROPERTY()를 붙여줘야한다.
- 가상함수 PlayGame을 만들고, 이것또한 UFUNCTION()을 붙여서 UCLASS에서 확인되도록 설정한다.
- 생성자에 닉네임Name, 아이템레벨Level을 넣는다. 항상 말하지만 이건 기본값이다.
- 가상함수 PlayGame()관련 내용 작성
- Getter Setter은 간단히 작성
Artist(UObject), Aeromancer(UObject)
- 헤더에서 주의 : ~.generated.h는 항상 가장 마지막에 작성해야한다.
- GameCharacter 에서 선언한 가상 함수 PlayGame을 override한다.
- private에는 캐릭터 클래스의 여러직업 중 이 직업만 사용하는 스킬 변수를 선언했다. 당연히 변수위에는 UPROPERTY()를 작성해야 UCLASS를 통해 접근할 수 있다.
- 생성자에서 해당 직업의 닉네임, 아이템레벨, 스킬이름 지정
- PlayGame은 GameCharacter에서 상속받은 함수이므로 Supper::사용해서 실행
- CDO(여기서는 UArtist에 해당하는 내용)를 고칠 경우 에디터를 껐다 켜야한다.(헤더 고칠때처럼)
→ CDO는 에디터가 활성화 되기 이전에 초기화되기 때문에
이와 동일하게 Aeromancer를 구성한다.
MyGameInstance Init()에서 실행하기
GameCharacter, Artist, Aeromancer에서 구성한 내용을 Init에서 실행해볼 수 있다.
헤더를 참조해서 해당클래스를 만들거기때문에 MyGameInstance.cpp에 Artist.h와 Aeromancer.h를 추가하는데,
현재 내가 정의하고 있는 cpp파일의 헤더가 가장 위에 있어야 한다.
먼저 이렇게 NewObject<>()로 언리얼 C++ 오브젝트를 생성해야한다.
일반적인 C++ 오브젝트는 new 를 쓰지만, 언리얼오브젝트는 NewObject를 사용한다.
1. MyGameInstance에서 Artist와 Aeromancer의 정보 가져오기
1) Getter, Setter 사용
Artist의 Name을 아가짱으로 변경하고, 이를 GetName으로 가져오는 코드이다.
2) 리플렉션 시스템 사용
기존 Name을 받을 변수와 새로운 Name을 받을 변수를 각각 선언하고,
FProperty로 Name이라는 프로퍼티가 있는지 확인한다. (NameProp)
그리고, GetValue_InContainer으로 해당 프로퍼티에 있는 값을 아까 만든 변수에 저장한다.
SetValue_InContainer으로 새로운 Name 변수값을 넣어준다.
2. MyGameInstance에서 함수 호출하기
1) 일반 클래스에서 함수 가져오기
2) 리플렉션을 사용해서 함수 가져오기
런타임중 동작중인 객체의 UFUNCTION 프로퍼티 중 이름이 "PlayGame"인 함수의 포인터를 가져와서 존재하면,
ProcessEvent를 사용해서 함수를 실행
'하고싶은거 > Unreal' 카테고리의 다른 글
Part1 08. 언리얼 C++ 설계 : 컴포지션 (0) | 2024.03.10 |
---|---|
Part1 07. 언리얼 C++ 설계 : 인터페이스 (0) | 2024.03.09 |
Part1 04. 언리얼 오브젝트 (0) | 2024.03.03 |
Part1 03. 기존 C++과 다른 언리얼 C++ (0) | 2024.03.02 |
Part1 02. 언리얼C++ 코딩규칙 (2) | 2024.02.29 |
#개발 #게임 #일상
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!