Как ускорить этот алгоритм перемещения?В JavaScript - PullRequest
5 голосов
/ 06 июня 2011


У меня есть массив из 16 бильярдных шаров в JS, и я хочу плавно перемещать каждый шар в зависимости от его направления и скорости.
Для этого я установил таймер, вызывая UpdateThis() каждые 42 мс (для 24 кадров в секунду)).
Проблема в том, что UpdateThis() принимает 53 мс в качестве состояния firebug.
Теперь UpdateThis перебирает каждый шар и вызывает UpdateBall(ball).
Я предполагаю, что проблема заключается именно в этом.
UpdateBall выглядит так:

function UpdateBall(ball)
{
if(ball.direction.x != 0 && ball.direction.y != 0) {
    //ball moving!
    for(var i = 0; i < balls.length; i++) {
        //CheckCollision(ball, balls[i]); //even without this it takes 53 ms!
    }

    var ps = VAdd(ball.position, VMul(ball.direction, ball.speed)); //Multiply Direction with speed and add to position!
    if(ps.x < Bx || ps.y < By || ps.x > Bw || ps.y > Bh) { //Bounce off the wall!
        ball.direction = VMul(ball.direction, -1); //Invert direction
        ball.speed *= 1;
        ps = VAdd(ball.position, VMul(ball.direction, ball.speed)); //Calc new position!
    }
    ball.position = ps;
    ball.MoveTo(); //See explanation at the bottom.
    ball.speed *= GRK; //Gravity
    if(ball.speed < 0.05) {
        ball.speed = 0;
    }
  }
}

кажется, что большинствовремя тратится в ball.MoveTo(), что выглядит следующим образом:

function() 
{
     this.image.style.left = this.position.x + "px";
     this.image.style.top = this.position.y + "px"; 
}


- ОБНОВЛЕНИЕ -

function UpdateThis() {
    for(var i = 0; i < balls.length; i++) {
         var cur = balls[i];
         UpdateBall(cur);
         balls[i] = cur;
    }
  }

, а нагрузка выглядит как

nx = setInterval(function() { UpdateThis(); }, 42);

Есть ли у кого-нибудь идеи о том, как ускорить это?

- ОБНОВЛЕНИЕ 2 -

Вы можете скачать папку с файлом HTML здесь (пароль - пароль)

Ответы [ 3 ]

2 голосов
/ 07 июня 2011

Как насчет отделения обновлений позиции от чертежа? Итак, что-то вроде этого (непроверенный код):

function DrawBall(ball)
{
    ball.MoveTo(); //Take this line out of UpdateBall
}

-

function UpdateThis() {
    for(var i = 0; i < balls.length; i++) {
         var cur = balls[i];
         UpdateBall(cur);
         balls[i] = cur;
    }
}

-

function DrawThis() {
    for(var i = 0; i < balls.length; i++) {
         DrawBall(balls[i]);
    }
    setTimeout(function() { DrawThis(); }, 42);
}

-

nx = setInterval(function() { UpdateThis(); }, 42);
setTimeout(function() { DrawThis(); }, 42);

Если действительно медленное перемещение позиции, таким образом, обновление логики по-прежнему происходит при 42 мс, а частота кадров не превышает 42 мс, но она может пропускать кадры. (Я на самом деле не пробовал это, так что это все теоретически, и вам, возможно, придется кое-что настроить)

2 голосов
/ 06 июня 2011

Почему движение может быть (и, скорее всего, медленным)?

Функциональность перемещения может быть медленной, потому что в ней есть больше вещей, чем простое назначение переменных. Это должно фактически сделать какой-то элемент в другом месте. Вы можете проверить это, если вы запустите это на IE9. Я ожидаю, что он должен работать быстрее, поскольку он использует аппаратное ускорение видео.

Что касается другой рутины, я надеюсь, что другие будут ее анализировать. :)

Вопросы к вам

  1. Не могли бы вы описать, как движутся шары? Спорадически? Как вы называете UpdateBall() для каждого шара? Вы ставите в очередь эти звонки?

  2. Обеспечивает VMul и VAdd функциональность

  3. Играли ли вы со стилем? Возможно, относительное расположение непосредственного родителя шаров может ускорить рендеринг. И настройте overflow:hidden на него. Я не знаю. Зависит от того, как ты это сделал. Следовательно, JSFiddle был бы очень полезен.

Предложение

Вместо того, чтобы использовать setInterval для вызова вашей функции, вы, возможно, должны просто поставить их в очередь и позволить им исполняться так быстро, как только возможно. И просто ради этого, предоставьте центральному setInterval несколько наблюдателей , чтобы они не работали слишком быстро.

Но я думаю, что он все еще использует ваш процессор на 100%, что в любом случае нехорошо.

Очень важное примечание : Не запускайте ваше приложение, когда Firebug включен, потому что общеизвестно, что Javascript выполняется намного медленнее, когда работает Firebug.

1 голос
/ 06 июня 2011

Это сложно, если MoveTo () на самом деле является вашим узким местом, так как там не так много всего происходит. О единственных вещах, о которых я могу думать, сразу же, это

1) Кэшируйте свойство стиля изображения и позиции для более быстрого поиска. Каждый раз, когда вы видите точку в цепочке объектов, требуется пройти через цепочку областей видимости. В идеале вы можете кэшировать это свойство во время создания родительского объекта MoveTo ().

2) Требуются ли строки 'px'? Это может привести к неверной спецификации CSS, но все еще может работать. Мне трудно поверить, что 2-х струнный конкат действительно все так сильно изменит.

Основная проблема здесь, вероятно, заключается в том, что каждый раз, когда вы меняете DOM, браузеры повторно отображают всю страницу. Единственным другим вариантом может быть рефакторинг, чтобы вместо изменения стилей вы фактически удалили предыдущее содержимое и заменили его фрагментом документа, описывающим новое состояние. Это приведет к 2 повторным потокам за весь этап (1 для удаления, 1 для добавления) вместо 2 для каждого шара.

РЕДАКТИРОВАТЬ: Что касается № 1 выше, когда я говорю кэш, я имею в виду не только локально в вызове функции. Но, возможно, как замыкание в родительском объекте. Например:

var Ball = function(img){
    var style = img.style;
    var posX;
    var posY;

    function MoveTo(){
        style.left = posX + "px";
        style.right = posY + "px"; 
    }
};
...