前言
Filter 筛选器,是个很常见的工具,在各种游戏引擎中都有对应的工具。
比如 UE 的 Content Browser。
这里我们不讨论 UI 层面的实现,只按照 Filter 的逻辑,实现相关的逻辑。
原理分析
如上图,UE 的筛选器,和其他概念的筛选器,根本就是一大堆枚举状态的融合。
比如有一个 enum,有各种的标志。
enum class EFlags: uint32
{
None = 0
New,
Pending,
Running,
Stop
};
如果想让一个 enum 对象拥有多个 Flag 呢?比如 New、Pending、Stop。
这个时候就需要使用二进制了。
我们可以指定
New = 0001
Pending = 0010
Running = 0100
Stop = 1000
如果我们想筛选出既是 New 的,又是 Stop 的,只需要把他们对应的数值 按位或,也就是 | 就可以了。
New | Stop = 0001 | 1000 = 1001
代码实现
这里使用的是 UE C++
先定义一个 Flags 的模板类,重载对应的运算符。
template<typename Enum>
struct TEnumClassFlagsHelper
{
// 确保 Enum 是一个枚举类
static_assert(TIsEnumClass<Enum>::Value, "Enum must be an enum class type");
// 定义按位操作符
friend Enum operator|(Enum lhs, Enum rhs)
{
return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
}
friend Enum operator&(Enum lhs, Enum rhs)
{
return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
}
friend Enum operator~(Enum lhs)
{
return static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(lhs));
}
friend Enum& operator|=(Enum& lhs, Enum rhs)
{
lhs = lhs | rhs;
return lhs;
}
friend Enum& operator&=(Enum& lhs, Enum rhs)
{
lhs = lhs & rhs;
return lhs;
}
};
再写操作二进制的子类。
当然你们也可以把两个类的内容合在一起。我这样写是为了方便其他不适用二进制Flags的枚举继承自TEnumClassFlagsHelper,就不用每个 enum 都写对应的重载了。
template<class Enum>
class TBitwiseFlags : TEnumClassFlagsHelper<Enum>
{
// 确保继承自 uint32
static_assert(std::is_same<typename std::underlying_type<Enum>::type, uint32>::value, "The underlying type of Enum must be uint32");
public:
bool HasAnyFlags(Enum CheckFlags) { return static_cast<uint32>(CurrentFlags & CheckFlags) != 0; }
bool EqualsFlags(Enum CheckFlags) { return CurrentFlags == CheckFlags; }
void AddFlags(Enum AddedFlags) { CurrentFlags |= AddedFlags; }
void RemoveFlags(Enum RemovedFlags) { CurrentFlags &= ~RemovedFlags; }
Enum LowFlag() { return CurrentFlags & -CurrentFlags; }
void SetCurrentFlags(Enum NewFlags) { CurrentFlags = NewFlags; }
Enum GetCurrentFlags() { return CurrentFlags; }
private:
Enum CurrentFlags;
};
最后再写对应的类。
enum class EFlags: uint32
{
None = 0
New,
Pending,
Running,
Stop
};
class TestClass
{
TBitwiseFlags<EFlags> CurrentFlags;
};
用起来就很简单了。
CurrentFlags.SetCurrentFlags(EFlags::None);
CurrentFlags.HasAnyFlags(EFlags::New | EFlags::Stop);
CurrentFlags.AddFlags(EFlags::New | EFlags::Stop);