我需要检查一个物体是否在远处以显示在屏幕上(它是一个侧卷轴视频游戏)
到目前为止我有这个:
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) });
}
}
}
答案 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毫秒。随着worldSize
和viewSize
之间的比率增加,循环将变得越来越快。
总的来说,我会说第一个算法更简单,但是如果你发现自己在动画循环中按下了时间并且在游戏(或级别)加载时可能需要一秒钟来初始化,那么你应该使用第二个算法
也许,如果您知道硬币的位置,您甚至可以在编写代码时预先对列表进行排序并预先构建地图。然后你根本不需要客户端进行初始化。
如果您有任何疑问,请询问!