-
Автор темы
- #1
И снова тодошки в прототипе системы инвентаря от Lyra… На 93-й строке LyraInventoryManagerComponent.cpp мы наткнулись на вырубленное в камне послание от эпиков:
Как говорится, "нет ничего более постоянного, чем временное решение". Но не сегодня! Мы избавим код от этого костыля и реализуем правильное решение, воспользовавшись нашим подсистемным подходом. Пора управлять экземплярами предметов грамотно и централизованно.
В чем проблема?
Баг UE-127172 вынуждал использовать актор вместо компонента в качестве "внешнего объекта" для экземпляров предметов. Однако такое решение:
Мы добавим централизованное управление жизненным циклом предметов через реализованную в прошлый раз подсистему ULyraInventorySubsystem.
Подсистема теперь будет:
Добавляем методы в ULyraInventorySubsystem
Новые методы:
Что происходит:
Теперь вместо кустарного создания экземпляров мы будем пользоваться нашей новой подсистемой.
Изменения:
Что мы сделали?
Вывод
Мы убрали баг UE-127172 с корнем и заменили костыль на надёжное, централизованное решение. Подсистема теперь рулит всем процессом создания и управления экземплярами, следит за порядком и даже подметает следы при завершении работы.
Как говорится, "Хороший код — это когда даже временные решения выглядят намеренно"... но мы-то знаем, что лучше решить проблему сразу, чем прятать её за TODO.
TODO: Using the actor instead of component as the outer due to UE-127172
. Все ясно! Тодошка из отряда костылеобразных.Как говорится, "нет ничего более постоянного, чем временное решение". Но не сегодня! Мы избавим код от этого костыля и реализуем правильное решение, воспользовавшись нашим подсистемным подходом. Пора управлять экземплярами предметов грамотно и централизованно.
В чем проблема?
Баг UE-127172 вынуждал использовать актор вместо компонента в качестве "внешнего объекта" для экземпляров предметов. Однако такое решение:
- Ломает иерархию владения: экземпляры должны принадлежать компоненту, а не актору.
- Портит модульность и усложняет отладку.
- Выглядит как типичный "костыль", с которым стыдно показываться в приличном коде.
Мы добавим централизованное управление жизненным циклом предметов через реализованную в прошлый раз подсистему ULyraInventorySubsystem.
Lyra Inventory Fix 02: Подсистема для фрагментов инвентаря. Решаем TODO с умом.
Ох уж эти TODO-комментарии от Epic Games... Они, как забытые под диваном носки: вроде лежат себе тихо, но каждый раз, проходя мимо, ты слышишь их шёпот: "Вернись! Не оставляй меня здесь!". И, заглянув под диван, на 49-й строке LyraInventoryItemDefinition.h мы как раз и нашли один такой:
Ох уж эти TODO-комментарии от Epic Games... Они, как забытые под диваном носки: вроде лежат себе тихо, но каждый раз, проходя мимо, ты слышишь их шёпот: "Вернись! Не оставляй меня здесь!". И, заглянув под диван, на 49-й строке LyraInventoryItemDefinition.h мы как раз и нашли один такой:
Todo: Make into a subsystem instead?
прямо перед ULyraInventoryFunctionLibrary. Что ж, пора отправить его на заслуженную пенсию и навести порядок- Создавать экземпляры предметов.
- Инициализировать их правильно.
- Управлять их жизненным циклом, очищая их при деинициализации.
Добавляем методы в ULyraInventorySubsystem
LyraInventorySubsystem.h:
public:
/** Create a new inventory item instance */
UFUNCTION(BlueprintCallable, Category="Lyra|Inventory")
ULyraInventoryItemInstance* CreateInventoryItemInstance(UObject* Outer, TSubclassOf<ULyraInventoryItemDefinition> ItemDef);
/** Initialize a newly created inventory item instance */
void InitializeItemInstance(ULyraInventoryItemInstance* Instance, TSubclassOf<ULyraInventoryItemDefinition> ItemDef);
private:
/** Cache of created inventory items for proper lifecycle management */
UPROPERTY()
TArray<TObjectPtr<ULyraInventoryItemInstance>> ManagedItems;
- CreateInventoryItemInstance — создаёт экземпляр предмета и настраивает его внешний объект (outer).
- InitializeItemInstance — выполняет дополнительную инициализацию экземпляра.
- ManagedItems — массив для отслеживания всех созданных экземпляров.
LyraInventorySubsystem.cpp:
// Надеюсь знаем куда вставлять?
#include "LyraInventoryItemInstance.h"
// Добавляем в функцию очистку управляемых предметов
void ULyraInventorySubsystem::Deinitialize()
{
// Cleanup managed items
ManagedItems.Empty(); // Вот эта строка
RegisteredInventoryComponents.Empty();
Super::Deinitialize();
}
// И добавляем реализации объявленных функций:
ULyraInventoryItemInstance* ULyraInventorySubsystem::CreateInventoryItemInstance(UObject* Outer, TSubclassOf<ULyraInventoryItemDefinition> ItemDef)
{
if (!ItemDef || !Outer)
{
return nullptr;
}
// Create the instance with the provided outer
ULyraInventoryItemInstance* NewInstance = NewObject<ULyraInventoryItemInstance>(Outer);
if (NewInstance)
{
InitializeItemInstance(NewInstance, ItemDef);
ManagedItems.Add(NewInstance);
}
return NewInstance;
}
void ULyraInventorySubsystem::InitializeItemInstance(ULyraInventoryItemInstance* Instance, TSubclassOf<ULyraInventoryItemDefinition> ItemDef)
{
if (!Instance || !ItemDef)
{
return;
}
Instance->SetItemDef(ItemDef);
// Initialize with fragments
if (const ULyraInventoryItemDefinition* Definition = ItemDef.GetDefaultObject())
{
for (const ULyraInventoryItemFragment* Fragment : Definition->Fragments)
{
if (Fragment)
{
Fragment->OnInstanceCreated(Instance);
}
}
}
}
- Созданный экземпляр привязывается к компоненту, а не к актору.
- Экземпляры добавляются в массив ManagedItems, что позволяет правильно управлять их жизненным циклом.
- При деинициализации подсистемы массив очищается, предотвращая утечки памяти.
Теперь вместо кустарного создания экземпляров мы будем пользоваться нашей новой подсистемой.
LyraInventoryManagerComponent.cpp:
/* Заменяем вот этот код:
FLyraInventoryEntry& NewEntry = Entries.AddDefaulted_GetRef();
NewEntry.Instance = NewObject<ULyraInventoryItemInstance>(OwnerComponent->GetOwner()); //[USER=891005]@Todo[/USER]: Using the actor instead of component as the outer due to UE-127172
NewEntry.Instance->SetItemDef(ItemDef);
for (ULyraInventoryItemFragment* Fragment : GetDefault<ULyraInventoryItemDefinition>(ItemDef)->Fragments)
{
if (Fragment != nullptr)
{
Fragment->OnInstanceCreated(NewEntry.Instance);
}
}
NewEntry.StackCount = StackCount;
Result = NewEntry.Instance;
//const ULyraInventoryItemDefinition* ItemCDO = GetDefault<ULyraInventoryItemDefinition>(ItemDef);
MarkItemDirty(NewEntry);
return Result;
...на вот этот:*/
// Create the item instance through the subsystem
if (UGameInstance* GameInstance = OwnerComponent->GetWorld()->GetGameInstance())
{
if (ULyraInventorySubsystem* InventorySubsystem = GameInstance->GetSubsystem<ULyraInventorySubsystem>())
{
FLyraInventoryEntry& NewEntry = Entries.AddDefaulted_GetRef();
NewEntry.Instance = InventorySubsystem->CreateInventoryItemInstance(OwnerComponent, ItemDef);
if (NewEntry.Instance)
{
NewEntry.StackCount = StackCount;
NewEntry.LastObservedCount = StackCount;
Result = NewEntry.Instance;
//MarkItemDirty(NewEntry);
BroadcastChangeMessage(NewEntry, /[I]OldCount=[/I]/ 0, /[I]NewCount=[/I]/ StackCount);
}
}
}
return Result;
- AddEntry вызывает метод CreateInventoryItemInstance из подсистемы.
- Внешним объектом (outer) теперь корректно выступает компонент, а не актор.
- Инициализация экземпляра проводится через InitializeItemInstance.
LyraInventoryItemInstance.h:
// После строки "friend struct FLyraInventoryList;" добавим:
friend class ULyraInventorySubsystem;
- Создали централизованное управление экземплярами предметов:
- Создание, инициализация и очистка управляются через подсистему.
- Правильная иерархия владения: компонент теперь внешний объект.
- Исправили проблему UE-127172:
- Избавились от временного решения с актором.
- Система стала чище, логичнее и модульнее.
- Добавили новые возможности:
- Масштабируемость: можно легко добавить кастомную логику создания предметов.
- Управление жизненным циклом: больше никаких утечек или зомби-объектов.
- Чистота кода: логика создания и управления экземплярами сосредоточена в одном месте — в подсистеме.
- Правильная иерархия: предметы принадлежат компоненту, как и положено.
- Расширяемость: легко добавить новый функционал — например, пуллинг объектов или асинхронную инициализацию.
- Оптимизация: управление массивом экземпляров позволяет избежать утечек.
Вывод
Мы убрали баг UE-127172 с корнем и заменили костыль на надёжное, централизованное решение. Подсистема теперь рулит всем процессом создания и управления экземплярами, следит за порядком и даже подметает следы при завершении работы.
Как говорится, "Хороший код — это когда даже временные решения выглядят намеренно"... но мы-то знаем, что лучше решить проблему сразу, чем прятать её за TODO.
Последнее редактирование: