2015-05-02 3 views
4

Предыстория: За последнюю неделю я работал над игрой, которая является по существу многонаправленной Трон, используя 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; 
    } 
} 

Спасибо за ответы .. многие ваши идеи нашли свой путь в конечном (?) Продукте, и я ценю это. Закрытие этой темы.

+0

Что означает «skyrockets», более или менее? Можете ли вы предоставить пример выполнения, который показывает проблему? – Oriol

+0

Я не вижу причин для утечки памяти в этом коде, но код неэффективен, поэтому я подозреваю, что проблема в том, что ваша игра очень плотная, и у вас мало времени для GC. Не могли бы вы показать нам, как вы вызываете цикл (например, используете ли вы setTimeout/Interval, requestAnimationFrame ...)? – K3N

+0

@ KenFyrstenberg Отредактировано в описании; это requestAnimationFrame(). Сегодня вечером я попробую сделать фрейм-шапку, чтобы увидеть, дает ли это время GC. –

ответ

0

Есть ли какой-то момент, когда вы можете позвонить по номеру context.getImageData(0, 0, context.canvas.width, context.canvas.height).data, чтобы использовать этот номер UInt8ClampedArray вместо того, чтобы использовать его. Также, когда вы закончите с данными изображения (ImageData, а не TypedArray), вы можете попробовать позвонить на нем delete, хотя я не уверен, что это освободит память.

+0

Я попытался запустить удаление, и он не освобождает память. Но первая идея звучит многообещающе: не слишком хорошо знакомы с UInt8ClampedArrays, поэтому я вернусь к вам. –

+0

@JackBritton Ваш '' pixelData'' является '' UInt8ClampedArray'', просто для уточнения. –

0

Хотя я уверен, что должен быть лучший способ, я нашел решение, которое работает достаточно хорошо. Ограничивая частоту кадров около 30, фактически работал с точки зрения долгосрочной производительности, но я ненавидел то, как игра выглядела на 30 FPS .. поэтому я построил цикл, у которого была необработанная частота кадров для всего обновления и рендеринга EXCEPT для обработки столкновений, которые я обновлено на 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; 
    } 
} 

Не может быть лучшим решением, но оно непротиворечиво.