-
Автор темы
- #1
Epic Games снова кинули нас, оставив в LyraInventoryManagerComponent.cpp полуфабрикат. Очередной недоношенный ребёнок разработки — фильтры инвентаря.
Вот где зарыта собака: закомментированные строки с ULyraInventoryFilter и его производным ULyraInventoryFilter_HasTag. Код как бы есть, но его как бы нет. Будто кто-то начал делать, отвлёкся на мемы в Твиттере и забыл допилить. Нам остаётся только дописать этот недостающий кусок, чтобы он не оставил нас с головной болью, а работал как надо.
В своих потугах будем использовать UE5.5.0
Базовый функционал: ULyraInventoryFilter
Начнём с основ. ULyraInventoryFilter — это абстрактный класс, задающий интерфейс для фильтрации предметов. Его сердце — виртуальный метод PassesFilter. Он решает, проходит ли объект проверку или идёт лесом. Наследники смогут переопределять его, задавая свои правила отбора.
И да, добавим поддержку Blueprint. Потому что даже те, кто боится C++ больше, чем налоговую, должны иметь возможность ковыряться в этом механизме.
Конкретные фильтры: бери и пользуйся
Теперь натягиваем на скелет мясо. Берём базовый класс и клепаем три полезных фильтра:
ULyraInventoryFilter_ItemDefinition
Фильтр для элиты. Работает на уровне классов и позволяет искать предметы по их типам. Учитывает наследование — если фильтруете по родительскому классу, то и его наследники пройдут проверку.
ULyraInventoryFilter_HasTag
Прямолинейный, как лом. Ищет предметы по тегам. Если тег есть — пропускает, если нет — идёт лесом.
ULyraInventoryFilter_Composite
Мозговитый. Позволяет комбинировать несколько фильтров, создавая сложные условия.
Добавляем фильтрацию в LyraInventoryManagerComponent
Теперь на сцену выходит GetFilteredItems — метод в ULyraInventoryManagerComponent, который принимает фильтр и возвращает отфильтрованный список предметов.
Работает безопасно — фильтр не передан? Не беда, просто вернёт пустой массив, а не уйдёт в закат с ошибкой. Никаких неожиданных вылетов, только контроль и порядок.
Как это использовать: простота — сестра таланта
Пример на C++:
Пример создания композитного фильтра:

Что в итоге?
Мы не просто подчинили фильтры — мы сделали их такими, какими они должны были быть с самого начала.
Вот где зарыта собака: закомментированные строки с ULyraInventoryFilter и его производным ULyraInventoryFilter_HasTag. Код как бы есть, но его как бы нет. Будто кто-то начал делать, отвлёкся на мемы в Твиттере и забыл допилить. Нам остаётся только дописать этот недостающий кусок, чтобы он не оставил нас с головной болью, а работал как надо.
В своих потугах будем использовать 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 "UObject/NoExportTypes.h"
#include "System/GameplayTagStack.h"
#include "GameplayTagContainer.h"
#include "LyraInventoryItemInstance.h"
#include "LyraInventoryFilter.generated.h"
class ULyraInventoryItemInstance;
struct FGameplayTag;
/**
* Base class for inventory filters
*/
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;
};
/**
* Filter that checks if an item has a 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;
};
/**
* 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;
};
/**
* 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;
};
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_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
return ItemInstance->HasStatTag(RequiredTag);
}
bool ULyraInventoryFilter_ItemDefinition::PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const
{
if (!ItemInstance || !ItemDefinitionClass)
{
return false;
}
return ItemInstance->GetItemDef() == ItemDefinitionClass;
}
bool ULyraInventoryFilter_Composite::PassesFilter_Implementation(const ULyraInventoryItemInstance* ItemInstance) const
{
if (!ItemInstance || Filters.Num() == 0)
{
return false;
}
for (const TObjectPtr<ULyraInventoryFilter>& Filter : Filters)
{
if (!Filter)
{
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 require all filters and haven't returned false yet, all filters passed
// If we require any filter and haven't returned true yet, no filter passed
return bRequireAllFilters;
}
Теперь натягиваем на скелет мясо. Берём базовый класс и клепаем три полезных фильтра:
ULyraInventoryFilter_ItemDefinition
Фильтр для элиты. Работает на уровне классов и позволяет искать предметы по их типам. Учитывает наследование — если фильтруете по родительскому классу, то и его наследники пройдут проверку.
ULyraInventoryFilter_HasTag
Прямолинейный, как лом. Ищет предметы по тегам. Если тег есть — пропускает, если нет — идёт лесом.
ULyraInventoryFilter_Composite
Мозговитый. Позволяет комбинировать несколько фильтров, создавая сложные условия.
- AND — все фильтры должны быть пройдены.
- OR — достаточно одного совпадения.
Добавляем фильтрацию в LyraInventoryManagerComponent
Теперь на сцену выходит GetFilteredItems — метод в ULyraInventoryManagerComponent, который принимает фильтр и возвращает отфильтрованный список предметов.
Работает безопасно — фильтр не передан? Не беда, просто вернёт пустой массив, а не уйдёт в закат с ошибкой. Никаких неожиданных вылетов, только контроль и порядок.
LyraInventoryManagerComponent.h:
public:
// 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;
}
Пример на C++:
cpp:
// Создание фильтра по типу предмета
auto ItemFilter = NewObject<ULyraInventoryFilter_ItemDefinition>();
ItemFilter->ItemDefinitionClass = USpecificItemDefinition::StaticClass();
// Получение отфильтрованных предметов
TArray<ULyraInventoryItemInstance*> FilteredItems = InventoryComponent->GetFilteredItems(ItemFilter);
cpp:
// Создание композитного фильтра
auto CompositeFilter = NewObject<ULyraInventoryFilter_Composite>();
CompositeFilter->bRequireAllFilters = true; // AND операция
// Добавление подфильтров
CompositeFilter->Filters.Add(ItemFilter1);
CompositeFilter->Filters.Add(ItemFilter2);
// Получение предметов, которые проходят все фильтры
TArray<ULyraInventoryItemInstance*> FilteredItems = InventoryComponent->GetFilteredItems(CompositeFilter);

Что в итоге?
Мы не просто подчинили фильтры — мы сделали их такими, какими они должны были быть с самого начала.
- Просто — код не вызывает когнитивный диссонанс, а пользоваться им можно без ритуалов с бубном.
- Расширяемо — хочешь новый фильтр? Просто наследуйся от ULyraInventoryFilter и делай, что вздумается.
- Гибко — комбинируй фильтры в ULyraInventoryFilter_Composite и строй логические конструкции уровня «убийца-головоломка».
- Доступно для Blueprint — даже если C++ для тебя тёмный лес, ты всё равно сможешь собрать свой фильтр без боли.
- Грамотно встроено в Lyra — использует систему геймплейных тегов, идеально ложится в архитектуру проекта.
Пожалуйста, авторизуйтесь для просмотра ссылки.
Последнее редактирование: