개발일지공부아이디어
    • 4월
    • 3월
    • 2월
    • 1월
  • 17

    26. 4. 17.

  • 16

    26. 4. 16.

  • 15

    26. 4. 15.

  • 14

    26. 4. 14.

  • 10

    26. 4. 10.

  • 09

    26. 4. 9.

  • 08

    26. 4. 8.

로딩 중...

2026. 4. 16.

언리얼 오브젝트 관리 Ⅱ - 패키지

언리얼 오브젝트 패키지

  • UObject를 파일로 저장하면 UAsset, UAsset들을 하나로 모으면 UPackage

패키지의 중의적 개념

  • 언리얼 엔진은 다양한 곳에서 패키지란 단어를 사용하고 있음
  • 언리얼 오브젝트를 감싼 포장 오브젝트
  • 개발된 최종 콘텐츠를 정리해 프로그램으로 만드는 작업(게임 패키징)
  • DLC같은 확장 콘텐츠에 사용되는 데이터 묶음

패키지와 애셋

  • 기본적으로 언리얼 오브젝트는 반드시 패키지에 소속돼있음
  • 특정 패키지를 선택하지 않으면 임시 패키지에 소속됨(Transient Package)
  • 구조상 패키지는 다수의 언리얼 오브젝트를 소유할 수 있으나 일반적으로는 하나의 애셋만 가짐

NewObject의 비밀

  • T* NewObject(UObject* Outer = (UObject*)GetTransientPackage())
  • 기본값으로 TransientPackage로 끼워줌
  • TransientPackage는 타고 들어가보면 UObject임

패키지 저장 방식:

void UMyGameInstance::SaveStudentPackage() const{	UPackage* StudentPackage = CreatePackage(*PackageName);	// 패키지에 사용할 플래그. Public | Standalone이 가장 일반적 조합	EObjectFlags ObjectFlag = RF_Public | RF_Standalone;		UStudent* Student = NewObject<UStudent>(		StudentPackage,				// UObject 생성 시에 Package와 연결함		UStudent::StaticClass(), 		*AssetName, 		ObjectFlag	);	Student->SetName(TEXT("곽민규"));	Student->SetOrder(19);	FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());	// FString PackageFileName2 = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Content"), FString::Printf(TEXT("%s%s"), *PackageName, *FPackageName::GetAssetPackageExtension()));	FPaths::MakeStandardFilename(PackageFileName);	FSavePackageArgs SaveArgs;	SaveArgs.TopLevelFlags = ObjectFlag;	if (UPackage::SavePackage(		StudentPackage, 		nullptr,			// asset은 이미 package에 연결돼있으므로 지정 필요없음		*PackageFileName, 		SaveArgs	))	{		UE_LOG(LogTemp, Log, TEXT("패키지가 성공적으로 저장되었습니다."));	}}

UPROPERTY() 옵션

  • ~DefaultsOnly: 애셋을 생성하면 메모리에 올라가기 전 파일 상태의 기본값을 설정할 수 있음
  • ~InstanceOnly: 실제 객체에 포함돼 메모리에 올라갔을 때를 가리킴

패키지 로드 방식:

void UMyGameInstance::LoadStudentPackage() const{	UPackage* StudentPackage = LoadPackage(nullptr, *PackageName, LOAD_None);	if (!StudentPackage)	{		UE_LOG(LogTemp, Warning, TEXT("패키지를 찾을 수 없습니다."));		return;	}	// 패키지 규모가 큰 경우 완전히 로드 보장	StudentPackage->FullyLoad();	UStudent* Student = FindObject<UStudent>(StudentPackage, *AssetName);	if (Student)	{		PrintStudentInfo(TEXT("FindObject Result"), Student);	}}

서브 오브젝트 추가

// 서브 오브젝트 추가const int32 SubObjectCount = 10;for (int32 i = 1; i <= SubObjectCount; ++i){	FString SubObjectName = FString::Printf(TEXT("Student%d"), i);	UStudent* SubStudent = NewObject<UStudent>		(			Student, 			UStudent::StaticClass(), 			*SubObjectName, 			ObjectFlag		);	SubStudent->SetName(FString::Printf(TEXT("학생%d"), i));	SubStudent->SetOrder(i);}
  • 구조: 패키지 → 애셋 → 서브오브젝트

저장하고 uasset 파일을 열어보면 서브오브젝트가 저장돼있음

애셋 정보 저장과 로딩 전략

  • 게임 제작 단계에서 애셋 간 연결 작업을 위해 직접 패키지를 불러 할당하는 작업은 부하가 큼
  • 애셋 로딩 대신 오브젝트 경로(FName, 해시테이블)로만 에디터에서 사용
  • 오브젝트 경로는 유일함(같은 경로 내 같은 파일명 불가능하므로)

로딩 전략

  • 에디터에서 애셋을 건드릴 때마다 로드할 필요는 없음
  • 프로젝트에서 처음부터 애셋이 반드시 필요한 경우: 생성자 코드에서 미리 로딩(강참조)
  • 런타임에서 필요한 때 바로 로딩하는 경우: 런타임 로직에서 정적 로딩
  • 런타임에서 비동기적으로 로딩하는 경우: 런타임 로직에서 관리자를 사용해 비동기 로딩

강참조

UPROPERTY()TObjectPtr<UTexture2D> HardRefTexture;  // 또는 UTexture2D*UPROPERTY()TSubclassOf<AActor> HardRefClass;

특징:

  • 즉시 로드: 해당 객체를 참조하는 순간 자동으로 메모리에 로드됩니다
  • 패키지 의존성: 컴파일 타임에 의존성이 생성되어, 참조하는 애셋이 항상 함께 로드됩니다
  • 메모리 상주: 참조가 끊기지 않는 한 메모리에 계속 남아있습니다
  • 빠른 접근: 이미 메모리에 있으므로 즉시 사용 가능합니다 문제점:
  • 불필요한 애셋까지 로드되어 메모리 낭비 발생
  • 로딩 시간이 길어질 수 있음
  • 대용량 게임에서는 성능 저하 원인

약참조

UPROPERTY()TSoftObjectPtr<UTexture2D> SoftRefTexture;UPROPERTY()TSoftClassPtr<AActor> SoftRefClass;

특징:

  • 지연 로드: 참조만 가지고 있고, 실제 필요할 때만 로드합니다
  • 경로만 저장: FSoftObjectPath로 애셋 경로만 저장 (약 수십 바이트)
  • 명시적 로드 필요: LoadSynchronous() 또는 비동기 로드 필요
  • 메모리 효율적: 필요한 것만 로드하여 메모리 절약

|상황|사용할 참조| |항상 필요한 핵심 애셋 (플레이어 캐릭터 등)|강참조| |가끔 사용되는 애셋 (특정 무기, 스킬 이펙트)|약참조| |대용량 애셋 (시네마틱, 보스 몬스터)|약참조| |레벨별로 다른 애셋|약참조| |UI에서 선택 가능한 수백 개의 아이템|약참조|

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

  • 애셋 비동기 로딩 지원하는 관리자 객체
  • 콘텐츠 제작과 무관한 싱글톤 클래스에 FStreamableManager 선언하기(GameInstance도 좋은 선택)
  • FStreamableManager로 애셋의 동기/비동기 로딩 관리.
  • 다수의 오브젝트 경로를 입력해 한꺼번에 로딩도 가능

언리얼 빌드 시스템

에디터 구성

  • 게임 제작을 위해 에픽 게임즈가 제공하는 저작도구
  • 에디터: 게임제작을 위해 제공되는 응용 프로그램
  • 게임빌드: EXE 파일과 리소스로 이루어진 독립적으로 동작하는 게임 클라이언트
  • 에디터에서 기획과 개발을 완료한 후, 게임 빌드를 통해 최종 게임 결과물을 패키징함

에디터의 동작

  • .uproject 확장자 실행하면 에디터 트리거
  • .uproject확장자는 윈도우 레지스트리에 등록돼있음(등록 안돼있으면 런처 실행해 등록)
  • UnrealVersionSelector 프로그램으로 프로젝트 정보가 넘겨짐
  • UnrealVersionSelect는 런처가 저장해놓은 에디터 정보에 적합한 에디터 실행

에디터 버전 정보

  • {프로젝트}.uproject 텍스트 파일에 기록돼있음
  • .uproject 확장자는 에디터를 띄우기 위한 명세서 역할
  • JSON 형식임
  • ProgramData/Epic/UnrealLauncher 폴더에 에픽 런처가 지정한 정보가 있음(JSON)

언리얼 C++ 모듈

  • 언리얼 엔진의 소스코드는 모두 모듈(Module) 단위로 구성돼있음
  • 모듈을 컴파일함으로서 에디터 및 게임에 우리가 제작한 로직을 공급함
  • C++ 소스코드 컴파일하면
    • 에디터용 DLL(동적 라이브러리, 장점: 이식성, 단점: 메모리 공간 이동으로 불안정)
    • 게임용 정적 라이브러리(최종 빌드에 포함)
  • 에디터용 모듈은 언제나 UnrealEditor-{모듈이름}.dll 규칙을 준수함

직접 모듈 추가

  • 기본 언리얼 모듈에 자체제작 C++ 모듈을 추가하려면 DLL을 빌드 폴더에 넣어줘야 함

모듈 C++ 코드 관리

  • 언리얼 소스코드는 특정 플랫폼에 종속적이지 않음
  • UnrealBuildTool C#프로그램에 의해 플랫폼에 맞게 컴파일됨

Source 폴더 구조

/Source- 타겟 설정 파일- {프로젝트이름}	- 모듈 설정 파일	- 소스 코드 파일

타겟 설정 파일: 전체 솔루션이 다룰 빌드 대상 지정

  • {프로젝트이름}.Target.cs : 게임 빌드 설정
  • {프로젝트이름}Editor.Target.cs : 에디터 빌드 설정

모듈 설정 파일: 모듈 빌드용 C++ 프로젝트 설정 정보

  • {모듈이름}.Build.cs : 모듈 빌드 환경 설정

C# 특성상(JIT 컴파일) 런타임에 cs 파일을 읽어 빌드 환경 구축하고 컴파일함

  • Windows, Android만 JIT됨. MacOS, iOS는 AOT만 됨(유니티 고통)

모듈 구성요소

  • 소스코드(.h, .cpp)
  • 모듈의 뼈대 - 매크로
    • IMPLEMENT_MODULE: 일반 모듈(= 플러그인)
    • IMPLEMENT_GAME_MODULE: 게임모듈
    • IMPLEMENT_PRIMARY_GAME_MODULE: 주 게임모듈(게임에 반드시 하나는 있어야 함)

모듈간 종속 관계

  • 모든 모듈들이 GameModule에 포함되고, GameModule을 언리얼이 가져가서 exe로 만듦

모듈의 공개와 참조

  • 필요한 만큼만 공개해야 의존성도 줄어들고 컴파일 시간도 최소화됨
  • 예전 언리얼 엔진은 /Classes 폴더가 있어 Public 폴더 역할을 하면서 언리얼 오브젝트를 관리했음.
  • 지금은 공개 파일은 모두 /Public로
  • 숨길 파일은 모두 /Private로
  • 외부로 공개할 클래스 선언엔 {모듈이름}_API 매크로 붙이기(__declspec(dllexport))
  • 각 모듈에서 .Build.cs 설정으로 참조할 모듈 설정할 수 있음

플러그인 시스템

  • 모듈을 분리시키는 방법 중 하나
  • 플러그인은 결국 상호 독립적인 모듈임
  • 에디터 설정 > 플러그인 창에서 빌트인이나 자체제작 플러그인 유연하게 추가삭제 가능

구조

  • .uplugin: .uproject와 동일한 개념
  • /Resources: 아이콘 등 리소스
  • /Source/{플러그인이름}/: 모듈 소스코드(Public/Private), 의존성 설정(.Build.cs)

게임 빌드

  • 게임 타겟 설정({프로젝트이름}.Target.cs)을 추가하면 게임 빌드 옵션(Shipping)이 추가됨
  • 게임 타겟으로 빌드된 모듈은 정적 라이브러리로 exe에 포함됨
  • 게임이 실행되기 위해선 실행 파일 + 콘텐츠 애셋이 필요함.
  • 빌드: 실행 파일을 생성하기 위한 컴파일
  • 쿠킹: 지정한 플랫폼에 맞는 콘텐츠 애셋 변환 작업
  • 패키징: 실행 파일과 콘텐츠 소스를 모아 하나의 프로그램으로 만드는 작업

쿠킹 단계가 빠지면 아래와 같이 exe 실행이 안됨

캡쳐와 같이 Shipping 옵션으로 Package Project하면 지정된 경로에 실행파일이 빌드됨

  • 쿠킹 단계만 미리 해놓으면 콘텐츠 소스 변경이 없다는 가정 하에 패키징 시간이 줄어듦
왼쪽 화살표오른쪽 화살표