Как я могу сделать этот эскиз Processing.org более эффективным? - PullRequest
1 голос
/ 07 января 2009

У меня есть простой набросок (в Обработка ), в основном бродит куча точек, если они вступают в контакт друг с другом, с которым сражаются (у каждой есть значение силы, которое увеличивается при каждом выигрыше, если он равен, случайным образом выбирается победитель)

Хорошо работает примерно с 5000 12-пиксельными "зомби" (небольшое замедление на полсекунды, в то время как зомби изначально сталкиваются друг с другом), проблема в том, что когда зомби становятся меньше, они не сталкиваются друг с другом так же быстро, и замедление может длиться гораздо дольше ..

Код действительно прост - в основном, каждый зомби - это класс, имеющий координату X / Y. В каждом кадре все зомби подталкиваются на один пиксель, случайным образом поворачиваясь на 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 ]

4 голосов
/ 07 января 2009

Вы получили себе O(n^2) сложность, и это убивает ваш алгоритм. Правильно, что каждый движущийся зомби должен проверить со всеми остальными, столкнулись ли они, что приводит вас к квадратичной сложности.

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

1 голос
/ 16 января 2009

Ваш основной алгоритм обнаружения столкновений имеет O (n ^ 2) сложность.

Вам нужен какой-то подход, который уменьшит количество сравнений.

Один из подходов, уже упомянутых, заключается в разделении игрового поля на зоны / регионы, и проверять столкновение только в том случае, если зомби находится в той же зоне / регионе. Это попытка сортировать объекты топологически (по расстоянию). То, что вы хотите, это отделить эти зомби не просто по географии, но и сортировать их так, чтобы они сравнивались только тогда, когда они «близки» друг к другу. И вы хотите игнорировать пустые регионы.

Рассмотрите древовидную структуру в ваших регионах. Когда в регионе больше чем несколько N зомби, вы можете разделить регион на меньшее, пока радиус региона не достигнет вашего расстояния столкновения. Используйте карту для поиска региона и проверьте всех зомби в данном регионе (и любой «достаточно близкий» регион).

Вы, вероятно, хотите, чтобы N было <= log (n) ... </p>

1 голос
/ 07 января 2009

Как гласит 1800 ИНФОРМАЦИЯ, вам нужно как-то сократить количество сравнений.

Хорошая идея - разделить игровую зону на зоны. Я думаю, что время, которое требуется для сравнения текущего местоположения с границами зоны и добавления / удаления зомби из соответствующих коллекций, того стоит. Предполагая, что они обычно идут по прямой линии, они не должны менять зоны слишком часто.

У нас есть проблема возможных столкновений между зонами. Чтобы воспользоваться этой идеей, вы можете разделить экран на 4 зоны, а затем снова на 9 зон. Подумайте о крестиках, наложенных на крест. Это плохой рисунок, но:

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

Таким образом, каждый зомби находится в двух зонах одновременно, и каждая граница в одной схеме покрыта другой зоной. Тебе даже не придется снова проверять всех тех же зомби, потому что либо мы умрем, либо они будут. Таким образом, единственная двойная обработка - это одна others[i].dead проверка.


Еще одна вещь, которую я могу быстро увидеть, это то, что вы все еще просматриваете остальные элементы, даже если вы мертвы:

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

Это может не сэкономить много времени на обработку, но, безусловно, может сократить некоторые инструкции, если вы:

  if (dead) return;

вместо.


Также в качестве примечания вы хотите проверить диаметр или радиус по отношению к расстоянию?

0 голосов
/ 07 января 2009

Это напоминает мне эту тему: Не знаю, в чем может быть проблема !! . И справка по обнаружению столкновений , где я указываю на статью по обнаружению столкновений в Википедии.
Quadtrees , кажется, часто используются для 2D-разбиения.

0 голосов
/ 07 января 2009

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

...