Cat hungry.png
Здравствуйте! Собираем деньги на перевод материалов по электронике(https://www.allaboutcircuits.com/education/). Реквизиты указаны здесь.

Processing:Примеры/Стая

Материал из Онлайн справочника
Перейти к: навигация, поиск


Перевод: Максим Кузьмин (Cubewriter)
Перевел 2686 статей для сайта.

Контакты:

Проверка/Оформление/Редактирование: Мякишев Е.А.


Ambox content.png Черновик


Описание[1]

Реализация в Processing программы Крейга Рейнольдса «Boids», симулирующей стайное поведение птиц. Поведение каждой «птицы» регулируется 3 правилами – уклонения, выравнивания и целостности.

Кликните мышкой по экрану, чтобы добавить новую «птицу».

Пример

  1. Flock flock;
  2.  
  3. void setup() {
  4.   size(640, 360);
  5.   flock = new Flock();
  6.   // добавляем в систему начальную стаю «птиц»:
  7.   for (int i = 0; i < 150; i++) {
  8.     flock.addBoid(new Boid(width/2,height/2));
  9.   }
  10. }
  11.  
  12. void draw() {
  13.   background(50);
  14.   flock.run();
  15. }
  16.  
  17. // добавляем в систему новую «птицу»:
  18. void mousePressed() {
  19.   flock.addBoid(new Boid(mouseX,mouseY));
  20. }
  21.  
  22.  
  23.  
  24. // объект «Flock» (список объектов «Boid»)
  25.  
  26. class Flock {
  27.   ArrayList<Boid> boids; // объект ArrayList для хранения всех «птиц»
  28.  
  29.   Flock() {
  30.     boids = new ArrayList<Boid>(); // инициализируем «boids»
  31.   }
  32.  
  33.   void run() {
  34.     for (Boid b : boids) {
  35.       b.run(boids);  // прогоняем все объекты «Boid»
  36.                      // в списке «boids» друг через друга
  37.     }
  38.   }
  39.  
  40.   void addBoid(Boid b) {
  41.     boids.add(b);
  42.   }
  43.  
  44. }
  45.  
  46.  
  47.  
  48.  
  49. // класс «Boid»
  50.  
  51. class Boid {
  52.  
  53.   PVector position;
  54.   PVector velocity;
  55.   PVector acceleration;
  56.   float r;
  57.   float maxforce;    // максимальная сила поворота
  58.   float maxspeed;    // максимальная скорость
  59.  
  60.     Boid(float x, float y) {
  61.     acceleration = new PVector(0, 0);
  62.  
  63.     // это новый метод для объекта «PVector»,
  64.     // еще не добавленный в JavaScript:
  65.     // velocity = PVector.random2D();
  66.  
  67.     // временно воспользуемся этим кодом,
  68.     // чтобы этот скетч можно было запустить в JavaScript:
  69.     float angle = random(TWO_PI);
  70.     velocity = new PVector(cos(angle), sin(angle));
  71.  
  72.     position = new PVector(x, y);
  73.     r = 2.0;
  74.     maxspeed = 2;
  75.     maxforce = 0.03;
  76.   }
  77.  
  78.   void run(ArrayList<Boid> boids) {
  79.     flock(boids);
  80.     update();
  81.     borders();
  82.     render();
  83.   }
  84.  
  85.   void applyForce(PVector force) {
  86.     // здесь, если необходимо, можно добавить массу (A = F / M):
  87.     acceleration.add(force);
  88.   }
  89.  
  90.   // рассчитываем ускорение на основе 3 правил:
  91.   void flock(ArrayList<Boid> boids) {
  92.     PVector sep = separate(boids);   // уклонение
  93.     PVector ali = align(boids);      // выравнивание
  94.     PVector coh = cohesion(boids);   // целостность
  95.     // задаем всем этим силам некоторые значения:
  96.     sep.mult(1.5);
  97.     ali.mult(1.0);
  98.     coh.mult(1.0);
  99.     // применяем к ускорению все три вектора силы:
  100.     applyForce(sep);
  101.     applyForce(ali);
  102.     applyForce(coh);
  103.   }
  104.  
  105.   // функция для обновления данных о позиции:
  106.   void update() {
  107.     // обновляем скорость:
  108.     velocity.add(acceleration);
  109.     // ограничиваем скорость:
  110.     velocity.limit(maxspeed);
  111.     position.add(velocity);
  112.     // на каждом цикле сбрасываем скорость до «0»:
  113.     acceleration.mult(0);
  114.   }
  115.  
  116.   // функция для расчета и применения силы поворота в сторону цели
  117.   // (сила поворота = вектор «desired» - вектор «velocity»):
  118.   PVector seek(PVector target) {
  119.     // вектор, указывающий от позиции к цели:
  120.     PVector desired = PVector.sub(target, position);
  121.     // масштабируем «desired» к максимальной скорости:
  122.     desired.normalize();
  123.     desired.mult(maxspeed);
  124.  
  125.     // две строчки выше можно сократить с помощью
  126.     // нового метода setMag() для объекта PVector,
  127.     // но мы не будем его использовать,
  128.     // пока его не начнет поддерживать Processing.js:
  129.     // desired.setMag(maxspeed);
  130.  
  131.     // сила поворота = вектор «desired» - вектор «velocity»:
  132.     PVector steer = PVector.sub(desired, velocity);
  133.     steer.limit(maxforce);  // ограничиваем до
  134.                             // максимальной силы поворота:
  135.     return steer;
  136.   }
  137.  
  138.   void render() {
  139.     // рисуем треугольник, поворачивающийся
  140.     // в направлении вектора «velocity»:
  141.     float theta = velocity.heading2D() + radians(90);
  142.     // метод heading2D() выше – это теперь heading(),
  143.     // но я решил оставить старый синтаксис,
  144.     // пока Processing.js не начнет поддерживать новый
  145.    
  146.     fill(200, 100);
  147.     stroke(255);
  148.     pushMatrix();
  149.     translate(position.x, position.y);
  150.     rotate(theta);
  151.     beginShape(TRIANGLES);
  152.     vertex(0, -r*2);
  153.     vertex(-r, r*2);
  154.     vertex(r, r*2);
  155.     endShape();
  156.     popMatrix();
  157.   }
  158.  
  159.   // делаем так, чтобы «птицы», вылетая за пределы экрана,
  160.   // появлялись в противоположной части экрана:
  161.   void borders() {
  162.     if (position.x < -r) position.x = width+r;
  163.     if (position.y < -r) position.y = height+r;
  164.     if (position.x > width+r) position.x = -r;
  165.     if (position.y > height+r) position.y = -r;
  166.   }
  167.  
  168.   // Правило уклонения.
  169.   // Эта функция проверяет, есть ли рядом другие «птицы»,
  170.   // и если есть, то заставляет «птицу» уклониться от соседа:
  171.   PVector separate (ArrayList<Boid> boids) {
  172.     float desiredseparation = 25.0f;
  173.     PVector steer = new PVector(0, 0, 0);
  174.     int count = 0;
  175.     // проверяем каждую «птицу» в системе на предмет того,
  176.     // находится ли она близко к другой «птице»:
  177.     for (Boid other : boids) {
  178.       float d = PVector.dist(position, other.position);
  179.       // если дистанция больше «0» (это сама «птица»)
  180.       // и меньше значения в «desiredseparation»...
  181.       if ((d > 0) && (d < desiredseparation)) {
  182.         // рассчитываем вектор для уклонения от соседа:
  183.         PVector diff = PVector.sub(position, other.position);
  184.         diff.normalize();
  185.         diff.div(d);        // подгоняем к дистанции между «птицами»
  186.         steer.add(diff);
  187.         count++;            // отслеживаем, сколько раз
  188.                             // выполнялось это условие
  189.       }
  190.     }
  191.     // Среднее значение – делим на то,
  192.     // сколько раз выполнялось это условие:
  193.     if (count > 0) {
  194.       steer.div((float)count);
  195.     }
  196.  
  197.     // если длина вектора больше «0»...
  198.     if (steer.mag() > 0) {
  199.       // первые две строчки кода ниже
  200.       // можно заменить на новый метод setMag() для PVector;
  201.       // здесь мы его использовать не будем,
  202.       // пока его не начнет поддерживать Processing.js:
  203.       // steer.setMag(maxspeed);
  204.  
  205.       // используем правило Рейнольдса:
  206.       // (сила поворота = вектор «desired» - вектор «velocity»):
  207.       steer.normalize();
  208.       steer.mult(maxspeed);
  209.       steer.sub(velocity);
  210.       steer.limit(maxforce);
  211.     }
  212.     return steer;
  213.   }
  214.  
  215.   // Правило выравнивания.
  216.   // Рассчитываем среднюю скорость
  217.   // для каждой соседней «птицы» в системе:
  218.   PVector align (ArrayList<Boid> boids) {
  219.     float neighbordist = 50;
  220.     PVector sum = new PVector(0, 0);
  221.     int count = 0;
  222.     for (Boid other : boids) {
  223.       float d = PVector.dist(position, other.position);
  224.       if ((d > 0) && (d < neighbordist)) {
  225.         sum.add(other.velocity);
  226.         count++;
  227.       }
  228.     }
  229.     if (count > 0) {
  230.       sum.div((float)count);
  231.       // первые две строчки кода ниже
  232.       // можно заменить на новый метод setMag() для PVector;
  233.       // здесь мы его использовать не будем,
  234.       // пока его не начнет поддерживать Processing.js:
  235.       // sum.setMag(maxspeed);
  236.  
  237.       // используем правило Рейнольдса:
  238.       // (сила поворота = вектор «desired» - вектор «velocity»):
  239.       sum.normalize();
  240.       sum.mult(maxspeed);
  241.       PVector steer = PVector.sub(sum, velocity);
  242.       steer.limit(maxforce);
  243.       return steer;
  244.     }
  245.     else {
  246.       return new PVector(0, 0);
  247.     }
  248.   }
  249.  
  250.   // Правило целостности.
  251.   // Рассчитываем вектор поворота
  252.   // в сторону средней (то есть центральной) позиции
  253.   // всех соседних «птиц»:
  254.   PVector cohesion (ArrayList<Boid> boids) {
  255.     float neighbordist = 50;
  256.     PVector sum = new PVector(0, 0);   // начинаем с пустого вектора
  257.                                        // для сложения всех позиций
  258.     int count = 0;
  259.     for (Boid other : boids) {
  260.       float d = PVector.dist(position, other.position);
  261.       if ((d > 0) && (d < neighbordist)) {
  262.         sum.add(other.position); // добавляем позицию
  263.         count++;
  264.       }
  265.     }
  266.     if (count > 0) {
  267.       sum.div(count);
  268.       return seek(sum);  // поворачиваем в сторону позиции
  269.     }
  270.     else {
  271.       return new PVector(0, 0);
  272.     }
  273.   }
  274. }

См.также

Внешние ссылки

  1. processing.org - Flocking by Daniel Shiffman.