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

Начинающий
Статус
Оффлайн
Регистрация
9 Июл 2023
Сообщения
9
Реакции[?]
11
Поинты[?]
11K
Ох уж эти TODO-комментарии от Epic Games... Они, как забытые под диваном носки: вроде лежат себе тихо, но каждый раз, проходя мимо, ты слышишь их шёпот: "Вернись! Не оставляй меня здесь!". И, заглянув под диван, на 49-й строке LyraInventoryItemDefinition.h мы как раз и нашли один такой: 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:
 // Отправим на пенсию следующий класс:
/** @deprecated Use ULyraInventorySubsystem instead */
UCLASS(meta=(DeprecatedNode, DeprecationMessage="Use ULyraInventorySubsystem instead"))
class ULyraInventoryFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

    // На заслуженный отдых отправим и эту функцию:
/** @deprecated 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);
};
Что мы сделали и зачем это нужно?
  1. Создали подсистему ULyraInventorySubsystem:
    • Работает как GameInstanceSubsystem.
    • Предоставляет удобный и быстрый доступ к инвентарям.
  2. Добавили два основных метода:
    • FindItemDefinitionFragment — найти фрагмент в определении предмета.
    • GetAllInventoryComponents() — получить все ULyraInventoryManagerComponent.
  3. Обратная совместимость:
    • Старые методы помечены как deprecated.
    • Добавлены предупреждения, чтобы плавно перевести проект на новую систему.
Использование новой системы
В C++:
cpp:
// Получение подсистемы
auto* InventorySystem = GetWorld()->GetGameInstance()->GetSubsystem<ULyraInventorySubsystem>();

// Поиск фрагмента для определения предмета
auto* Fragment = InventorySystem->FindItemDefinitionFragment(ItemDefClass, FragmentClass);

// Получение всех инвентарей
TArray<class ULyraInventoryManagerComponent*> InventoryComponents
= InventorySystem->GetAllInventoryComponents();
В Blueprint:
  • Функции подсистемы доступны из Blueprint.
  • Поддержка выводного типа через метаданные.
Lis.PNG
Соглашусь, пока что функционал катастрофически мал для оправдания затраченных сил, НО...
Что дальше? Возможности для расширения
Наша подсистема не просто решает TODO, она открывает путь для будущих улучшений. Система легко расширяема и может быть в дальнейшем я (или быть может вы) дополните ее новыми возможностями. Я планирую ее использовать для дальнейшего расширения универсальной модульной системы инвентаря на основе прототипа LyrЫ. Поэтому дальнейшие мои публикации будут не раз затрагивать эту подсистему.
Вывод
Теперь у нас есть централизованная система для управления инвентарями. TODO-комментарий на 49-й строке превратился в надёжный инструмент, который в перспективе упростит жизнь разработчикам и уже сейчас открывает простор для новых возможностей.

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