使用Vector3.Distance进行性能问题

时间:2015-03-01 17:56:04

标签: c# performance unity3d

我正在开发一个系统,它将命令从HashSet分发到Player。我想将一个命令分发给最接近命令的播放器。

void AssignCommand(Player player, HashSet<Command> commandList) {
    //Player assigned;
    float min = float.MaxValue;
    float dist;

    foreach(Command command in commandList) {
        dist = Vector3.Distance(command.Position, player.Position);
        if(dist < min) {
            //check if command already assigned to another player
            assigned = command.assigned;
            if(assigned != null) {
                //reassign when distance is smaller
                if(dist < command.Distance(assigned)) {
                    //mark previously  assigned command as unassigned
                    if(player.activeCommand != null) player.activeCommand.assigned = null;
                    player.activeCommand = command;
                    command.assigned = player;
                    min = dist;

                    assigned.activeCommand = null;
                    AssignCommand(assigned, commandList);
                }
            }
            else {
                if(player.activeCommand != null) player.activeCommand.assigned = null;
                player.activeCommand = command;
                command.assigned = player;
                min = dist;
            }
        }
    }
}

我对这段代码的问题是,如果HashSet中有很多命令,它需要很长时间,并且我的机器上的帧速率从~60下降到大约~30 fps。这并不奇怪,因为Vector3.Distance方法只是为(every player) * (every command)调用,这太过分了。我现在正在寻找一种方法来减少以某种方式提高性能的调用次数。这里有什么想法吗?

我也尝试在不同的线程中运行此代码,但我放弃了,因为这正在改变并使用太多线程不安全值。我的最新尝试让我检查assigned != null是否会因为比较而抛出错误。

我会非常感谢提高此代码的整体速度或如何在ThreadPool中运行它的任何提示。如果需要,我还可以发布我为Thread尝试创建的JobClass。

3 个答案:

答案 0 :(得分:2)

所有线程解决方案和优化都很好,但您要记住的最重要的事情(对于此和未来)是:不要使用Vector3.DistanceVector3.magnitude永远。他们效率低下。

相反,使用Vector3.sqrMagnitude,这是相同的(用于距离比较),没有sqrt(最昂贵的部分)。

另一个优化是编写自己的(方形)距离计算,如果您不知道垂直距离,则抛出y值。我的距离比较代码很慢,所以我仔细测试了这一点,发现这是最快的方式(特别是如果你不关心垂直位置):

            tempPosition = enemy.transform.position; // declared outside the loop
            float xD = targetPosition.x - tempPosition.x;
            float yD = targetPosition.y - tempPosition.y; // optional
            float zD = targetPosition.z - tempPosition.z;
            float dist2 = xD*xD + yD*yD + zD*zD; // or xD*xD + zD*zD

编辑:另一项优化(你可能已经在做)只是在玩家移动时重新计算。我喜欢这个,因为它根本不会破坏数据。

答案 1 :(得分:1)

我为了统一编写了自己的System.Threading.Tasks版本,并添加了类似的内容,用于根据相机距离订购工作量。

基本上每当需要一个任务(或在你的case命令中)时,它就会将一个位置和任务传递给一个TaskManager,然后每个框架对它拥有的项目进行排序并运行它们。

你可能会做同样的/类似的事情,但不是像我在TaskManager上那样将命令传递给某种CommmandManager,而是在创建时查找并将命令传递给最接近相关点的玩家。

现在大多数人都把他们的场景图拉成类似四叉树的东西,这样可以让找到合适的玩家相当快,然后每个玩家都有责任执行自己的命令。

答案 2 :(得分:0)

好了几个小时处理这个问题后,我终于找到了一个解决方案,一方面提高性能,另一方面使其成为可线程的。我遇到了线程问题,因为播放器是Unity对象。而不是在任务中使用播放器对象,我只能在Start()方法中获得它的位置。因此,我设法使它成为可线程的,虽然我觉得很奇怪,我现在使用Vector3代表玩家的位置。

通过添加另一个存储每个玩家已计算距离的词典,确实可以提高性能。因此,当重新分配一个玩家时,距离不需要再次重新计算......我不确定这带来了多少性能,因为我和Thread一起测试了它,但至少我摆脱了一些距离调用回到stabel 60 fps!

此外,我通过递归搞砸了一些东西,这样我就可以为每个玩家提供100.000次递归..这不应该发生在o.O.修复很容易。简单地添加了一个minCommand命令,我在foreach期间设置并且只分配命令并触摸foreach之后的Sets ..我打赌代码现在会像糖一样运行,即使没有线程......