Lyra Inventory Fix 01: Фильтрация предметов. Когда порядок — не роскошь, а необходимость.

Lyra Inventory Fix 01: Фильтрация предметов. Когда порядок — не роскошь, а необходимость.

Разработчики из Epic Games как обычно, оставили нам полуготовую систему — в этот раз речь про фильтры инвентаря в LyraInventoryManagerComponent.cpp

////////////////////////////////////////////////////////////////////// // // UCLASS(Abstract) // class ULyraInventoryFilter : public UObject // { // public: // virtual bool PassesFilter(ULyraInventoryItemInstance* Instance) const { return true; } // }; // UCLASS() // class ULyraInventoryFilter_HasTag : public ULyraInventoryFilter // { // public: // virtual bool PassesFilter(ULyraInventoryItemInstance* Instance) const { return true; } // };

Тут закомментированы строки с ULyraInventoryFilter и его производным классом ULyraInventoryFilter_HasTag. Видно, хотели сделать хорошо, но Ctrl+C нажали, а вот Ctrl+V забыли. Что ж, давайте доделаем эту систему, а заодно придумаем, как с ней можно будет работать без головной боли.

В своих потугах использую UE5.5.0

Базовый функционал: ULyraInventoryFilter

В первую очередь создаём ULyraInventoryFilter — абстрактный класс, задающий интерфейс для фильтрации предметов. Главное в нём — виртуальный метод PassesFilter, который позволяет проверять, проходит ли предмет через фильтр. Производные классы смогут его переопределять под свои нужды. Плюс, поддержим создание фильтров в Blueprint, чтобы даже те, кто избегает C++ как чумы, могли с этим работать.

LyraInventoryFilter.h

// Copyright Cainoli. You can use me to fulfill your fantasies. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "LyraInventoryFilter.generated.h" #include "System/GameplayTagStack.h" #include "UObject/NoExportTypes.h" class ULyraInventoryItemInstance; struct FGameplayTag; /** * Base class for inventory filters in Lyra */ UCLASS(DefaultToInstanced, EditInlineNew, Abstract, Blueprintable) class LYRAGAME_API ULyraInventoryFilter : public UObject { GENERATED_BODY() public: ULyraInventoryFilter() {} // Main filter function that determines if an item passes the filter UFUNCTION(BlueprintNativeEvent, Category = "Inventory") bool PassesFilter(const ULyraInventoryItemInstance* ItemInstance) const; virtual bool PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const; }; /** * Composite filter that combines multiple filters using logical operations */ UCLASS(Blueprintable) class LYRAGAME_API ULyraInventoryFilter_Composite : public ULyraInventoryFilter { GENERATED_BODY() public: // List of filters to combine UPROPERTY(EditAnywhere, Instanced, Category = "Filter") TArray<TObjectPtr<ULyraInventoryFilter>> Filters; // If true, ALL filters must pass (AND operation) // If false, ANY filter must pass (OR operation) UPROPERTY(EditAnywhere, Category = "Filter") bool bRequireAllFilters = true; virtual bool PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const override; }; /** * Filter that checks item definition class */ UCLASS(Blueprintable) class LYRAGAME_API ULyraInventoryFilter_ItemDefinition : public ULyraInventoryFilter { GENERATED_BODY() public: // The item definition class to filter by UPROPERTY(EditAnywhere, Category = "Filter") TSubclassOf<class ULyraInventoryItemDefinition> ItemDefinitionClass; virtual bool PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const override; }; /** * Filter that checks if item has specific gameplay tag */ UCLASS(Blueprintable) class LYRAGAME_API ULyraInventoryFilter_HasTag : public ULyraInventoryFilter { GENERATED_BODY() public: // The gameplay tag to check for UPROPERTY(EditAnywhere, Category = "Filter") FGameplayTag RequiredTag; virtual bool PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const override; };

LyraInventoryFilter.cpp

// Copyright Cainoli. You can use me to fulfill your fantasies. All Rights Reserved. #include "LyraInventoryFilter.h" #include "LyraInventoryItemInstance.h" #include "LyraInventoryItemDefinition.h" bool ULyraInventoryFilter::PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const { // Base class implementation always returns true // Derived classes should override this to implement specific filtering logic return true; } bool ULyraInventoryFilter_Composite::PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const { if (Filters.Num() == 0) { // If no filters are specified, pass everything through return true; } for (const TObjectPtr<ULyraInventoryFilter>& Filter : Filters) { if (Filter == nullptr) { continue; } const bool bPassesFilter = Filter->PassesFilter(ItemInstance); if (bRequireAllFilters) { // AND operation - if any filter fails, return false if (!bPassesFilter) { return false; } } else { // OR operation - if any filter passes, return true if (bPassesFilter) { return true; } } } // If we're here and using AND, all filters passed // If we're here and using OR, no filters passed return bRequireAllFilters; } bool ULyraInventoryFilter_ItemDefinition::PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const { if (!ItemInstance || !ItemDefinitionClass) { return false; } return ItemInstance->GetItemDef() && ItemInstance->GetItemDef()->GetClass()->IsChildOf(ItemDefinitionClass); } bool ULyraInventoryFilter_HasTag::PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const { if (!ItemInstance || !RequiredTag.IsValid()) { return false; } // Check if the item instance has the required tag as a stat tag if (ItemInstance->HasStatTag(RequiredTag)) { return true; } return false; }

Конкретные фильтры: бери и пользуйся

На основе базового класса создадим три полезных фильтра:

ULyraInventoryFilter_ItemDefinition

Этот фильтр работает на уровне классов и позволяет искать предметы по их типам. Учитывает наследование — если фильтруете по родительскому классу, то и его наследники пройдут проверку.

ULyraInventoryFilter_Composite

Позволяет комбинировать несколько фильтров для создания сложных условий:

  • AND — все фильтры должны быть пройдены.
  • OR — хотя бы один фильтр должен быть пройден.

ULyraInventoryFilter_HasTag

Этот фильтр позволяет искать предметы по их тегам.

С таким инструментом можно создавать цепочки логики, как в лучших традициях спагетти-кода, только теперь красиво и модульно.

Добавляем фильтрацию в LyraInventoryManagerComponent

Теперь настала очередь добавить метод GetFilteredItems в ULyraInventoryManagerComponent. Этот метод принимает фильтр и возвращает массив предметов, которые ему соответствуют. Работает безопасно — если фильтр вдруг не передан, функция просто вернёт пустой массив и не вылетит, как это обычно бывает.

LyraInventoryManagerComponent.h

// Returns all items that pass the specified filter UFUNCTION(BlueprintCallable, Category="Lyra|Inventory") TArray<ULyraInventoryItemInstance*> GetFilteredItems(TSubclassOf<ULyraInventoryFilter> Filter) const;

LyraInventoryManagerComponent.cpp

TArray<ULyraInventoryItemInstance*> ULyraInventoryManagerComponent::GetFilteredItems(TSubclassOf<ULyraInventoryFilter> Filter) const { TArray<ULyraInventoryItemInstance*> Results; if (Filter == nullptr) { // If no filter is specified, return all items return InventoryList.GetAllItems(); } // Get all items and filter them TArray<ULyraInventoryItemInstance*> AllItems = InventoryList.GetAllItems(); for (ULyraInventoryItemInstance* Item : AllItems) { if (const auto* FilterDef = Filter.GetDefaultObject()) { if (FilterDef->PassesFilter(Item)) { Results.Add(Item); } } } return Results; }

Как это использовать: простота — сестра таланта

// Создание фильтра по типу предмета auto ItemFilter = NewObject<ULyraInventoryFilter_ItemDefinition>(); ItemFilter->ItemDefinitionClass = USpecificItemDefinition::StaticClass(); // Получение отфильтрованных предметов TArray<ULyraInventoryItemInstance*> FilteredItems = InventoryComponent->GetFilteredItems(ItemFilter); // Создание композитного фильтра auto CompositeFilter = NewObject<ULyraInventoryFilter_Composite>(); CompositeFilter->bRequireAllFilters = true; // AND операция // Добавление подфильтров CompositeFilter->Filters.Add(ItemFilter1); CompositeFilter->Filters.Add(ItemFilter2); // Получение предметов, которые проходят все фильтры TArray<ULyraInventoryItemInstance*> FilteredItems = InventoryComponent->GetFilteredItems(CompositeFilter);
Lyra Inventory Fix 01: Фильтрация предметов. Когда порядок — не роскошь, а необходимость.

Что в итоге?

Мы получили систему фильтрации, которая:

  • Проста в реализации и использовании.
  • Расширяема — можно создавать новые фильтры, просто наследуясь от ULyraInventoryFilter.
  • Гибкая — фильтры можно комбинировать с помощью ULyraInventoryFilter_Composite.
  • Дружественная к Blueprint — даже без C++ можно создавать кастомные фильтры.
  • Интегрирована с Lyra — использует существующую систему геймплейных тегов и легко вписывается в архитектуру проекта.

Теперь порядок в вашем инвентаре — не миф, а реальность. С такими фильтрами находить нужный предмет будет проще, чем потерять его!
#lyra #unrealengine #cpp #inventory

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