如何加速这个移动算法?在 JavaScript 中

发布于 2024-11-14 13:02:53 字数 1799 浏览 3 评论 0原文


我在 JS 中有一个由 16 个台球组成的数组,并且希望以其方向和速度平滑地移动每个球。
为此,我设置了一个计时器,每 42 毫秒调用一次 UpdateThis()(对于 24 fps)。
问题是 UpdateThis() 需要 53ms 作为 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;
    }
  }

和 onload 看起来像

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

有人对如何加快速度有任何想法吗?

-- 更新 2 --

您可以在此处下载包含 HTML 文件的文件夹(密码就是密码)

I have an Array of 16 billiard balls in JS and want to move each ball smoothly with its direction and speed.
For that I set up a timer, calling UpdateThis() every 42ms (for 24 fps).
The problem is that UpdateThis() takes 53ms as firebug states.

Now UpdateThis iterates over every ball and calls UpdateBall(ball).
I assume that the problem lies there.
UpdateBall looks like this:

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;
    }
  }
}

it seems that the most time is spent in ball.MoveTo() which looks like this:

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

-- UPDATE --

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

and onload looks like

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

Does somebody have any ideas on how to speed this up?

-- UPDATE 2 --

You can download the folder with the HTML file here (the password is password)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情绪操控生活 2024-11-21 13:02:53

将位置更新与绘图分开怎么样?所以有这样的东西(未经测试的代码):

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);

如果确实是位置的移动很慢,这样逻辑更新仍然发生在 42ms,并且帧率不会快于 42ms,但它可以跳帧。 (我实际上没有尝试过,所以这都是理论上的,你可能需要调整一些东西)

What about separating the position updates from the drawing? So have something like this (untested code):

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);

If indeed it's the moving of the position that's slow, this way the logic update still happens at 42ms, and the framerate is no faster than 42ms but it can skip frames. (I haven't actually tried this, so this is all theoretical and you may need to tweak some stuff)

动听の歌 2024-11-21 13:02:53

为什么移动可能(而且很可能)缓慢?

移动功能可能会很慢,因为它比简单的变量分配有更多的事情要做。它必须实际将某些元素渲染到其他地方。如果你在 IE9 上运行它,你可以测试一下。我预计它应该运行得更快,因为它使用硬件视频加速。

至于其他的套路希望大家剖析一下。 :)

有个问题要问你

  1. 你能描述一下球是如何移动的吗?偶尔?如何为每个球调用 UpdateBall() ?您对这些呼叫进行排队吗?

  2. 提供VMulVAdd功能

  3. 你有吗玩造型?也许球的直接父级的相对定位可能会加快渲染速度。并对其设置 overflow:hidden 。我不知道。取决于你是如何做到的。因此 JSFiddle 会非常有帮助。

一个建议

而不是使用 setInterval 来调用你的函数,你应该只是将它们排队并让它们尽可能快地执行。并且只是为了它,提供一个中央 setInterval 和一些观察者,它们运行得不会太快。

但我猜它仍然 100% 使用你的处理器,这无论如何都不好。

非常重要的注意事项:不要在 Firebug 启用时运行您的应用程序,因为众所周知,Firebug 运行时 Javascript 的执行速度要慢得多。

Why moving may be (and most probably is) slow?

Move functionality could be slow, because it has more things to do than simple variable assignment. It has to actually render some element to some other place. You could test this if you run this on IE9. I anticipate it should run faster since it uses hardware video acceleration.

As for the other routine I hope others will dissect it. :)

Questions for you

  1. Can you please describe how do balls move? Sporadically? How do you call UpdateBall() for each ball? Do you queue those calls?

  2. Provide VMul and VAdd functionality

  3. Have you played with styling? Maybe relative positioning of balls' immediate parent may speed up rendering. And setting overflow:hidden on it as well. I don't know. Depends on how you've done it. Hence a JSFiddle would be very helpful.

A suggestion

Instead of using setInterval to call your function you should maybe just queue them and let them execute as fast as it gets. And just for the sake of it, provide a central setInterval with some watcher that they don't run too fast.

But I guess that it still utilizes your processor to 100% which isn't good anyway.

Very important note: Don't run you app while Firebug's enabled because it's a well known fact that Javascript executes much slower when Firebug is running.

爺獨霸怡葒院 2024-11-21 13:02:53

如果 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"; 
    }
};

That's tough, if MoveTo() is in fact your bottleneck, since there is not a whole lot going on there. About the only things I can think of, right off hand, are

1) Cache the style property of the image and position for faster lookups. Everytime you see a dot in the object chain it's requires stepping through the scope chain. Ideally you can cache this property at the time the parent of MoveTo() is constructed.

2) Are the 'px' strings required? It may result in an invalid CSS specification, but it may still work. I have a hard time believing 2 string concats would really change all that much though.

The main problem here is likely the fact that anytime you change the DOM, the browsers re-flows the entire page. Your only other option may be to refactor such that instead of changing the styles, you actually remove the previous contents, and replace it with the a document fragment describing the new state. This would result in only 2 re-flows for the entire step (1 for removal, 1 for addition), instead of 2 for each ball.

EDIT: Regarding #1 above, when I say cache, I don't mean just locally in the function call. But perhaps as a closure in the parent object. For example:

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

    function MoveTo(){
        style.left = posX + "px";
        style.right = posY + "px"; 
    }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文