이번에 다룰 내용은 언리얼에서 사용하는 시스템 방식인 CDO (Class Default Object) 입니다.
https://dev.epicgames.com/documentation/ko-kr/unreal-engine/objects-in-unreal-engine
에픽 게임즈에서 제공하는 해당 문서를 참조하면
UCLASS 매크로는 언리얼 기반 타입을 설명하는 UCLASS 에 대한 레퍼런스를 UObject 에 제공합니다. 각 UCLASS 는 클래스 디폴트 오브젝트(Class Default Object, CDO) 라는 하나의 오브젝트를 유지합니다. CDO는 기본적으로 클래스 생성자에 의해 생성되고 이후에는 수정되지 않는 디폴트 '템플릿' 오브젝트입니다.
라고 작성되어있습니다.
여기서 핵심은 디폴트 템플릿 오브젝트 라는 CDO의 정의입니다.
언리얼에서 클래스가 생성될 때 기본값을 저장하는 각 클래스 별로 존재하는 특별한 객체를 의미합니다.
예시를 들어보겠습니다.
개발자가 AEnemy 클래스를 하나 생성했습니다.
이 클래스를 사용하여 적군을 생성합니다.
SpawnActor<AEnemy>() 를 사용해 적군을 생성했습니다.
이 AEnemy 액터는 생성될 때 AEnemy CDO에 저장된 값을 바탕으로 생성됩니다.
이 이후에 개발자가 CDO에 있는 Enemy의 HP 값을 100에서 200으로 변경했습니다.
이전에 생성된 AEnemy 액터들은 그대로 100의 체력을 가지게 되지만
이후 생성된 액터들은 200의 체력을 가지게 됩니다.
CDO는 기본값을 가지고 설정하는데 왜 변하지 않을까요?
그 이유는 CDO의 값은 초기화 시 복사 되기 때문입니다.
언리얼에서는 CDO를 사용하지 않고 객체를 생성할 수 없도록 해두었습니다.
만약 new AEnemy 같은 방법으로 객체를 생성했다고 가정한다면 해당 객체는 에디터에서 관리하지 못하고 GC도 처리하지 못하며
리플렉션 또한 연결되지 않는 비정상적이며 메모리적으로도 위험한 객체가 생성됩니다.
결국 CDO는 UClass 한개의 프로토타입 인스턴스로 기본값의 원본을 저장합니다.
새로운 객체는 해당 CDO를 복사하여 사용하며 리플렉션, GC, 에디터와의 연결을 가능하게 해줍니다.
CDO의 생성 및 초기화 순서는 다음과 같습니다.
클래스 로드 → CDO 생성 → 클래스 생성자 실행(여기서 디폴트 서브오브젝트 생성) → Config가 있으면 INI 값 로드 → 이후 인스턴스 생성 시 CDO로부터 복사.
이 과정을 거치면서 클래스가 생성되고 그 클래스의 CDO가 생성되어 사용되게 됩니다.
CDO의 역할에 대해서 더 자세히 설명하겠습니다.
CDO는 이전에 말한것 처럼 새로운 객체의 기본값입니다.
액터나 오브젝트가 생성될 때 CDO의 프로퍼티 값을 복사해서 생성됩니다.
결과적으로 클래스 기본값이 인스턴스의 시작값으로 보장됩니다.
에셋의 저장에서도 데이터 중복을 줄일 수 있습니다.
CDO의 차이만 비교하여 저장하면 불필요한 중복을 줄이고 저장할 수 있습니다.
CDO의 데이터는 블루프린트와 에디터에도 연결됩니다.
Class Defaults 창에서 변경하는 값은 CDO의 값입니다.
UPROPERTY(EditDefaultsOnly)는 CDO 에서만 변경이 가능합니다.
CDO는 또한 GC에서 수거되지 않는 객체로 클래스가 생존해있다면 항상 존재합니다.
이를 통해 리플렉션 시스템이 클래스가 어떤 값을 제공하는지 안정적으로 참조가 가능합니다.
CDO의 값을 쓰는게 항상 적절한 것은 아닙니다.
모든 새 인스턴스에 공통된 값을 적용 시켜야 한다면 CDO를 수정하는것이 적절하나 런타임 중에 사용하거나
기존 인스턴스를 변경하기 위해선 별도의 업데이트가 필요합니다.