Unreal EngineMiddleTechnical
Что такое Data Asset в Unreal Engine и когда использовать его вместо struct?
Data Asset — это UObject-наследник с UPROPERTY-полями, сохраняемый как отдельный asset в контент-браузере; struct не имеет identity, не может иметь ссылки и не редактируется как самостоятельный файл.
Что такое Data Asset
UDataAsset — базовый класс Unreal Engine для объектов-данных, которые сохраняются как отдельные .uasset-файлы в контент-браузере. Они поддерживают стандартный UE-пайплайн: ссылки через TSoftObjectPtr, асинхронная загрузка через Asset Manager, Blueprint-редактирование и наследование.
Когда использовать Data Asset
- Конфигурация оружия, предметов, NPC — данные, которые дизайнеры правят независимо.
- Нужна ссылка на конкретный экземпляр (Asset Reference) — например, иконка предмета, звук, скелетная меш.
- Данные должны быть загружены асинхронно через
UAssetManager. - Нужно наследование:
UWeaponData→URifleData.
Когда использовать struct
- Данные — только значения, без asset-ссылок и идентификатора.
- Передача параметров внутри одной системы (не нужно сохранять в контенте).
- Встроенные данные внутри другого объекта (поле в DataTable, параметр функции).
Создание Data Asset в C++
// WeaponData.h
#pragma once
#include "Engine/DataAsset.h"
#include "WeaponData.generated.h"
UCLASS(BlueprintType)
class MYGAME_API UWeaponData : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Stats")
float Damage = 25.f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Stats")
float FireRate = 0.1f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Visuals")
TSoftObjectPtr<USkeletalMesh> Mesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Audio")
TSoftObjectPtr<USoundBase> FireSound;
// Обязательно для Asset Manager
virtual FPrimaryAssetId GetPrimaryAssetId() const override
{
return FPrimaryAssetId(TEXT("WeaponData"), GetFName());
}
};
Регистрация в Asset Manager (DefaultGame.ini)
[/Script/Engine.AssetManagerSettings]
+PrimaryAssetTypesToScan=(
PrimaryAssetType="WeaponData",
AssetBaseClass="/Script/MyGame.WeaponData",
bHasBlueprintClasses=False,
Directories=((Path="/Game/Data/Weapons"))
)
Асинхронная загрузка через Asset Manager
UAssetManager& AM = UAssetManager::Get();
FPrimaryAssetId WeaponId(TEXT("WeaponData"), TEXT("Rifle_AK47"));
TArray<FName> Bundles;
FStreamableDelegate Delegate = FStreamableDelegate::CreateUObject(
this, &AMyCharacter::OnWeaponLoaded, WeaponId);
AM.LoadPrimaryAsset(WeaponId, Bundles, Delegate);
void AMyCharacter::OnWeaponLoaded(FPrimaryAssetId AssetId)
{
UWeaponData* Data = Cast<UWeaponData>(
UAssetManager::Get().GetPrimaryAssetObject(AssetId));
if (Data) { /* применяем данные */ }
}
Подводные камни
- UDataAsset vs UPrimaryDataAsset: только
UPrimaryDataAssetподдерживает Asset Manager и bundle-загрузку;UDataAsset— просто UObject, безGetPrimaryAssetId(). - Hard reference вместо TSoftObjectPtr: прямая ссылка
USkeletalMesh*в Data Asset грузит меш сразу при загрузке самого asset, раздувая память. - Отсутствие PrimaryAssetType в DefaultGame.ini: Asset Manager не найдёт asset, метод
GetPrimaryAssetId()вернёт Invalid. - Изменение имени класса: переименование C++-класса Data Asset разрывает все ссылки в контенте; нужен
UObjectRedirectorили ручное переподключение. - CDO и изменяемые дефолты: Data Asset — не CDO; если несколько систем читают один asset и кто-то изменяет его поля в рантайме, изменения видны всем.
- DataTable vs DataAsset: для табличных данных (сотни строк) DataTable + struct эффективнее, чем сотни отдельных Data Asset файлов.
Common mistakes
- Объяснять Data Asset только по синтаксису, без жизненного цикла и стоимости.
- Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
- Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
- Использовать макросы или specifier-и наугад и забывать про UObject GC.
What the interviewer is testing
- Кандидат формулирует точную модель для Data Asset, а не только определение.
- Пример компилируем, безопасен по lifetime и соответствует версии технологии.
- Названы trade-off, ограничения и диагностируемые симптомы ошибки.
- Понимает границу между C++ кодом, runtime/framework metadata и editor/UI слоем.