Hard Reference
- UPROPERTY로 직접 선언하는 포인터 참조 방식
- 참조하는 오브젝트가 로드될 때 참조된 에셋도 자동으로 메모리에 함께 로드됨
- 별도의 로딩 코드 없이 바로 사용 가능
- 참조 체인(Reference Chain)을 따라 연결된 모든 에셋이 함께 로딩됨
- 사용 예시:
UPROPERTY() UStaticMesh* MyMesh;
장점
- 코드 작성이 간단하고 직관적
- 에셋을 즉시 사용할 수 있어 런타임 로딩 지연 없음
- Null 체크만으로 간단하게 에러 처리 가능
단점
- 불필요한 에셋까지 메모리에 로드되어 메모리 낭비 발생
- 초기 로딩 시간이 길어질 수 있음
- 참조 체인이 복잡해지면 의도치 않은 대량의 에셋 로딩 발생
Soft Reference
- TSoftObjectPtr, TSoftClassPtr, FSoftObjectPath 등을 사용한 간접 참조 방식
- 에셋의 경로 정보만 저장하고 실제 에셋은 메모리에 로드하지 않음
- 필요한 시점에 명시적으로 로딩해야 사용 가능
- 비동기 로딩(AsyncLoad)과 동기 로딩(LoadSynchronous) 지원
- 사용 예시:
UPROPERTY() TSoftObjectPtr<UStaticMesh> MyMesh;
장점
- 초기 메모리 사용량을 크게 절감
- 필요한 시점에만 로딩하여 메모리 효율적 관리
- 로딩 타이밍을 개발자가 제어 가능
- 대규모 프로젝트의 성능 최적화에 필수적
단점
- 로딩 코드를 직접 작성해야 함
- 비동기 로딩 시 콜백 처리 등 복잡도 증가
- 에셋이 로드되지 않은 상태에서 접근 시 Null 처리 필요
Hard Reference를 사용하는 경우
- 항상 필요한 필수 에셋 (예: 캐릭터의 기본 메시)
- 작은 용량의 에셋
- 프로토타입 단계에서 빠른 개발이 필요할 때
Soft Reference를 사용하는 경우
- 조건부로 사용되는 에셋 (예: 스킬 이펙트, UI 위젯)
- 대용량 에셋 (예: 시네마틱 영상, 고해상도 텍스처)
- 레벨 스트리밍이나 DLC 콘텐츠
- 메모리 최적화가 중요한 모바일/콘솔 플랫폼
Hard Reference vs Soft Reference 비교
| 구분 | Hard Reference | Soft Reference |
|---|
| 선언 방식 | UPROPERTY() UClass* | UPROPERTY() TSoftObjectPtr<UClass> |
| 로딩 시점 | 자동 로딩 (오브젝트 로드 시) | 명시적 로딩 필요 |
| 메모리 사용 | 참조 체인 전체 로딩 (높음) | 필요한 것만 로딩 (낮음) |
| 초기 로딩 속도 | 느림 (연결된 모든 에셋 로딩) | 빠름 (경로만 저장) |
| 사용 편의성 | 높음 (바로 사용 가능) | 낮음 (로딩 코드 필요) |
| 런타임 성능 | 즉시 접근 가능 | 로딩 대기 필요 |
| 메모리 최적화 | 어려움 | 용이함 |
| 적합한 상황 | 필수 에셋, 작은 에셋 | 조건부 사용, 대용량 에셋 |
| 로딩 제어 | 불가능 (자동) | 가능 (AsyncLoad/Sync) |
코드 예시
Hard Reference
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UStaticMesh* WeaponMesh;
void UseWeapon()
{
if (WeaponMesh)
{
}
}
Soft Reference
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftObjectPtr<UStaticMesh> WeaponMesh;
void LoadAndUseWeapon()
{
FStreamableManager& Streamable = UAssetManager::GetStreamableManager();
Streamable.RequestAsyncLoad(WeaponMesh.ToSoftObjectPath(),
FStreamableDelegate::CreateLambda([this]()
{
if (UStaticMesh* LoadedMesh = WeaponMesh.Get())
{
}
})
);
}
동기 로딩 (Synchronous Loading)
- 에셋 로딩이 완료될 때까지 게임이 멈춤 (블로킹)
- 로딩이 끝나야 다음 코드가 실행됨
- 사용법:
LoadSynchronous(), LoadObject(), StaticLoadObject()
- 구현이 간단하지만 프레임 드랍(Frame Drop) 발생 위험
- 로딩 시간이 길면 게임이 얼어붙은 것처럼 보임
동기 로딩을 사용하는 경우
- 로딩 화면이 표시되는 동안
- 게임 초기화 단계
- 레벨 전환 시
- 작은 용량의 에셋을 즉시 필요로 할 때
코드 예시
UStaticMesh* LoadedMesh = WeaponMeshSoft.LoadSynchronous();
if (LoadedMesh)
{
SetStaticMesh(LoadedMesh);
}
비동기 로딩 (Asynchronous Loading)
- 백그라운드에서 로딩하면서 게임은 계속 실행됨 (논블로킹)
- 로딩 완료 시 콜백 함수가 호출됨
- 사용법:
RequestAsyncLoad(), FStreamableManager, LoadAsync()
- 구현이 복잡하지만 부드러운 게임플레이 유지 가능
- 프레임 저하 없이 에셋을 로딩할 수 있음
비동기 로딩을 사용하는 경우
- 게임플레이 도중 에셋 로딩
- 레벨 스트리밍 (Level Streaming)
- 대용량 에셋 로딩
- 사용자 경험을 해치지 않아야 할 때
코드 예시
FStreamableManager& Streamable = UAssetManager::GetStreamableManager();
TSharedPtr Handle = Streamable.RequestAsyncLoad(
WeaponMeshSoft.ToSoftObjectPath(),
[this]()
{
if (UStaticMesh* LoadedMesh = WeaponMeshSoft.Get())
{
SetStaticMesh(LoadedMesh);
}
}
);