使碰撞检测更有效

时间:2015-09-14 17:49:41

标签: javascript html5-canvas collision-detection tile

我正在开发HTML5-canvas游戏,其中地图是随机生成的10px×10px图块,然后玩家可以挖掘并构建。图块存储在一个对象数组中,一个小图包含大约23000个图块。我的碰撞检测功能会检查玩家在每次穿过的所有非空格砖上的位置(使用requestAnimationFrame()),并且它工作正常,但我觉得它是CPU密集型的。碰撞功能如下(代码来自在线教程):

function colCheck(shapeA, shapeB) {
    var vX = (shapeA.x + (shapeA.width / 2)) - (shapeB.x + (shapeB.width / 2)),
    vY = (shapeA.y + (shapeA.height / 2)) - (shapeB.y + (shapeB.height / 2)),
    hWidths = (shapeA.width / 2) + (shapeB.width / 2),
    hHeights = (shapeA.height / 2) + (shapeB.height / 2),
    colDir = null;

    // if the x and y vector are less than the half width or half height, they we must be inside the object, causing a collision
    if (Math.abs(vX) < hWidths && Math.abs(vY) < hHeights) {         
        // figures out on which side we are colliding (top, bottom, left, or right)         
        var oX = hWidths - Math.abs(vX),             
            oY = hHeights - Math.abs(vY);         
        if (oX >= oY) {
            if (vY > 0) {
                colDir = "t";
                shapeA.y += oY;
            } else {
                colDir = "b";
                shapeA.y -= oY;
            }
        } else {
            if (vX > 0) {
                colDir = "l";
                shapeA.x += oX;
            } else {
                colDir = "r";
                shapeA.x -= oX;
            }
        }
    }
    return colDir;
};

然后在我的更新函数中,我以播放器和tile作为参数运行此函数:

for (var i = 0; i < tiles.length; i++) {
    //the tiles tag attribute determines rendering colour and how the player can interact with it ie. dirt, rock, etc. 
    //anything except "none" is solid and therefore needs collision
    if (tiles[i].tag !== "none") {
        dir = colCheck(player, tiles[i]);
        if (dir === "l"){
            player.velX = 0;
            player.jumping = false;
        } else if  (dir === "r") {
            player.velX = 0;
            player.jumping = false;
        } else if (dir === "b") {
            player.grounded = true;
            player.jumping = false;
        } else if (dir === "t") {
            player.velY *= -0.3;
        }
    }
};

所以我想知道的是,如果我只使用类似Math.abs(tiles[i].x - player.x) < 100y的条件检查距离玩家一定距离内的切片,是否应该使代码更有效率因为它会检查碰撞较少的瓷砖,或者检查额外的参数效率较低?

如果在没有测试的情况下很难说,我该如何找到代码的运行情况呢?

2 个答案:

答案 0 :(得分:0)

  

但我觉得它是CPU密集型的

CPU旨在快速完成很多工作。有数学可以确定算法的效率,看来您当前的实现是O(n)。如果将瓦片的数量减少到一个常数,您将获得O(1),这样会更好,但对于您的应用程序可能不会引人注意。要实现O(1),您必须保留X个最近区块的索引,并在最近区块更改时逐步更新索引。即如果玩家向右移动,您将修改索引,以便删除最左侧的图块列,并在右侧获得新的图块列。检查碰撞时,您只需遍历索引中固定数量的切片而不是整组切片。

  

...是否应该使代码更有效率,因为它会检查更少的磁贴碰撞,或者检查额外的参数效率不高?

回答这个问题的最佳方法是使用分析器,但我希望它会提高性能,尤其是在较大的地图上。这将是一个O(n)解决方案,因为您仍然迭代整个图块集,并且您可以想象当您的图块集接近无穷大时,性能将再次开始降级。您提出的解决方案可能是我在上面提出的O(1)解决方案之间的良好折衷。

您不想做的事情是过早优化代码。最好在实际遇到性能问题时进行优化,并且应该系统地进行优化,以便获得最大的收益。换句话说,即使你 有性能问题,碰撞检测也可能不是源。

  

如何查找代码的运行情况?

优化代码的最佳方法是attach a profiler并测量代码的哪些部分是CPU密集度最高的。当你弄清楚你的代码的哪一部分太慢时,要么自己找出解决方案,要么转到https://codereview.stackexchange.com/并询问如何改进那些性能不佳的代码部分并包括您的探查器信息和相关的代码部分。

答案 1 :(得分:0)

回应Samuel的回答,建议我使用探查器:

地图由数组中的~23 000个瓷砖组成: 原始碰撞代码运行了48%的时间。通过将if (tiles[i].tag !== "none")更改为以下内容,检查冲突所花费的时间减少到5%。

if (tiles[i].tag !== "none" 
    && Math.abs(tiles[i].x - player.x) < 200 
    && Math.abs(tiles[i].y - player.y) < 200)

地图由~18万个瓷砖组成: 原始碰撞代码运行时间为60-65%,游戏性能非常低,无法播放。使用更新的代码,碰撞功能仅运行0.5%的时间,但性能仍然很低,所以我认为即使检查更少的瓷砖进行碰撞,也有很多瓷砖检查它们相对于玩家的位置是否导致游戏运行缓慢。