在JavaScript中查找对象数组中的数字的快速方法

时间:2014-04-19 16:58:20

标签: javascript arrays html5 object side-scroller

我需要检查一个物体是否在远处以显示在屏幕上(它是一个侧卷轴视频游戏)

到目前为止我有这个:

for ( var i=0; i < Coins.length; i++ )
{
    var obj = Coins[i];


    if ( worldObj.distance > obj.distance && worldObj.distance < obj.distance + canvas.height )
    {

        DrawCoins(obj);
    }
}

worldObj.distance是玩家行进的距离,obj.distance是物体的距离。

问题:

由于级别中的硬币数量(超过10,000)并且每秒执行60次(60 fps),此for循环会导致移动设备性能下降。

我该如何解决这个问题?

谢谢! :)

编辑:尝试在循环之前将canvas.height缓存到变量(例如:var height = canvas.height;)中。没有性能差异(I5 2500K上44毫秒对44毫秒,想象一下移动!!)。

编辑:在循环之前尝试缓存Coins.length(例如:var len = Coins.length;)。没有性能差异(44毫秒对44毫秒)。

编辑:这是我创造硬币的方式:

for(var i=0; i<10000; i++)
{

    /* Coins */
    for ( var z=0; z < 6; z++ )
    {
        if ( RNG(0,100) < Upgrades.coinChance ) // random number generator, Upgrades.coinChance = 5; -> 5% chance
        {
            Coins.push({ active: 1, type: "Gold", cash: 5, x: 60*(z+1), distance: i*RNG(20,100) });
        }
    }
}

1 个答案:

答案 0 :(得分:0)

好的。我做了一些数字处理并提出了两种算法,在它们之间进行权衡。

作为测试平台,我正在运行以下初始化代码:

var n,m;

var Coin=function(x)
{
    return {x:x,r:1};
}
var coins=[];
var map={};

var viewSize=50;
var worldSize=1000;

n=100000;
while(n--)
{
    coins[n]=Coin(worldSize*Math.random());
}

如你所见,这里有10万个硬币。我知道,你答案中的规定超过你的规定,但我想对事情进行压力测试。

第一个算法是您在问题中发布的算法的某种优化版本。它只是循环并测试每个硬币的最近边缘是否距离某个点viewSize/2的距离小x。如果是这样,它会将它添加到一个最终返回的数组中。

var checkCoins1=function(list,x,w)
{
    var selected,k,n,no;
    selected=[];
    n=list.length;
    while(n--)
    {
        no=list[n];
        if(Math.abs(no.x-x)<w/2+no.r)
        {
            selected.push(no);
        }
    }
    return selected;
};

使用我们的100000个硬币,此方法不需要额外的时间来设置和每次执行7毫秒。绝对是动画循环中的成本,但却是可管理的。

第二种算法使用地图。它首先对硬币列表进行排序,然后在worldSize之间选择一组点,每一点与最后一个点分开viewSize。它创建一个以这些点为键的对象。然后它循环遍历所有硬币并保存最靠近对象中每个点的索引。这需要一些时间,但只需要完成一次。当实际循环运行时,它只是在查看窗口的左边(或者更低,视情况而定)边缘之前找到地图中的点。然后它循环通过列表中的所有硬币,将它们保存到阵列中,直到它到达比观察窗口的右(或上)边缘更远的硬币。然后它停止并返回数组。这样,它不必检查列表中的每个硬币,而只需检查其中的几个。

设置代码如下所示:

coins.sort(
    function(a,b)
    {
        return -(a.x<b.x)+(a.x>b.x);
    }
);
n=Math.floor(worldSize/viewSize);
while(n--)
{
    map[n*viewSize]=undefined;
}
n=coins.length;
while(n--)
{
    for(m in map)
    {
        if(map[m]===undefined || Math.abs(coins[n].x-m)<Math.abs(coins[map[m]].x-m))
        {
            map[m]=n;
        }
    }
}

主循环看起来像这样:

var checkCoins2=function(list,map,x,w)
{
    var selected,y,k,n,no;
    selected=[];
    y=x-w/2;
    n=map[Math.floor(y/w)*w];
    while(1)
    {
        no=list[n++];
        if(Math.abs(no.x-x)<w/2+no.r)
        {
            selected.push(no);
        }
        else if(no.x>x)
        {
            break;
        }
    }
    return selected;
};

初始化需要高达900毫秒,但每次运行时循环只需1毫秒。随着worldSizeviewSize之间的比率增加,循环将变得越来越快。

总的来说,我会说第一个算法更简单,但是如果你发现自己在动画循环中按下了时间并且在游戏(或级别)加载时可能需要一秒钟来初始化,那么你应该使用第二个算法

也许,如果您知道硬币的位置,您甚至可以在编写代码时预先对列表进行排序并预先构建地图。然后你根本不需要客户端进行初始化。

如果您有任何疑问,请询问!