[UE5] 이득우 Part 1. 14강 언리얼 오브젝트 관리Ⅱ - 패키지

공부 스파이럴·2023년 12월 28일
0

언리얼 오브젝트 패키지

  • 단일 언리얼 정보는 직렬화를 통해서 저장가능
  • 복잡한 계층 구조를 가진 언리얼 오브젝트를 효과적으로 저장, 불러오기 방법을 통일
    • UPackage 단위로 언리얼 오브젝트를 관리
  • 패키지의 중의적 개념
    • 다양한 곳에서 패키지 단어를 사용
    1. 언리얼 오브젝트를 감싼 포장 오브젝트
    2. 개발된 최종 콘텐츠를 정리해 프로그램으로 만드는 작업
    3. DLC와 같이 확장 콘텐츠에 사용되는 별도의 데이터 묶음 의미 (pkg 파일)

패키지(Package)와 에셋(Asset)

  • UObject 패키지는 다수의 UObject를 포장하는데 사용하는 UObject
    • 모든 UObject는 패키지에 소속되어 있음
  • UObject 패키지의 서브 오브젝트를 에셋(Asset)이라 함
  • 패키지는 다수의 에셋을 소유할 수 있지만, 일반적으로 하나의 에셋만 가짐
  • 에셋은 다수의 서브 오브젝트를 가지며 패키지에 포함됨
    • 에셋은 에디터에 노출되나 에셋의 서브 오브젝트는 노출되지 않음

실습

  • 패키지를 사용하기 위해서는 패키지와 패지키가 담고 있는 대표 에셋을 설정
  • /Game 경로: 게임에서 사용되는 에셋들을 모아 놓은 대표 폴더
  • 패키지 경로(이름)과 에셋 이름 설정

  • CreatePackage 를 통해 패키지 생성
  • 패키지 저장 Flag 설정
    • RF_PublicRF_Standalone 을 사용하는 게 일반적
  • 오브젝트를 생성할 때 패키지를 지정할 수 있음
    • 아무것도 지정하지 않으면 Transient Package라는 임시 패키지 안에 UObject가 저장됨
  • 패키지, 클래스 이름, 에셋 이름, Flag를 지정해서 생성
UPackage* 패키지 = CreatePackage(*패키지이름);
EObjectFlags 플래그 = RF_Public | RF_Standalone;
U오브젝트* 오브젝트 = NewObject<U오브젝트>(
패키지, U오브젝트::StaticClass(), *에셋이름, 플래그);
  • 서브 오브젝트 지정
    • 첫 인자가 Outer
U오브젝트* 서브오브젝트 = NewObject<U오브젝트>(오브젝트, U오브젝트::StaticClass(), *서브오브젝트이름, 플래그);
  • 패키지 저장
    • #include "UObject\SavePackage.h" 추가
    • 패키지 파일 이름 설정
      • FPackageName::LongPackageNameToFilename 이용
      • FPackageName::GetAssetPackageExtension 이용(UASSET 확장자)
    • 플래그 설정
    • UPackage::SavePackage
const FString 패지키파일이름 = FPackageName::LongPackageNameToFilename(패키지이름, FPackageName::GetAssetPackageExtension());
FSavePackageArgs 패키지저장플래그;
패키지저장플래그.TopLevelFlags = 위 플래그 써도 됨;
if (UPackage::SavePackage(패키지, nullptr, *패키지파일이름, 패키지저장플래그))
{
	//...
}
  • 패키지를 저장할 때 이미 패키지가 있다면 다 로딩하고 저장하는 것이 안전
    • 처음에 로딩해두고 진행
UPackage* 패키지 = ::LoadPackage(nullptr, *패키지이름, LOAD_None);
if (패키지)
{
	패키지->FullyLoad();
}
  • 패키지 불러오기

에셋 정보의 저장과 로딩 전략

  • 에셋과의 연결 작업이 빈번함
    • 에셋과 연결할 때 마다 직접 패키지를 불러서 동적으로 메모리 할당하는 작업은 부하가 큼
  • 에디터는 에셋을 직접 로딩하지 않고 패키지와 오브젝트를 지정한 문자열을 대체해서 사용
    • 오브젝트 경로라고 함
    • 프로젝트 내에서 오브젝트 경로 값은 유일함을 보장
    • 오브젝트 간의 연결은 오브젝트 경로 값으로 기록될 수 있음
    • 오브젝트 경로를 사용해 다양한 방법으로 에셋을 로딩할 수 있음
  • 로딩 전략
    • 프로젝트에서 에셋이 반드시 필요한 경우
      • 생성자 코드에서 미리 로딩
    • 런타임에서 필요한 때 바로 로딩하는 경우
      • 런타임 로직에서 정적 로딩
      • 정적 로딩은 다른 프로세스의 실행을 막음 (게임이 멈춤)
    • 런타임에서 비동기적으로 로딩하는 경우
      • 런타임 로직에서 관리자를 사용해 비동기 로딩

오브젝트 경로

  • 패키지 이름과 에셋 이름을 묶은 문자열
  • 에셋 클래스 정보는 생략 가능
  • 필요한 에셋만 로드할 수 있음
  • {에셋클래스정보}'{패키지이름}.{에셋이름}' 또는
  • {패키지이름}.{에셋이름}

에셋 참조

https://docs.unrealengine.com/5.3/ko/referencing-assets-in-unreal-engine/

직접 프로퍼티 참조

  • 타입을 명시적으로 지정

생성 시간 참조

  • 강 참조를 진행할 때 해당 오브젝트가 가리키고 있는 에셋을 생성자 코드에서 생성
    • 생성자 코드는 엔진이 초기화될 때 실행
    • 즉, 게임이 실행되기 전에 해당 에셋이 로딩

간접 프로퍼티 참조

  • TSoftObjectPtr 을 사용
  • LoadObject<>() 메서드나 StaticLoadObject() 나 FStreamingManager 를 사용하여 오브젝트를 로드할 수 있음
  • FSoftObjectPath 가 오브젝트 경로를 의미
    • 이 값을 이용해 스트리밍 매니저를 사용해 비동기적이나 동기적으로 에셋을 로딩

오브젝트 검색/로드

  • 생성 또는 로드된 오브젝트: FindObject
  • 로드되지 않은 오브젝트: LoadObject

에셋 스트리밍 관리자(Streamable Manager)

  • 에셋의 비동기 로딩을 지원하는 관리자 객체
  • 콘텐츠 제작과 무관한 싱글톤 클래스에 FStreamableManager를 선언해두면 좋음
    • 예) GameInstance
  • FStreamableManager를 활용해 에셋의 동기/비동기 로딩 관리 가능
  • 다수의 오브젝트 경로를 입력해 다수의 에셋을 로딩하는 것도 가능

실습

  • LoadObject<> 와 오브젝트 경로를 통해서 로딩

  • 생성자에서 로딩
  • ConstructorHelpers::FObjectFinder<> 를 이용
  • Succeeded 를 통해 로딩이 됐는지 확인 가능
  • .Object 로 접근해서 사용 가능
  • 생성자에서 로딩하는 경우 에셋이 반드시 있다는 가정하에 진행 됨
    • 없으면 시작할 때 강력한 경고와 에러 메시지가 나옴

  • 비동기 로딩
  • #include "Engine/StreamableManager.h" 헤더에 인클루드
  • FStreamableManager StreamableManager 멤버변수
    • 포인터가 아니라 인클루드해줘야함
  • TSharedPtr< FStreamableHandle > Handle 멤버변수
  • StreamableManager.RequestAsyncLoad 를 이용
    • 오브젝트 경로와 람다 함수 이용
Handle = StreamableManager.RequestAsyncLoad(경로,
	[&]()
	{
		if (Handle.IsValid() && Handle->HasLoadCompleted())
		{
			U오브젝트* 오브젝트 = Cast<U오브젝트>(Handle->GetLoadedAsset());
			if (오브젝트)
			{
            	//...
				Handle->ReleaseHandle();
				Handle.Reset();
			}
		}
	}
);

0개의 댓글