简体   繁体   English

使用jQuery提高循环效率

[英]Increase efficiency of a loop with jQuery

I have a game coded in jQuery where bots are moved around the screen. 我有一个用jQuery编码的游戏,其中机器人在屏幕上移动。 The below code is a loop that runs every 20ms, currently if you have over 15 bots you start to notice the browser lagging (simply because of all the advanced collision detection going on). 下面的代码是一个每20毫秒运行一次的循环,当前,如果您有超过15个漫游器,您会开始注意到浏览器滞后(这是因为所有高级碰撞检测都在进行)。

Is there any way to reduce the lag, can I make it any more efficient? 有什么办法可以减少延迟,我可以提高延迟效率吗?

Ps sorrry for just posting a block of code, I can't see a way to make my point clear enough without! ps抱歉,仅发布了一段代码,如果没有它,我看不到一种方法可以使我的观点足够清楚!

$.playground().registerCallback(function(){ //Movement Loop
        if(!pause) {
            for (var i in bots) {
                //bots - color, dir, x, y, z, spawned?, spawnerid, prevd
                var self = $('#b' + i);
                var current = bots[i];
                if(bots[i][5]==1) {
                    var xspeed = 0, yspeed = 0;
                    if(current[1]==0) { yspeed = -D_SPEED; }
                    else if(current[1]==1) { xspeed = D_SPEED; }
                    else if(current[1]==2) { yspeed = D_SPEED; }
                    else if(current[1]==3) { xspeed = -D_SPEED; }

                    var x = current[2] + xspeed;
                    var y = current[3] + yspeed;
                    var z = current[3] + 120;
                    if(current[2]>0&&x>PLAYGROUND_WIDTH||current[2]<0&&x<-GRID_SIZE||
                        current[3]>0&&y>PLAYGROUND_HEIGHT||current[3]<0&&y<-GRID_SIZE) {
                            remove_bot(i, self);
                    } else {
                        if(current[7]!=current[1]) {
                            self.setAnimation(colors[current[0]][current[1]]);
                            bots[i][7] = current[1];
                        }
                        if(self.css({"left": ""+(x)+"px", "top": ""+(y)+"px", "z-index": z})) {
                            bots[i][2] = x;
                            bots[i][3] = y;
                            bots[i][4] = z;
                            bots[i][8]++;
                        }
                    }
                }
            }
            $("#debug").html(dump(arrows));
            $(".bot").each(function(){
                var b_id = $(this).attr("id").substr(1);
                var collision = false;
                var c_bot = bots[b_id];
                var b_x = c_bot[2];
                var b_y = c_bot[3];
                var b_d = c_bot[1];
                $(this).collision(".arrow,#arrows").each(function(){ //Many thanks to Selim Arsever for this fix!
                    var a_id = $(this).attr("id").substr(1);
                    var piece = arrows[a_id];
                    var a_v = piece[0];
                    if(a_v==1) {
                    var a_x = piece[2];
                    var a_y = piece[3];     
                        var d_x = b_x-a_x;
                        var d_y = b_y-a_y;
                        if(d_x>=4&&d_x<=5&&d_y>=1&&d_y<=2)  {
                            //bots - color, dir, x, y, z, spawned?, spawnerid, prevd
                            bots[b_id][7] = c_bot[1];
                            bots[b_id][1] = piece[1];   
                            collision = true;
                        }
                    }
                });
                if(!collision) {
                    $(this).collision(".wall,#level").each(function(){
                        var w_id = $(this).attr("id").substr(1);
                        var piece = pieces[w_id];
                        var w_x = piece[1];
                        var w_y = piece[2];
                        d_x = b_x-w_x;
                        d_y = b_y-w_y;
                        if(b_d==0&&d_x>=4&&d_x<=5&&d_y>=27&&d_y<=28) { kill_bot(b_id); collision = true; } //4 // 33
                        if(b_d==1&&d_x>=-12&&d_x<=-11&&d_y>=21&&d_y<=22) { kill_bot(b_id); collision = true; } //-14 // 21
                        if(b_d==2&&d_x>=4&&d_x<=5&&d_y>=-9&&d_y<=-8) { kill_bot(b_id); collision = true; } //4 // -9
                        if(b_d==3&&d_x>=22&&d_x<=23&&d_y>=20&&d_y<=21) { kill_bot(b_id); collision = true; } //22 // 21
                    });
                }
                if(!collision&&c_bot[8]>GRID_MOVE) {
                    $(this).collision(".spawn,#level").each(function(){
                        var s_id = $(this).attr("id").substr(1);
                        var piece = pieces[s_id];
                        var s_x = piece[1];
                        var s_y = piece[2];
                        d_x = b_x-s_x;
                        d_y = b_y-s_y;  
                        if(b_d==0&&d_x>=4&&d_x<=5&&d_y>=19&&d_y<=20) { kill_bot(b_id); collision = true; } //4 // 33
                        if(b_d==1&&d_x>=-14&&d_x<=-13&&d_y>=11&&d_y<=12) { kill_bot(b_id); collision = true; } //-14 // 21
                        if(b_d==2&&d_x>=4&&d_x<=5&&d_y>=-11&&d_y<=-10) { kill_bot(b_id); collision = true; } //4 // -9
                        if(b_d==3&&d_x>=22&&d_x<=23&&d_y>=11&&d_y<=12) { kill_bot(b_id); collision = true; } //22 // 21*/
                    });
                }
                if(!collision) {
                    $(this).collision(".exit,#level").each(function(){
                        var e_id = $(this).attr("id").substr(1);
                        var piece = pieces[e_id];
                        var e_x = piece[1];
                        var e_y = piece[2];
                        d_x = b_x-e_x;
                        d_y = b_y-e_y;
                        if(d_x>=4&&d_x<=5&&d_y>=1&&d_y<=2)  {
                            current_bots++;
                            bots[b_id] = false;
                            $("#current_bots").html(current_bots);
                            $("#b" + b_id).setAnimation(exit[2], function(node){$(node).fadeOut(200)});
                        }
                    });
                }
                if(!collision) {
                    $(this).collision(".bot,#level").each(function(){
                        var bd_id = $(this).attr("id").substr(1);
                        if(bd_id!=b_id) {                           
                            var piece = bots[bd_id];                                
                            var bd_x = piece[2];
                            var bd_y = piece[3];
                            d_x = b_x-bd_x;
                            d_y = b_y-bd_y;
                            if(d_x>=0&&d_x<=2&&d_y>=0&&d_y<=2) { kill_bot(b_id); kill_bot(bd_id); collision = true; }
                        }
                    });
                }
            });

        }
    }, REFRESH_RATE);

Many thanks, 非常感谢,

There are many ways to improve your code. 有很多方法可以改善您的代码。

Algorithmic improvements 算法上的改进

You're using almost exclusively O(n 2 ) algorithms for your collision detection. 您几乎只使用O(n 2算法进行碰撞检测。 You're testing each bot against each object. 您正在针对每个对象测试每个机器人。 For example if you've got 20 bots and 30 obstacles your computer has to test for (19+18+...+1) + (20*30) = 190+600 = 790 collisions. 例如,如果您有20个机器人和30个障碍,则计算机必须测试(19+18+...+1) + (20*30) = 190+600 = 790碰撞。

A simple way of reducing the amount of necessary collision tests is to split the field into smaller parts. 减少必要的碰撞测试数量的一种简单方法是将磁场分成较小的部分。 One simple way of doing this is to use sorted lists. 一种简单的方法是使用排序列表。 I assume your objects have rectangular boundaries. 我假设您的对象具有矩形边界。 Just create a sorted list of the left edge. 只需创建左边缘的排序列表即可。

sorted_left_border = [40, 51, 234, 240];

Here's the used pseudo code: 这是使用的伪代码:

for bot in all_bots:
    left = bot.x, right = bot.x + bot.width
    top = bot.y, bottom = bot.y + bot.height
    // binary search is O(log n)
    colliding_objects = binary_search for range in sorted_left_border
    for obj in colliding_objects:
        // check the other 3 borders

There are ways to use all 4 borders, but they are considerably more complicated. 有使用所有4个边界的方法,但是它们要复杂得多。

There are some sorting algorithms that can sort in near to O(n) time if the input is almost sorted, but I'd just use the Javascript built-in sort function. 如果输入几乎被排序,则有一些排序算法可以在O(n)时间附近进行排序,但是我只使用Javascript内置的排序功能。 (If that's too slow you can write your own later.) (如果太慢,您可以稍后编写自己的。)

The DOM is slow DOM很慢

Try to update the DOM offline before you insert it back into the browser HTML. 尝试脱机更新DOM,然后再将其重新插入浏览器HTML。 The way you're doing it now, the browser has to update the DOM every time you change something. 现在的操作方式是,每次更改内容时,浏览器都必须更新DOM。 The browser should have to update the DOM not more than 1 time per frame. 浏览器每帧更新DOM的时间不得超过1次。

To pull the elements from the DOM, there's the detach method in jQuery. 为了从DOM中提取元素,jQuery中提供了detach方法。 Be sure to use jQuery selectors that match only the detached elements: 确保使用仅与分离元素匹配的jQuery选择器:

// remove from DOM, avoid updating it.
var theGame = $("#theGame").detach();

// select all bots and do the updating
var bots = $(".bot", theGame);

// reinsert
theGame.appendTo("body");

Using appropriate technologies 使用适当的技术

Using jQuery with DOM elements is the wrong technique. 将jQuery与DOM元素一起使用是错误的技术。 The DOM wasn't built for this. DOM不是为此而构建的。 If you're only programming for modern browsers, consider using the <canvas> element . 如果您仅针对现代浏览器进行编程,请考虑使用<canvas>元素

I think what Georg is describing is something like this . 我认为格奥尔格(Georg)所描述的就是这样 Its a java applet so its going to be faster than javascript, but it illustrates the principle. 它是一个Java applet,因此它比javascript更快,但它说明了原理。 You'll note that distances are only calculated between an object and the objects in the adjacent 8 squares in the grid. 您会注意到,仅在对象与网格中相邻的8个正方形中的对象之间计算距离。 If there are no other objects in those 8 squares then that bubble doesn't get checked for collisions at all. 如果在这8个正方形中没有其他对象,则根本不会检查该气泡是否存在碰撞。

This technique is built on a hash table, which shouldn't be too hard to implement in javascript because every js object is a hash table! 该技术建立在哈希表的基础上,在哈希表中实现起来应该不难,因为每个js对象都是哈希表!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM