背景:在过去的一周里,我一直在使用Canvas和JavaScript开发基本上是多向Tron的游戏。我选择不在每一帧都清除画布,以便我的小线段留下痕迹。对于碰撞检测,我使用此功能:
// 8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1; //points just outside the circumference
var counter = 0;
var pixelData;
for (var i = 0; i < 16; i += 2) {
//collisionPixels[] is an array of 8 (x, y) offsets, spaced evenly around the center of the circle
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
pixelData = context.getImageData(x,y,1,1).data; //pixel data at each point
if (pixelData[3] != 0) {
counter++;
}
}
if (counter > 4) {
this.collision();
}
这里的目的是在刷点的表面周围获得8个像素的alpha值; alpha值为0仅在背景上。如果总数为8的碰撞像素数大于4(这包括玩家后面的踪迹),那么我调用collision()
方法。这个函数实际上运行得很好(这个IS在一个函数中,所以这些声明是本地的。)
问题在于context.getImageData()
使我的内存使用量大幅增加,并且在3或4场游戏中使用了帧速率。只需剪切该行并为其分配pixelData
一些其他值,即使在进行其他计算时,所有内容都会非常流畅地运行。
如何修复此内存泄漏?并且,如果这种类型的碰撞检测方法不那么复杂,那么它是什么?
编辑:根据要求,这是我的循环:
function loop() {
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
}
requestAnimationFrame(loop);
}
编辑2:所以我尝试了Patrick的UInt8ClampedArrays想法:
//8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1;
var counter = 0;
for (var i = 0; i < 16; i += 2) {
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
//translate into UInt8ClampedArray for data
var index = (y * canvas.width + x) * 4 + 3; //+3 so we're at the alpha index
if (canvasArray[index] != 0) {
counter++;
}
}
而且,在我的循环顶部,我添加了一个新的全局变量,每帧更新一次:
var canvasArray = context.getImageData(0,0,canvas.width,canvas.height).data;
希望我做得对。它有效,但你玩的每一轮的记忆和帧率都会变差。要上传一些堆快照。
编辑3:
快照1:https://drive.google.com/open?id=0B-8p3yyYzRjeY2pEa2Z5QlgxRUk&authuser=0
快照2:https://drive.google.com/open?id=0B-8p3yyYzRjeV2pJb1NyazY3OWc&authuser=0
快照1是在第一场比赛之后,2是在第二场比赛之后。
编辑4:试图限制帧速率:
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
//lastUpdate = now;
if (delta > interval) {
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
}
}
}
其中
interval = 1000 / fps;
它延迟了最终的性能损失,但是使用此选项,内存仍在攀升。
编辑5:虽然我确定必须有更好的方法,但我找到了一个合理的解决方案。将帧率限制在30左右实际上在长期性能方面有效,但我讨厌游戏看30 FPS的方式..所以我构建了一个循环,它具有一个无上限的帧速率,用于所有更新和渲染除了碰撞处理,我以30 FPS更新。
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
感谢您的回答..您的很多想法都进入了最终(?)产品,我对此表示赞赏。关闭这个帖子。
答案 0 :(得分:0)
您是否可以致电context.getImageData(0, 0, context.canvas.width, context.canvas.height).data
,以便您可以使用该单UInt8ClampedArray
而不是您使用的多个ImageData
?此外,当您完成图片数据(TypedArray
,而不是delete
里面的图片数据)时,您可以尝试在其上调用grep
,尽管我可以使用grep
。我不确定这是否会释放记忆。
答案 1 :(得分:0)
虽然我确信必须有更好的方法,但我找到了一个合理的解决方案。将帧率限制在30左右实际上在长期性能方面有效,但我讨厌游戏看30 FPS的方式..所以我构建了一个循环,它具有一个无上限的帧速率,用于所有更新和渲染除了碰撞处理,我以30 FPS更新。
//separate update cycle for collision detection
var collisionFPS = 30;
var lastCollisionUpdate;
var collisionInterval = 1000 / collisionFPS;
var canvasData;
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
可能不是最好的解决方案,但它是一致的。