首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >getImageData内存泄漏?

getImageData内存泄漏?
EN

Stack Overflow用户
提问于 2015-05-03 06:09:01
回答 2查看 1.1K关注 0票数 4

背景:在过去的一周里,我一直在使用Canvas和JavaScript开发一款本质上是多方向Tron的游戏。我选择不清除每一帧的画布,这样我的小线段就会留下痕迹。对于碰撞检测,我使用以下函数:

代码语言:javascript
复制
// 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()方法。这个函数实际上工作得很好(这是在一个函数内部,所以这些声明是局部的)。

问题是,context.getImageData()使我的内存使用量激增,在玩了3到4场游戏后,我的帧率降到了最低点。只要去掉这一行,并为pixelData赋一些其他值,就可以使一切运行得非常顺利,即使在进行其他计算时也是如此。

我如何修复这个内存泄漏?而且,如果有一种不那么复杂的方法来进行这种类型的碰撞检测,它是什么?

编辑:应请求,下面是我的循环:

代码语言:javascript
复制
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:所以我尝试了帕特里克的UInt8ClampedArrays点子:

代码语言:javascript
复制
//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++;
        }
    }

并且,在我的循环的顶部,我添加了一个新的全局变量,每帧更新一次:

代码语言:javascript
复制
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:尝试设置帧率上限:

代码语言:javascript
复制
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();
                }
            }
        }
    }
}

哪里

代码语言:javascript
复制
interval = 1000 / fps;

它延迟了最终的性能影响,但内存仍然随着这个选项的增加而攀升。

编辑5:虽然我确信一定有更好的方法,但我找到了一个工作得相当好的解决方案。将帧率限制在30左右实际上在长期性能方面是有效的,但我讨厌游戏看起来30 FPS的方式。所以我构建了一个循环,除了冲突处理之外,所有的更新和渲染都有一个无上限的帧率,我以30FPS更新了它。

代码语言:javascript
复制
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;
    }
}

谢谢你的回答..你的许多想法都进入了决赛(?)产品,我很欣赏这点。正在关闭此线程。

EN

回答 2

Stack Overflow用户

发布于 2015-05-03 06:15:41

在某些情况下,您是否可以调用context.getImageData(0, 0, context.canvas.width, context.canvas.height).data,以便可以使用单个UInt8ClampedArray而不是正在使用的多个use?此外,当您处理完图像数据(即ImageData,而不是其中的TypedArray )时,您可以尝试对其调用delete,尽管我不确定这是否会释放内存。

票数 0
EN

Stack Overflow用户

发布于 2015-05-04 19:06:29

虽然我确信一定有更好的方法,但我找到了一个相当有效的解决方案。将帧率限制在30左右实际上在长期性能方面是有效的,但我讨厌游戏看起来30 FPS的方式。所以我构建了一个循环,除了冲突处理之外,所有的更新和渲染都有一个无上限的帧率,我以30FPS更新了它。

代码语言:javascript
复制
//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;
    }
}

可能不是最好的解决方案,但它是一致的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30008032

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档