如何检查FTransform数组是否存在重复并处理重复项?
Unreal Engine中FTransform数组重复项检查与优化方案
需求说明
我需要处理一个FTransform类型的数组,核心需求是:
- 检查数组中是否存在重复项
- 若存在重复,要么移除重复项,要么在指定范围内重新随机选择不重复的项
原始实现代码与错误
我定义了MyActor类来实现随机选择逻辑,但运行时出现错误。
MyActor.h
void RandomLocation(TArray<FTransform>& Arr, FTransform& Row);
MyActor.cpp
void AMyActor::RandomLocation(TArray<FTransform>& Arr, FTransform& Row) { // 注:原代码误用了Available变量,应为Arr.Num()-1 Row = Arr[FMath::RandRange(0, Arr.Num() - 1)]; } // 测试逻辑 void TestRandomTransform() { FTransform RowLocal {}; TArray<FTransform> ArrLocal {}; // 存储0-99范围的Transform列表 TArray<FTransform> TempArrLocal {}; // 记录已选的Transform RandomLocation(ArrLocal, RowLocal); if (TempArrLocal.Find(RowLocal) == -1) { UE_LOG(LogTemp, Log, TEXT("未找到重复项")); } else { UE_LOG(LogTemp, Log, TEXT("找到重复项,重新随机选择")); RandomLocation(ArrLocal, RowLocal); } }
运行错误
错误等级 错误代码 C2678 二进制‘==’:未找到接受左操作数类型为‘const FTransform’的运算符(或无可接受的转换)
临时解决方案(不满意)
通过自定义谓词逐个比较Transform的位置、旋转和缩放:
if (ArrLocal.ContainsByPredicate([RowLocal](const FTransform Transform) { return Transform.GetLocation() == RowLocal.GetLocation() && Transform.GetRotation() == RowLocal.GetRotation() && Transform.GetScale3D() == RowLocal.GetScale3D(); })) { UE_LOG(LogTemp, Log, TEXT("找到重复项,重新随机选择")); RandomLocation(ArrLocal, RowLocal); } else { UE_LOG(LogTemp, Log, TEXT("未找到重复项")); }
优化解决方案
方案1:重载FTransform的==运算符
通过重载==运算符,让UE容器(TArray、TSet)可以直接比较FTransform对象,简化代码。
在项目公共头文件(如MyActor.h或自定义工具头)中添加:
#include "Math/Transform.h" // 重载==运算符,判断两个FTransform是否完全相等 // 可选:使用Equals方法添加浮点精度容错 bool operator==(const FTransform& A, const FTransform& B) { return A.GetLocation().Equals(B.GetLocation(), KINDA_SMALL_NUMBER) && A.GetRotation().Equals(B.GetRotation(), KINDA_SMALL_NUMBER) && A.GetScale3D().Equals(B.GetScale3D(), KINDA_SMALL_NUMBER); }
之后可直接使用TArray的Find或Contains方法:
if (!TempArrLocal.Contains(RowLocal)) { UE_LOG(LogTemp, Log, TEXT("未找到重复项")); } else { UE_LOG(LogTemp, Log, TEXT("找到重复项,重新随机选择")); RandomLocation(ArrLocal, RowLocal); }
方案2:使用TSet实现高效去重与重复检查
TSet的查找效率远高于TArray(O(1) vs O(n)),适合频繁检查重复的场景,但需要为FTransform提供哈希函数。
添加哈希函数:
#include "Misc/Hash.h" // 为FTransform生成哈希值,用于TSet存储 uint32 GetTypeHash(const FTransform& Transform) { uint32 Hash = 0; Hash = HashCombine(Hash, GetTypeHash(Transform.GetLocation())); Hash = HashCombine(Hash, GetTypeHash(Transform.GetRotation())); Hash = HashCombine(Hash, GetTypeHash(Transform.GetScale3D())); return Hash; }
高效随机选择不重复项
TSet<FTransform> SelectedTransforms; void AMyActor::RandomUniqueLocation(TArray<FTransform>& Arr, FTransform& OutRow) { // 所有项已选过,清空集合重新开始 if (Arr.Num() == 0 || SelectedTransforms.Num() == Arr.Num()) { SelectedTransforms.Empty(); } // 循环直到选到未使用的项 do { OutRow = Arr[FMath::RandRange(0, Arr.Num() - 1)]; } while (SelectedTransforms.Contains(OutRow)); // 记录已选项 SelectedTransforms.Add(OutRow); }
快速移除数组中的重复项
// 将TArray转换为TSet自动去重,再转回TArray TSet<FTransform> TransformSet(ArrLocal); ArrLocal.Empty(); ArrLocal.Append(TransformSet.Array());
方案3:洗牌法避免重复选择
如果目标是从数组中随机选择不重复的项,可先对数组洗牌,再按顺序取元素,完全避免重复检查:
void AMyActor::ShuffleAndSelect(TArray<FTransform>& Arr, TArray<FTransform>& OutSelected, int32 NumToSelect) { // 先洗牌 Arr.Shuffle(); // 取前NumToSelect个元素(确保不超过数组长度) int32 TakeCount = FMath::Min(NumToSelect, Arr.Num()); OutSelected.Append(Arr.GetData(), TakeCount); }
内容的提问来源于stack exchange,提问作者Jasmine Kakarva
相关产品推荐
相关产品推荐

