Предыстория: За последнюю неделю я работал над игрой, которая является по существу многонаправленной Трон, используя Canvas и JavaScript. Я решил не очищать Canvas в каждом фрейме, чтобы мои маленькие сегменты линии не оставляли след. Для обнаружения столкновения, я использую эту функцию:Ошибка памяти getImageData?
// 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 пикселей вокруг поверхности brushpoint в; альфа-значения 0 находятся только на фоне. Если количество сталкивающихся пикселей из общего числа 8 больше 4 (это включает в себя след за игроком), то я вызываю метод collision()
. Эта функция действительно работает очень хорошо (и это внутри функции, поэтому эти объявления являются локальными).
Проблема заключается в том, что context.getImageData()
повышает скорость использования памяти, а после 3 или 4-х игр танков - частота кадров. Вырезание только этой строки и присвоение pixelData
некоторых других значений заставляет все работать очень плавно, даже при выполнении других вычислений.
Как исправить эту утечку памяти? И, если есть менее запутанный способ обнаружения столкновения этого типа, что это такое?
EDIT: по желанию, вот мой цикл:
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);
}
EDIT 2: Так что я попытался 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 после того, как второй.
EDIT 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;
Это задерживает возможное падение производительности, но память все еще поднимается с этой опцией.
EDIT 5: Хотя я уверен, что должен быть лучший способ, я нашел решение, которое работает достаточно хорошо. Ограничивая частоту кадров около 30, фактически работал с точки зрения долгосрочной производительности, но я ненавидел то, как игра выглядела на 30 FPS .. поэтому я построил цикл, у которого была необработанная частота кадров для всего обновления и рендеринга EXCEPT для обработки столкновений, которые я обновлено на 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;
}
}
Спасибо за ответы .. многие ваши идеи нашли свой путь в конечном (?) Продукте, и я ценю это. Закрытие этой темы.
Что означает «skyrockets», более или менее? Можете ли вы предоставить пример выполнения, который показывает проблему? – Oriol
Я не вижу причин для утечки памяти в этом коде, но код неэффективен, поэтому я подозреваю, что проблема в том, что ваша игра очень плотная, и у вас мало времени для GC. Не могли бы вы показать нам, как вы вызываете цикл (например, используете ли вы setTimeout/Interval, requestAnimationFrame ...)? – K3N
@ KenFyrstenberg Отредактировано в описании; это requestAnimationFrame(). Сегодня вечером я попробую сделать фрейм-шапку, чтобы увидеть, дает ли это время GC. –