代码之家  ›  专栏  ›  技术社区  ›  dbr

如何使Processing.org草图更高效?

  •  1
  • dbr  · 技术社区  · 16 年前

    我有一个简单的素描(英文) Processing ),基本上是一堆圆点四处游荡,如果它们相互接触,它们就会战斗(每一个都有一个强度值,每次获胜都会增加,如果相等,则赢家是随机选择的)

    它适用于大约5000个12像素的“僵尸”(半秒钟内会有轻微的减速,而僵尸最初会相互碰撞),问题是当僵尸变小时,它们之间的碰撞不会很快,而且减速会持续更长时间。。

    lurching 学位(或否)。我认为速度慢的最大原因是碰撞检测——每个僵尸都会互相检查(所以僵尸1检查2-5000,僵尸2检查1,3-5000等等)

    我希望一切都保持简单,并且“简单处理”(不使用外部库,这可能更高效、更简单,但我觉得它对学习没有多大用处)

    int numZombies = 5000;
    
    Zombie[] zombies = new Zombie[numZombies];
    
    void setup(){
      size(512, 512);
      noStroke();
      for(int i = 0; i < numZombies; i++){
        zombies[i] = new Zombie(i, random(width), random(height), random(360), zombies);
      }
    }
    
    void draw(){
      background(0);
    
      for(int i = 0; i < numZombies; i++){
        zombies[i].move();
        zombies[i].display();
      }
    }
    
    class Zombie{
      int id; // the index of this zombie
    
      float x, y; // current location
      float angle; // angle of zombies movement
      float lurching = 10; // Amount angle can change
      float strength = 2;
    
      boolean dead = false; // true means zombie is dead
    
      float diameter = 12; // How big the zombie is
      float velocity = 1.0; // How fast zombie moves
    
      Zombie[] others; // Stores the other zombies
    
      Zombie(int inid, float xin, float yin, float inangle, Zombie[] oin){
        id = inid;
        x = xin;
        y = yin;
        angle = inangle;
        others = oin;
      }
    
      void move(){
        if(dead) return;
    
        float vx = velocity * sin(radians(180-angle));
        float vy = velocity * cos(radians(180-angle));
    
        x = x + vx;
        y = y + vy;
    
        if(x + vx < 0 || x + vx > width || y + vy < 0 || y + vy > height){
          // Collided with wall
          angle = angle + 180;
        }
    
        float adecide = random(3);
    
        if(adecide < 1){
          // Move left
          angle=angle - lurching;
        }
        else if(adecide > 1 && adecide < 2){
          // Don't move x
        }
        else if(adecide > 2){
          // Move right
          angle = angle + lurching;
        }
    
        checkFights();
      }
    
      void checkFights(){
        for (int i=0; i < numZombies; i++) {
          if (i == id || dead || others[i].dead){
            continue;
          }
    
          float dx = others[i].x - x;
          float dy = others[i].y - y;
          float distance = sqrt(dx*dx + dy*dy);
    
          if (distance < diameter){
            fight(i);
          }
        }
      }
    
      void fight(int oid){
        Zombie o = others[oid];
    
        //println("Zombie " + id + "(s: "+ strength +") fighting " + oid + "(s: "+ o.strength +")");
    
        if(strength < o.strength){
          kill();
          o.strength++;
        } 
        else if (strength == o.strength){
          if(random(1) > 0.5){
            kill();
            o.strength++;
          }
          else{
            o.kill();
            strength++;
          }
        }
      }
    
      void kill(){
        dead = true;
      }
    
      void display(){
        if(dead) return;
        ellipse(x, y, diameter, diameter);
      }
    }
    
    5 回复  |  直到 6 年前
        1
  •  4
  •   Yuval Adam    16 年前

    你找到你自己了 O(n^2) 复杂性,这会扼杀你的算法。正确的做法是,每个移动的僵尸都必须与所有其他僵尸一起检查它们是否发生碰撞,这就导致了二次复杂性。

        2
  •  1
  •   lc.    16 年前

    正如1800条信息所说,你需要减少比较的次数。

    把比赛场地分成几个区域是个好主意。我可以想象,将当前位置与区域边界进行比较以及从适当的集合中添加/删除僵尸所需的时间是值得的。假设他们通常会走直线,他们不应该太频繁地改变区域。

    我们有一个问题,区域之间可能发生碰撞。根据这个想法,你可以把屏幕分成4个区域,然后再分成9个区域。想象一下,一块贴在十字架上的tic-tac趾板。这是一张糟糕的图纸,但是:

        |  ! |
        |  ! |
    ----+--!-+----
        |  ! |
    ====|==x=|====
    ----+--!-+----
        |  ! |
        |  ! |
    

    这样,每个僵尸一次在两个区域中,一个方案中的每个边界都被另一个区域覆盖。你甚至不必再次检查所有的僵尸,因为要么我们死了,要么他们死了。因此,唯一的双重处理是单一处理 others[i].dead 检查


    我能很快看到的另一件事是,即使你已经死了,你仍然可以循环使用其余的元素:

      if (i == id || dead || others[i].dead){
        continue;
      }
    

    它可能不会节省大量处理,但如果您:

      if (dead) return;
    

    相反


        3
  •  1
  •   chuck chuck    16 年前

    您的基本碰撞检测算法 O(n^2) 复杂性

    按拓扑(按距离)对实体进行排序。你想把这些分开 僵尸不仅仅是按地理位置分类的,而是要对它们进行分类,以便只有在 他们彼此“接近”。您希望忽略空区域。

    考虑一个树结构到你的地区。当一个区域有多个 N 对于僵尸,可以将区域分割得更小,直到区域半径接近碰撞距离。使用地图查找区域,并检查给定区域(以及任何“足够近”区域)中的所有僵尸。

    你可能想要 待到<=日志(n)。。。

        4
  •  0
  •   1800 INFORMATION    16 年前

    也许你应该把游戏场地分成几个区域,只检查同一区域内的僵尸之间的碰撞。您需要减少比较的次数。

        5
  •  0
  •   PhiLho    16 年前

    它让我想起了这条线索: No idea what could be the problem!! collision detection help 我指的是维基百科的碰撞检测文章。
    Quadtrees 似乎经常用于2D分区。