-
Автор темы
- #1
Фикс 1: Валидация и инкапсуляция
Нашли TODO, зарытый на 136-й строке LyraInventoryManagerComponent.cpp.
Суть проблемы: Валидация (nullptr, проверка состояния Entry.Instance) выполняется прямо в компоненте. Логика смешивается, инкапсуляция нарушается, код превращается в свалку проверок и условий.
Решение
Скрываем валидацию там, где её место — в подсистеме.
В своих потугах будем использовать UE5.5.0.
Что мы делаем?
Мы берём грязную работу и отдаём её подсистеме, чтобы компонент наконец-то мог дышать спокойно, не забивая голову бесполезными проверками.
Обновляем FLyraInventoryList
Теперь компонент инвентаря делегирует проверку валидности и очистку подсистеме.
Что мы сделали?

Мы перехватили весь этот беспорядок и отдали его подсистеме, чтобы компоненты больше не жрали свою память на скучные проверки.
Мы не просто перенесли логику. Мы её закапываем там, где ей и место. Компонент инвентаря стал легче, а код — чище и надежнее.
Как говорится: "Меньше проверок руками — больше времени на крутые фичи!"
Фикс 2: Имплементация
Молодёжь, конечно… Какое-то постоянное состояние неопределённости. Как этот unimplemented(), который каждый раз шепчет: "Начну с понедельника." А потом ты такой — раз, и забываешь, а код остаётся как подгузник, который ты не сменил вовремя. Всё вроде работает, но разгоняется — и бах!
Два метода AddEntry — зачем они нужны?
Потому что жизнь никогда не бывает линейной. Всё сложнее.
Первый метод — для новых экземпляров. Мы создаём что-то с нуля, задаём параметры и отправляем в мир. Это как роддом для предметов.
Второй метод — для существующих предметов. Вроде как, пришёл, а тут тебе только нужно проверить, а не стал ли он тыквой по дороге. Если всё в порядке — подсистема его доделает, если надо.
Почему unimplemented() — это плохо?
Забыть про этот метод — как забыть прикрутить гайку. Всё будет работать, пока не разгонишься. А потом в самый неподходящий момент машина возьмёт и развалится на части. Так вот, этот метод — необходим. А если его нет, то весь процесс создаётся в голове как пустое место, как заброшенная деревня, куда никто не заходит.
Как его реализовать?
Не будем терять время на всякие отговорки. Заполняем пробел и двигаемся дальше:
Ну что, теперь всё как надо:
Два метода — два подхода: создать или добавить. Оба играют свою роль. И если какой-то из них не был реализован, а ждет, как старик на утренней рыбалке — не откладывайте! Сразу займитесь этим, иначе потом будете искать баг, когда всё уже валится на куски. Лучше сразу доделать, чем потом искать баг, когда всё уже развалилось!
Нашли TODO, зарытый на 136-й строке LyraInventoryManagerComponent.cpp.
Перевод: "Не хочу возиться с этим здесь, спрячьте куда-нибудь подальше.""Would prefer to not deal with this here and hide it further?"
Суть проблемы: Валидация (nullptr, проверка состояния Entry.Instance) выполняется прямо в компоненте. Логика смешивается, инкапсуляция нарушается, код превращается в свалку проверок и условий.
Решение
Скрываем валидацию там, где её место — в подсистеме.
- Логика проверок уходит из компонента.
- Подсистема берёт на себя всю грязную работу.
- Компонент вызывает только готовые методы.
- Чистота кода: компонент будет заниматься своим делом, не влезая в проверку данных.
- Надёжность: валидатор в подсистеме будет унифицирован и не допустит пропущенных проверок.
- Лёгкость поддержки: захочешь изменить валидацию? Меняй в одном месте — подсистема всё обработает.
Что мы делаем?
Мы берём грязную работу и отдаём её подсистеме, чтобы компонент наконец-то мог дышать спокойно, не забивая голову бесполезными проверками.
- Переносим логику проверки валидности и очистки в ULyraInventorySubsystem.
- Валидация: Проверка на nullptr и другие ошибки состояния больше не беспокоят компонент.
- Фильтрация: Подсистема фильтрует только валидные экземпляры.
- Очистка: Убираем невалидные предметы автоматически. Мусор не задерживается.
- Обновляем FLyraInventoryList:
Теперь компонент не несёт ответственность за эти проверки. Все они делегированы подсистеме. Компонент лишь запрашивает, а подсистема делает.
"Пусть каждый делает то, для чего он предназначен."
LyraInventorySubsystem.h:
public:
/** Validate if an inventory item instance is still valid */
UFUNCTION(BlueprintCallable, Category="Lyra|Inventory")
bool IsValidItemInstance(const ULyraInventoryItemInstance* Instance) const;
/** Get all valid items from an inventory entry array */
UFUNCTION(BlueprintCallable, Category="Lyra|Inventory")
TArray<ULyraInventoryItemInstance*> GetValidItemsFromList(const TArray<struct FLyraInventoryEntry>& Entries) const;
private:
/** Remove invalid items from management */
void CleanupInvalidItems();
LyraInventorySubsystem.cpp:
void ULyraInventorySubsystem::CleanupInvalidItems()
{
ManagedItems.RemoveAll([](const ULyraInventoryItemInstance* Item) {
return !IsValid(Item);
});
}
bool ULyraInventorySubsystem::IsValidItemInstance(const ULyraInventoryItemInstance* Instance) const
{
return IsValid(Instance) && Instance->GetItemDef() != nullptr && ManagedItems.Contains(Instance);
}
TArray<ULyraInventoryItemInstance*> ULyraInventorySubsystem::GetValidItemsFromList(const TArray<FLyraInventoryEntry>& Entries) const
{
TArray<ULyraInventoryItemInstance*> Results;
Results.Reserve(Entries.Num());
for (const FLyraInventoryEntry& Entry : Entries)
{
if (IsValidItemInstance(Entry.Instance))
{
Results.Add(Entry.Instance);
}
}
return Results;
}
LyraInventoryManagerComponent.h:
// Добавляем после #include "LyraInventoryManagerComponent.generated.h"
class ULyraInventorySubsystem;
// В FLyraInventoryEntry после строки friend ULyraInventoryManagerComponent;
friend ULyraInventorySubsystem;
Теперь компонент инвентаря делегирует проверку валидности и очистку подсистеме.
LyraInventoryManagerComponent.cpp:
// Полностью заменяем реализацию функции FLyraInventoryList::GetAllItems()
TArray<ULyraInventoryItemInstance*> FLyraInventoryList::GetAllItems() const
{
if (UGameInstance* GameInstance = OwnerComponent->GetWorld()->GetGameInstance())
{
if (ULyraInventorySubsystem* InventorySubsystem = GameInstance->GetSubsystem<ULyraInventorySubsystem>())
{
return InventorySubsystem->GetValidItemsFromList(Entries);
}
}
return TArray<ULyraInventoryItemInstance*>();
}

Мы перехватили весь этот беспорядок и отдали его подсистеме, чтобы компоненты больше не жрали свою память на скучные проверки.
- Добавили методы в подсистему:
- IsValidItemInstance: Проверяет валидность экземпляра.
- GetValidItemsFromList: Оставляет только валидные предметы, весь мусор вон из списка.
- CleanupInvalidItems: Убирает всё, что не прошло проверку. Грязные предметы — не наша забота.
- Перенесли логику валидации из компонента в подсистему:
FLyraInventoryList теперь передаёт задачи подсистеме. Ни nullptr, ни другие тупые проверки не тревожат компонент. - Инкапсуляция на уровне:
Вся логика валидации теперь под контролем ULyraInventorySubsystem.
Любая новая логика — логирование, асинхронная проверка, кастомные ошибки — теперь не проблема.
- Централизованная валидация: Никаких раскиданных проверок по всему коду. Вся грязная работа — в подсистеме.
- Чистый и модульный код: Компоненты теперь не врут про проверки. Они просто делают своё дело.
- Автоматическая очистка: Подсистема удаляет все невалидные предметы. Никаких ошибок, никаких утечек.
- Гибкость: Расширить систему — добавить новые проверки или асинхронное обновление? Пожалуйста. Всё есть.
Мы не просто перенесли логику. Мы её закапываем там, где ей и место. Компонент инвентаря стал легче, а код — чище и надежнее.
Как говорится: "Меньше проверок руками — больше времени на крутые фичи!"
Фикс 2: Имплементация
Молодёжь, конечно… Какое-то постоянное состояние неопределённости. Как этот unimplemented(), который каждый раз шепчет: "Начну с понедельника." А потом ты такой — раз, и забываешь, а код остаётся как подгузник, который ты не сменил вовремя. Всё вроде работает, но разгоняется — и бах!
Два метода AddEntry — зачем они нужны?
- ULyraInventoryItemInstance* AddEntry(TSubclassOf<ULyraInventoryItemDefinition> ItemClass, int32 StackCount)
Это наш молодой и амбициозный метод. Он создаёт новый предмет с нуля:- Берёт определение предмета (ItemClass).
- Создаёт экземпляр через подсистему.
- Устанавливает размер стека ( потому что мы не всегда один батончик хлеба хотим, иногда нужно их пару десятков, да?).
- Используется при создании новых предметов: игрок нашёл лопату, квест наградил булкой, или админ выдал баночку зелья.
- void AddEntry(ULyraInventoryItemInstance* Instance)
А вот это старый волк в системе — работает с уже существующими экземплярами предметов:- Экземпляр уже есть — неважно, откуда он: репликация, загрузка сохранёнки или переложили из другого инвентаря.
- Нужна только проверка валидности (а то мало ли, пока предмет полз к нам, превратился в тыкву).
- Подсистема может его дополнительно инициализировать, если что-то не так.
Потому что жизнь никогда не бывает линейной. Всё сложнее.
Первый метод — для новых экземпляров. Мы создаём что-то с нуля, задаём параметры и отправляем в мир. Это как роддом для предметов.
Второй метод — для существующих предметов. Вроде как, пришёл, а тут тебе только нужно проверить, а не стал ли он тыквой по дороге. Если всё в порядке — подсистема его доделает, если надо.
Почему unimplemented() — это плохо?
Забыть про этот метод — как забыть прикрутить гайку. Всё будет работать, пока не разгонишься. А потом в самый неподходящий момент машина возьмёт и развалится на части. Так вот, этот метод — необходим. А если его нет, то весь процесс создаётся в голове как пустое место, как заброшенная деревня, куда никто не заходит.
Как его реализовать?
Не будем терять время на всякие отговорки. Заполняем пробел и двигаемся дальше:
LyraInventoryManagerComponent.cpp:
void FLyraInventoryList::AddEntry(ULyraInventoryItemInstance* Instance)
{
// Добавляем реализацию вместо unimplemented();
if (!Instance || !OwnerComponent)
{
return;
}
AActor* OwningActor = OwnerComponent->GetOwner();
if (!OwningActor || !OwningActor->HasAuthority())
{
return;
}
// Check if the instance is already managed by the inventory subsystem
if (UGameInstance* GameInstance = OwnerComponent->GetWorld()->GetGameInstance())
{
if (ULyraInventorySubsystem* InventorySubsystem = GameInstance->GetSubsystem<ULyraInventorySubsystem>())
{
if (!InventorySubsystem->IsValidItemInstance(Instance))
{
// If not managed, initialize it through the subsystem
InventorySubsystem->InitializeItemInstance(Instance, Instance->GetItemDef());
}
}
}
// Add to inventory
FLyraInventoryEntry& NewEntry = Entries.AddDefaulted_GetRef();
NewEntry.Instance = Instance;
NewEntry.StackCount = 1;
NewEntry.LastObservedCount = 1;
BroadcastChangeMessage(NewEntry, /[I]OldCount=[/I]/ 0, /[I]NewCount=[/I]/ 1);
}
- AddEntry(ItemClass, StackCount) — создаёт новый предмет.
- AddEntry(Instance) — добавляет уже существующий экземпляр.
Два метода — два подхода: создать или добавить. Оба играют свою роль. И если какой-то из них не был реализован, а ждет, как старик на утренней рыбалке — не откладывайте! Сразу займитесь этим, иначе потом будете искать баг, когда всё уже валится на куски. Лучше сразу доделать, чем потом искать баг, когда всё уже развалилось!
Пожалуйста, авторизуйтесь для просмотра ссылки.
Последнее редактирование: