You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检查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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 23:15:53