-
Автор темы
- #1
Разработчики из Epic Games в очередной раз оставили нам полуготовую систему — на этот раз речь про фильтры инвентаря в LyraInventoryManagerComponent.cpp. Тут, как говорится, «собака порылась»: закомментированы строки с ULyraInventoryFilter и его производным классом ULyraInventoryFilter_HasTag. Видно, хотели сделать хорошо, но Ctrl+C нажали, а вот Ctrl+V забыли. Что ж, давайте доделаем эту систему, а заодно придумаем, как с ней можно будет работать без головной боли.
В своих потугах использую UE5.5.0
Базовый функционал: ULyraInventoryFilter
В первую очередь создаём ULyraInventoryFilter — абстрактный класс, задающий интерфейс для фильтрации предметов. Главное в нём — виртуальный метод PassesFilter, который позволяет проверять, проходит ли предмет через фильтр. Производные классы смогут его переопределять под свои нужды. Плюс, поддержим создание фильтров в Blueprint, чтобы даже те, кто избегает C++ как чумы, могли с этим работать.
Конкретные фильтры: бери и пользуйся
На основе базового класса создадим три полезных фильтра:
ULyraInventoryFilter_ItemDefinition
Этот фильтр работает на уровне классов и позволяет искать предметы по их типам. Учитывает наследование — если фильтруете по родительскому классу, то и его наследники пройдут проверку.
ULyraInventoryFilter_Composite
Позволяет комбинировать несколько фильтров для создания сложных условий:
Добавляем фильтрацию в LyraInventoryManagerComponent
Теперь настала очередь добавить метод GetFilteredItems в ULyraInventoryManagerComponent. Этот метод принимает фильтр и возвращает массив предметов, которые ему соответствуют. Работает безопасно — если фильтр вдруг не передан, функция просто вернёт пустой массив и не вылетит, как это обычно бывает.
Как это использовать: простота — сестра таланта
Пример на C++:
Пример создания композитного фильтра:
Что в итоге?
Мы получили систему фильтрации, которая:
В своих потугах использую 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 "LyraInventoryFilter.generated.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 — хотя бы один фильтр должен быть пройден.
Добавляем фильтрацию в 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 — использует существующую систему геймплейных тегов и легко вписывается в архитектуру проекта.
Последнее редактирование: