Lyra Inventory Fix 02: Подсистема для фрагментов инвентаря. Решаем TODO с умом.

Lyra Inventory Fix 02: Подсистема для фрагментов инвентаря. Решаем TODO с умом.

Ох уж эти TODO от Epic Games... Они как забытые под диваном носки. Вроде лежат себе тихо, но каждый раз, проходя мимо, ты слышишь их злобный шёпот: «Вернись… Не оставляй меня здесь…». И вот, заглянув под этот пыльный диван, мы находим его — LyraInventoryItemDefinition.h 49-я строка. Там, как надгробие, красуется надпись: // TODO: Make into a subsystem instead?

Прямо перед ULyraInventoryFunctionLibrary. Что ж, пора отправить этот реликт прошлого на заслуженную пенсию.

В своих потугах будем использовать UE5.5.0.

Почему подсистема?

Подсистемы в Unreal Engine — это как надёжный босс, который держит всё под контролем. Они живут на глобальном уровне, управляют данными централизованно и избавляют нас от бардака.

В нашем случае подсистема для инвентаря — это идеальный выход. Мы получаем:

  • Глобальный доступ к инвентарям из любой точки игры, без танцев с бубном.
  • Чистый кодвместо разбросанных по всему проекту кусочков логики.
  • Меньше дублирования, больше порядка.

Создаём новую подсистему: ULyraInventorySubsystem

Достаем инструменты. Разбираем старьё. Собираем новый, сияющий механизм.

LyraInventorySubsystem.h

// Copyright Cainoli. You can use me to fulfill your fantasies. All Rights Reserved. #pragma once #include "Subsystems/GameInstanceSubsystem.h" #include "LyraInventorySubsystem.generated.h" class ULyraInventoryItemDefinition; class ULyraInventoryItemFragment; class ULyraInventoryItemInstance; /** * Subsystem responsible for managing inventory-related functionality across the game */ UCLASS() class LYRAGAME_API ULyraInventorySubsystem : public UGameInstanceSubsystem { GENERATED_BODY() public: // Begin USubsystem virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; // End USubsystem /** Find a fragment in an item definition by class */ UFUNCTION(BlueprintCallable, Category="Lyra|Inventory", meta=(DeterminesOutputType=FragmentClass)) const ULyraInventoryItemFragment* FindItemDefinitionFragment(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, TSubclassOf<ULyraInventoryItemFragment> FragmentClass) const; /** Register an inventory component with the subsystem */ void RegisterInventoryComponent(class ULyraInventoryManagerComponent* Component); /** Unregister an inventory component from the subsystem */ void UnregisterInventoryComponent(class ULyraInventoryManagerComponent* Component); /** Get all registered inventory components */ UFUNCTION(BlueprintCallable, Category="Lyra|Inventory") const TArray<class ULyraInventoryManagerComponent*>& GetAllInventoryComponents() const { return RegisteredInventoryComponents; } private: /** List of all active inventory components */ UPROPERTY() TArray<class ULyraInventoryManagerComponent*> RegisteredInventoryComponents; };

LyraInventoryFragmentSubsystem.cpp

// Copyright Cainoli. You can use me to fulfill your fantasies. All Rights Reserved. #include "LyraInventorySubsystem.h" #include "LyraInventoryItemDefinition.h" #include "LyraInventoryManagerComponent.h" void ULyraInventorySubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); } void ULyraInventorySubsystem::Deinitialize() { RegisteredInventoryComponents.Empty(); Super::Deinitialize(); } const ULyraInventoryItemFragment* ULyraInventorySubsystem::FindItemDefinitionFragment(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, TSubclassOf<ULyraInventoryItemFragment> FragmentClass) const { if (const ULyraInventoryItemDefinition* Definition = ItemDef.Get()) { return Definition->FindFragmentByClass(FragmentClass); } return nullptr; } void ULyraInventorySubsystem::RegisterInventoryComponent(ULyraInventoryManagerComponent* Component) { if (Component) { RegisteredInventoryComponents.AddUnique(Component); } } void ULyraInventorySubsystem::UnregisterInventoryComponent(ULyraInventoryManagerComponent* Component) { if (Component) { RegisteredInventoryComponents.Remove(Component); } }

Что тут происходит?

Мы избавились от дерьма и собрали что-то стоящее. Теперь у нас GameInstanceSubsystem, который:

  • Централизует работу с инвентарями— один босс управляет всеми ресурсами, никакого бардака.
  • Добавляет метод поиска: FindItemDefinitionFragment — ищет фрагменты в определении предмета, быстро и без головной боли.

Обновляем старый код

Теперь настало время интегрировать новую подсистему в существующие классы. Удаляем старый мусор, который когда-то считался рабочим решением.

LyraInventoryItemDefinition.h

// Отправим на пенсию следующий класс: /** [USER=1433433]@deprecated[/USER] Use ULyraInventorySubsystem instead */ UCLASS(meta=(DeprecatedNode, DeprecationMessage="Use ULyraInventorySubsystem instead")) class ULyraInventoryFunctionLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() // На заслуженный отдых отправим и эту функцию: /** [USER=1433433]@deprecated[/USER] Use ULyraInventorySubsystem::FindItemDefinitionFragment instead */ UFUNCTION(BlueprintCallable, meta=(DeprecatedFunction, DeprecationMessage="Use ULyraInventorySubsystem::FindItemDefinitionFragment instead", DeterminesOutputType=FragmentClass)) static const ULyraInventoryItemFragment* FindItemDefinitionFragment(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, TSubclassOf<ULyraInventoryItemFragment> FragmentClass); };

Что мы сделали и зачем это нужно?

Мы взяли хаос и превратили его в порядок. Создали ULyraInventorySubsystem — теперь инвентарь живёт в GameInstanceSubsystem, а не разбросан по всему коду.

  • Централизованное управление — единая точка доступа ко всем инвентарям.
  • Удобство и скорость — больше не нужно лазить по классам в поисках нужных данных.

Добавили два ключевых метода:

  • FindItemDefinitionFragment — находит фрагмент в определении предмета, быстро и чётко.
  • GetAllInventoryComponents() — возвращает все ULyraInventoryManagerComponent, потому что всё, что связано с инвентарём, должно быть под контролем.

Обратная совместимость

  • Мы не рушим мосты, мы строим новые.
  • Старые методы — deprecated.
  • Добавлены предупреждения, чтобы все медленно, но верно переехали на новую систему.

Использование новой системы

В C++:

// Получение подсистемы auto* InventorySystem = GetWorld()->GetGameInstance()->GetSubsystem<ULyraInventorySubsystem>(); // Поиск фрагмента для определения предмета auto* Fragment = InventorySystem->FindItemDefinitionFragment(ItemDefClass, FragmentClass); // Получение всех инвентарей TArray<class ULyraInventoryManagerComponent*> InventoryComponents = InventorySystem->GetAllInventoryComponents();

В Blueprint:

  • Функции подсистемы доступны из Blueprint.
  • Поддержка выводного типа через метаданные.
Lyra Inventory Fix 02: Подсистема для фрагментов инвентаря. Решаем TODO с умом.

Соглашусь, пока что функционал катастрофически мал для оправдания затраченных сил, НО... это только начало.

Что дальше? Возможности для расширения

Мы не просто закрыли очередной TODO. Мы открыли дверь. Теперь система инвентаря не тупо работает — она готова к росту.

Хочешь новые возможности? Легко. Подсистема масштабируется без боли и рефакторинга.

Я планирую развивать её дальше, превращая в универсальную модульную систему инвентаря на базе Lyra. И если вам нужна мощная, гибкая система — добро пожаловать.

Вывод

  • 49-я строка больше не шепчет проклятия.
  • TODO превратился в рабочий инструмент.
  • Теперь инвентарь не раскидан по коду, а централизован.
  • Теперь добавлять новые возможности — не страдание, а удовольствие.

Как говорится, "Старый код не умирает, он просто становится частью подсистемы".

1
1
Начать дискуссию