728x90
반응형
- 정리
- 언리얼 오브젝트 패키지
- 언리얼 오브젝트 패키지 구조의 이해
- 패키지 클래스를 사용한 애셋 데이터의 관리
- 오브젝트 경로의 설계와 이를 활용한 다양한 애셋 로딩 방법의 이해
- 동기
- 비동기
- 언리얼 오브젝트 패키지
- 언리얼 오브젝트 패키지
- 단일 언리얼 오브젝트가 가진 정보는 저장할 수 있지만, 오브젝트들이 조합되어 있다면?
- 저장된 언리얼 오브젝트 데이터를 효과적으로 찾고 관리하는 방법은?
- 복잡한 계층 구조를 가진 언리얼 오브젝트를 효과적으로 저장과 불러들이는 방법을 통일해야 함
- 언리얼 엔진은 이를 위해 패키지(UPackage)단위로 언리얼 오브젝트를 관리함
- 패키지의 중의적 개념
- 언리얼 오브젝트를 감싼 포장 오브젝트를 의미
- 개발된 최종 콘텐츠를 정리해 프로그램으로 만드는 작업 (게임 패키징)
- DLC와 같이 향후 확장 콘텐츠에 사용되는 별도의 데이터 묶음 (pkg 파일)
- 단일 언리얼 오브젝트가 가진 정보는 저장할 수 있지만, 오브젝트들이 조합되어 있다면?
- 패키지(Package)와 애셋(Asset)
- 언리얼 오브젝트 패키지는 다수의 언리얼 오브젝트를 포장하는데 사용하는 언리얼 오브젝트
- 모든 언리얼 오브젝트는 패키지에 소속되어 있음 (예) Transient Package)
- 언리얼 오브젝트 패키지의 서브 오브젝트를 애셋(Asset)이라고 하며 에디터에는 이들이 노출됨
- 구조상 패키지는 다수의 언리얼 오브젝트를 소유할 수 있으나, 일반적으로 하나의 애셋만 가짐
- 애셋은 다시 다수의 서브 오브젝트를 가질 수 있으며, 모두 언리얼 오브젝트 패키지에 포함됨
- 하지만 에디터에는 노출되지 않음
- 언리얼 오브젝트 패키지는 다수의 언리얼 오브젝트를 포장하는데 사용하는 언리얼 오브젝트
const FString UMyGameInstance::PackageName = TEXT("/Game/Student"); // 패키지 이름 지정
const FString UMyGameInstance::AssetName = TEXT("TopStudent"); // 애셋 지정 지정
void UMyGameInstance::SaveStudentPackage() const
{
UPackage* StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
if (StudentPackage)
{
StudentPackage->FullyLoad();
}
StudentPackage = CreatePackage(*PackageName);
EObjectFlags ObjectFlag = RF_Public | RF_Standalone;
UStudent* TopStudent = NewObject<UStudent>(StudentPackage, UStudent::StaticClass(), *AssetName, ObjectFlag);
TopStudent->SetName(TEXT("이득우"));
TopStudent->SetOrder(36);
const int32 NumofSubs = 10;
for (int32 ix = 1; ix <= NumofSubs; ++ix)
{
FString SubObjectName = FString::Printf(TEXT("Student%d"), ix);
UStudent* SubStudent = NewObject<UStudent>(TopStudent, UStudent::StaticClass(), *SubObjectName, ObjectFlag);
SubStudent->SetName(FString::Printf(TEXT("학생%d"), ix));
SubStudent->SetOrder(ix);
}
const FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension()); // 패키지 관련 파일 이름을 설정하는 함수 / GetAssetPackageExtension = uasset
FSavePackageArgs SaveArgs; // 헤더 추가 UObject/SavePackage.h
SaveArgs.TopLevelFlags = ObjectFlag;
if (UPackage::SavePackage(StudentPackage, nullptr, *PackageFileName, SaveArgs)) // 패키지 저장
{
UE_LOG(LogTemp, Log, TEXT("패키지가 성공적으로 저장되었습니다."));
}
}
- MyGameInstance.cpp / 패키지를 생성하고 저장하는 코드
void UMyGameInstance::LoadStudentPackage() const
{
UPackage* StudentPackage = ::LoadPackage(nullptr, *PackageName, LOAD_None);
if (nullptr == StudentPackage) // 패키지가 없다면 if문 실행
{
UE_LOG(LogTemp, Warning, TEXT("패키지를 찾을 수 없습니다."));
return;
}
StudentPackage->FullyLoad(); // 패키지 안에 가지고 있는 에셋을 모두 로딩
// 패키지가 로딩이 되면 로딩된 패키지 안에서 원하는 오브젝트 에셋을 찾을 수 있다.
UStudent* TopStudent = FindObject<UStudent>(StudentPackage, *AssetName);
PrintStudentInfo(TopStudent, TEXT("FindObject Asset"));
}
- MyGameInstance.cpp / 패키지를 로드하는 코드
- 애셋 정보의 저장과 로딩 전략
- 게임 제작 단계에서 애셋 간의 연결 작업을 위해 직접 패키지를 불러 할당하는 작업은 부하가 크다.
- 애셋 로딩 대신 패키지와 오브젝트를 지정한 문자열을 대체해 사용. 이를 오브젝트 경로라 한다.
- 프로젝트 내에 오브젝트 경로 값은 유일함을 보장
- 그렇기에 오브젝트 간의 연결은 오브젝트 경로 값으로 기록될 수 있음
- 오브젝트 경로를 사용해 다양한 방법으로 애셋을 로딩할 수 있음
- 애셋의 로딩 전략
- 프로젝트에서 애셋이 반드시 필요한 경우
- 생성자 코드에서 미리 로딩
- 런타임에서 필요할 때 바로 로딩하는 경우
- 런타임 로직에서 정적 로딩
- 런타임에서 비동기적으로 로딩하는 경우
- 런타임 로직에서 관리자를 사용해 비동기 로딩
- 프로젝트에서 애셋이 반드시 필요한 경우
- 게임 제작 단계에서 애셋 간의 연결 작업을 위해 직접 패키지를 불러 할당하는 작업은 부하가 크다.
- 오브젝트 경로(Object Path)
- 패키지 이름과 애셋 이름을 한 데 묶은 문자열
- 애셋 클래스 정보는 생략할 수 있음
- 패키지 내 데이터를 모두 로드하지 않고 오브젝트 경로를 사용해 필요한 애셋만 로드할 수 있음
- {애셋클래스정보}’{패키지이름}.{애셋이름}’ 또는 {패키지이름}.{애셋이름}
- 애셋 스트리밍 관리자(Streamable Manager)
- 애셋의 비동기 로딩을 지원하는 관리자 객체
- 콘텐츠 제작과 무관한 싱글턴 클래스에 FStreamableManager를 선언해두면 좋음
- GameInstance는 좋은 선택지
- FStreamableManager를 활용해 애셋의 동기/비동기 로딩을 관리할 수 있음
- 다수의 오브젝트 경로를 입력해 다수의 애셋을 로딩하는 것도 가능
UMyGameInstance::UMyGameInstance() // 생성자
{
const FString TopSoftObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName); // 오브젝트 경로
static ConstructorHelpers::FObjectFinder<UStudent> UASSET_TopStudent(*TopSoftObjectPath); // 생성자에서 애셋을 로딩할 때는 LoadObject가 아닌 ConstructorHelpers라는 걸 사용해야 한다.
if (UASSET_TopStudent.Succeeded()) // 성공 여부 체크
{
PrintStudentInfo(UASSET_TopStudent.Object, TEXT("Constructor")); // UASSET_TopStudent.Object로딩된 애셋에 접근 가능
}
}
void UMyGameInstance::LoadStudentObject() const
{
const FString TopSoftObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
UStudent* TopStudent = LoadObject<UStudent>(nullptr, *TopSoftObjectPath);
PrintStudentInfo(TopStudent, TEXT("LoadObject Asset"));
}
- MyGameInstance.cpp
- 생성자에서 언리얼 애셋을 불러올수 있도록
- 생성자 코드에서 지정한 애셋이 존재하지 않는 경우 언리얼을 실행할 때 오류가 발생하게 된다. 반드시 있다는 가정하에 실행되는 것이라
#include "Engine/StreamableManager.h"
FStreamableManager StreamableManager;
TSharedPtr<FStreamableHandle> Handle; // 스트리밍된 오브젝트를 관리하기 위한 핸들
- MyGameInstance.h
const FString TopSoftObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName); // Path
Handle = StreamableManager.RequestAsyncLoad(TopSoftObjectPath,
[&]()
{
if (Handle.IsValid() && Handle->HasLoadCompleted()) // 로딩이 유효한지, 로딩이 끝났는지
{
UStudent* TopStudent = Cast<UStudent>(Handle->GetLoadedAsset()); // 형변환 (UStudent)
if (TopStudent) // 맞다면 출력
{
PrintStudentInfo(TopStudent, TEXT("AsyncLoad"));
Handle->ReleaseHandle(); // 다 쓴 핸들 닫기
Handle.Reset(); // 리셋
}
}
} // 람다 함수
);
- MyGameInstance.cpp
해당 포스트는 인프런의 <이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해>
강의를 수강하고 정리한 내용입니다.
이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해 | 이득우 - 인프런
이득우 | 대기업 현업자들이 수강하는 언리얼 C++ 프로그래밍 전문 과정입니다. 언리얼 엔진 프로그래머라면 게임 개발전에 반드시 알아야 하는 언리얼 C++ 기초에 대해 알려드립니다., [사진] 언....................................
www.inflearn.com
728x90
'공부 > 이득우의 언리얼 프로그래밍' 카테고리의 다른 글
[Study] Part 2 - 언리얼 엔진 게임 제작 기초 (1/15) (0) | 2024.05.16 |
---|---|
[Study] Part 1 - 언리얼 빌드 시스템 (15/15) (0) | 2024.05.12 |
[Study] Part 1 - 언리얼 오브젝트 관리 I - 직렬화 (13/15) (0) | 2024.05.08 |
[Study] Part1 - 언리얼 엔진의 메모리 관리 (12/15) (0) | 2024.05.07 |
[Study] Part 1 - 언리얼 컨테이너 라이브러리 II - 구조체와 Map (11/15) (0) | 2024.05.03 |