2

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

到目前为止,我有这个:

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 个),此 for 循环会导致移动设备上的性能大幅下降,并且每秒执行 60 次(60 fps)

我怎样才能解决这个问题?

谢谢!:)

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

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

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

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) });
        }
    }
}
4

1 回答 1

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());
}

如您所见,这里有 100000 个硬币。我知道,比你在回答中规定的要多,但我想对事情进行压力测试。

第一个算法是您在问题中发布的算法的某种优化版本。它只是循环并测试每个硬币的最近边缘是否小于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;
};

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

第二种算法使用地图。它首先对硬币列表进行排序,然后选择一组点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 毫秒。随着 和 之间的比率worldSize增加viewSize,循环将变得越来越快。

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

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

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

于 2014-04-19T20:53:05.037 回答