Study/TIL(Today I Learned)

24.10.21 UE5

에린_1 2024. 10. 22. 00:07
728x90

UE5

언리얼 오브젝트 리플렉션

언리얼 리플렉션 시스템

  • 프로그램이 실행시간에 자기 자신을 조사하는 기능이다.
  • C++는 어떠한 형태의 리플렉션을 지원하지 않아, 언리얼에서 자체적으로 C++ 클래스, 구조체, 함수, 멤버 변수 , 열거형 관련 정보를 수집, 질의, 조작하는 별도의 시스템이 구축되어 있다. 전형적으로 이러한 리플렉션은 ‘프로퍼티 시스템’이라고 부른다.
  • 리플리케이션 시스템은 옵션이다. 리플렉션 시스템이 보이도록 했으면 하는 유형이나 프로퍼티에 주석을 달아주면, Unreal Header Tool(UHT)가 그 프로젝트를 컴파일 할 떄 해당 정보를 수집한다.

리플렉션 데이터 사용하기

  • 대부분의 게임 코드는 실행 시간에 프로퍼티 시스템을, 그 장점을 십분 활용하여 무시할 수 있지만, 코드를 작성하거나 게임플레이 시스템을 빌드할 떄는 유용할 수 있다.
  • 프로퍼티 시스템에 대한 유형 계층구조
    • UField
    • UStruct
    • UClass
    • UScriptStruct
    • UFunction
    • UEnum
    • UProperty

언리얼 오브젝트 처리

  • UObject 시스템의 기능에 대한 개요이다.
  • 클래스, 프로퍼티, 함수에 적합한 매크로로 마킹해 주면 UClass, UProperty, UFunction 으로 변한다. 그러면 언리얼 엔진이 접근할 수 있어 ,다수의 내부적인 처리 기능을 구현할 수 있다.

자동 프로퍼티 초기화

  • UObject는 생성자 호출 전 초기화시 자동으로 0으로 채워진다. 클래스, UProperty, 네이티브 멤버 모두에게 전체적으로 벌어지는 일이다. 그 이후 멤버는 클래스 생성자의 커스텀 값으로 초기화 가능하다.

레퍼런스 자동 업데이트

  • AActor 또는 UActorComponent 가 소멸되거나 다른 식으로 플레이에서 제거되면, 리플렉션 시스템에 보이고 있는 그에 대한 모든 (TArray 같은 언리얼 엔진 컨테이너 클래스에 저장된 포인터와 UProperty 포인터 등의) 레퍼런스는 자동으로 null 이 된다. 이는 허상 참조를 예방하여 문제의 소지를 줄인다는 장점이 있지만, 다른 코드 부분에서 AActor 와 UActorComponent 포인터를 소멸시키는 경우 null 이 된다는 것을 뜻하기도 한다.
  • 여기서 최고의 장점은 null 검사 안정성이 높다는 것인데, 일반적인 null 포인터의 경우와 null 이 아닌 포인터가 삭제된 메모리를 가리키는 경우 둘 다 감지해 내기 때문이다.
  • 여기서 한 가지 중요한 점은, 이 기능은 UPROPERTY 로 마킹되어 있거나 언리얼 엔진 컨테이너 클래스에 저장된 UActorComponent 또는 AActor 레퍼런스에만 적용된다는 점이다. raw 포인터에 저장된 오브젝트 레퍼런스는 언리얼 엔진이 알지 못하기 때문에, 자동으로 null 되거나, 가비지 컬렉션이 방지되지 않는다.

Serialization

  • UObject 가 Serialize 될 때, 모든 UProperty 값은 명시적으로 "transient" (휘발성) 마킹 또는 생성자 이후 기본 값에서 미변경 상태가 아닌 이상 자동으로 읽고 쓰기 가능하다. 예를 들어 레벨에 AEnemy 인스턴스를 배치하고서, 그 Health 를 500 으로 설정하고 저장을 하면, UClass 정의 이외의 코드를 한 줄도 작성하지 않고도 다시 로드할 수 있다.
  • UProperty 가 추가 또는 제거될 때, 기존 콘텐츠 로드는 매끄럽게 처리된다. 새 프로퍼티는 새 CDO 에서 기본값을 복사해 온다. 제거된 프로퍼티는 말없이 무시된다.
  • 커스텀 작동 방식이 필요한 경우, UObject::Serialize 함수를 덮어쓰면 된다.

프로퍼티 값 업데이트 하기

  • UClass 의 클래스 디폴트 오브젝트 (CDO) 가 변경되면, 엔진은 그 클래스의 모든 인스턴스 로드시 알아서 변경사항을 적용 시도한다. 주어진 오브젝트 인스턴스에 대해, 업데이트된 변수 값이 이전 CDO 값과 일치한다면, 새로운 CDO 에 저장된 값으로 업데이트된다.

에디터 통합

  • UObject 와 UProperty 는 에디터에 인식되며, 에디터는 별도의 코드를 작성할 필요 없이 이 값을 자동으로 노출시킬 수 있다. 이는 선택적으로 블루프린트 비주얼 스크립팅 시스템으로의 통합(integration)이 가능하다.

런타임 유형 정보 및 형변환

  • UObject 는 언리얼 엔진 리플렉션 시스템의 일부이므로, 항상 자신이 무슨 UClass 인지 알고 있으며, 형변환을 실시간으로 할 수 있다.
  • 네이티브 코드에서, 모든 UObject 클래스에는 그 부모 클래스로 설정된 커스텀 "Super" typedef 가 있어, 덮어쓰기 행위에 대한 제어가 쉽게 가능하다.
  • class AEnemy : public ACharacter { virtual void Speak() { Say("Time to fight!"); } }; class AMegaBoss : public AEnemy { virtual void Speak() { Say("Powering up! "); Super::Speak(); } };
  • 또한, 템플릿 Cast 함수를 사용해서 베이스 클래스에서의 오브젝트를 좀더 파생된 클래스로 안전하게 형변환하거나, IsA 를 사용해서 오브젝트가 특정 클래스의 것인지 질의할 수 있다.
  • class ALegendaryWeapon : public AWeapon { void SlayMegaBoss() { TArray<AEnemy> EnemyList = GetEnemyListFromSomewhere(); // The legendary weapon is only effective against the MegaBoss for (AEnemy Enemy : EnemyList) { AMegaBoss* MegaBoss = Cast<AMegaBoss>(Enemy); if (MegaBoss) { Incinerate(MegaBoss); } } } };

가비지 컬렉션

  • 언리얼에서는 더이상 참조되지 않거나 명시적으로 소멸 예약시킨 UObject 를 주기적으로 정리하는 가비지 컬렉션(garbage collection) 스키마를 사용한다. 엔진에서는 레퍼런스 그래프를 만들어 어느 오브젝트가 아직 사용중이고 어느 것이 고아가 되었는지를 알아낸다.
  • 이 그래프 루트에는 "루트 세트"라 지정된 오브젝트 세트가 있다. 어떤 오브젝트도 루트 세트에 추가시킬 수 있다. 가비지 컬렉션이 발생하면, 엔진은 루트 세트부터 시작해서 알려진 UObject 레퍼런스 트리를 검색하여 참조된 오브젝트를 전부 추적할 수 있다. 참조되지 않은 오브젝트, 즉 트리 검색에서 찾지 못한 것들은 더이상 필요치 않은 오브젝트라 가정하고 제거한다.
  • 언리얼 엔진 4 의 가비지 컬렉션은 빠르고 효율적이며, 걸리는 부하를 최소화시키기 위한 기능이 다수 내장되어 있다. 프로젝트의 가비지 컬렉터 퍼포먼스 튜닝에 흔히 사용되는 세팅은 다음과 같다.

세팅 기능 설명

Create Garbage Collector UObject Clusters 가비지 컬렉터 UObject 클러스터 생성 - 프로젝트 세팅에서 켜거나 끌 수 있습니다 (기본으로 켜져 있습니다). 켜면 관련된 오브젝트들을 하나의 가비지 컬렉션 클러스터에 묶어, 오브젝트 각각이 아닌 클러스터 하나만 검사할 수 있도록 해놨습니다. 즉 전체 클러스터를 하나의 오브젝트로 취급할 수 있기에 도달가능성 파악이 빨라지지만, 클러스터의 개별 항목 전부를 같은 프레임에 언해시 및 삭제 준비를 하므로, 클러스터가 너무 클 경우 버벅임이 생길 수 있습니다. 일반적인 경우, 클러스터를 만들면 가비지 컬렉션 퍼포먼스가 향상되며 도달가능성 분석에 소요되는 시간이 단축됩니다.
Merge GC Clusters GC 클러스터 병합 - 클러스터 병합을 켜면 한 클러스터의 오브젝트가 다른 클러스터의 오브젝트를 참조할 때 클러스터를 합치도록 합니다. 참고로 병합을 유발시킨 레퍼런스를 지워도 새로 병합된 클러스터는 어떤 식으로든 분해되거나 나뉘지 않습니다. 이 기능의 정상 작동을 위해서는 Create Garbage Collector UObject Clusters 옵션도 켜져있어야 합니다. 그러면 가비지 컬렉터의 언해시 및 오브젝트 소멸 작업 빈도를 낮추지만, 더욱 많은 수의 오브젝트를 한꺼번에 언해시 및 소멸시킵니다. 추가로 클러스터 병합을 하지 않았다면 일어났을 가비지 컬렉션이 일어나지 않는 경우가 있을 수 있는데, 클러스터 내 오브젝트로의 레퍼런스가 있으면 전체 클러스터가 가비지 컬렉션 대상에서 제외되기 때문입니다.
Actor Clustering Enabled 액터 클러스터 활성화 - 프로젝트 세팅 에서 이 옵션을 켜고, bCanBeInCluster 변수를 true 로 설정하거나, 코드에서 CanBeInCluster 함수가 true 를 반환하도록 덮어써 주면 액터를 클러스터에 넣을 수 있습니다. 기본적으로, 스태틱 메시 액터와 리플렉션 캡처 컴포넌트를 제외한 액터 및 컴포넌트에는 이 기능이 꺼져있습니다. 이 기능은 한꺼번에 소멸시킬 것으로 예상되는 액터 그룹을 만드는 데 좋은데, 보통 레벨에 배치한 스태틱 메시가 속한 서브레벨을 언로드하지 않고서는 소멸되지 않도록 하는 것입니다.
Blueprint Clustering Enabled 블루프린트 클러스터 활성화 - 블루프린트의 UBlueprintGeneratedClass 및 관련 데이터, 이를테면 공유 UPROPERTY 및 UFUNCTION 데이터같은 것은, 이 세팅을 켜서 클러스터로 묶을 수 있습니다. 여기서 한 가지 중요한 점은, 이렇게 생긴 클러스터는 블루프린트의 개별 인스턴스가 아닌, Blueprint Generated Class 자체를 참조한다는 점입니다.
Time Between Purging Pending Kill Objects 킬 대기중 오브젝트 제거 간격 - 프로젝트 세팅에서 가비지 컬렉션 발동 빈도를 조절할 수 있습니다. 이 하이 레벨 컨트롤은 특히나 버벅임 방지에 좋습니다. 컬렉션 발동 간격을 줄임으로써, 다음 도달가능성 분석 패스에 걸릴 도달불가 오브젝트 발생 가능성을 낮추고, 동시에 너무 많은 액터를 정리하느라 발생할 수 있는 버벅임도 피할 수 있습니다.
  • CDO를 고쳐주는 생성자 코드를 변경하는 경우 헤더 파일을 고치는 것과 같이 에디터를 꺼줘야 한다.
728x90

'Study > TIL(Today I Learned)' 카테고리의 다른 글

24.10.23 VV  (0) 2024.10.23
24.10.22 VV, UE5  (3) 2024.10.23
24.10.18 UE5  (0) 2024.10.18
24.10.17 CS, UE5  (3) 2024.10.18
24.10.14 게임 서버  (0) 2024.10.14