Processing:Примеры/Стая
Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
Содержание | Среда разработки Processing | Справочник языка Processing | Библиотеки | Примеры | Режимы программирования |
Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.
Описание[1]
Реализация в Processing программы Крейга Рейнольдса «Boids», симулирующей стайное поведение птиц. Поведение каждой «птицы» регулируется 3 правилами – уклонения, выравнивания и целостности.
Кликните мышкой по экрану, чтобы добавить новую «птицу».
Пример
Flock flock;
void setup() {
size(640, 360);
flock = new Flock();
// добавляем в систему начальную стаю «птиц»:
for (int i = 0; i < 150; i++) {
flock.addBoid(new Boid(width/2,height/2));
}
}
void draw() {
background(50);
flock.run();
}
// добавляем в систему новую «птицу»:
void mousePressed() {
flock.addBoid(new Boid(mouseX,mouseY));
}
// объект «Flock» (список объектов «Boid»)
class Flock {
ArrayList<Boid> boids; // объект ArrayList для хранения всех «птиц»
Flock() {
boids = new ArrayList<Boid>(); // инициализируем «boids»
}
void run() {
for (Boid b : boids) {
b.run(boids); // прогоняем все объекты «Boid»
// в списке «boids» друг через друга
}
}
void addBoid(Boid b) {
boids.add(b);
}
}
// класс «Boid»
class Boid {
PVector position;
PVector velocity;
PVector acceleration;
float r;
float maxforce; // максимальная сила поворота
float maxspeed; // максимальная скорость
Boid(float x, float y) {
acceleration = new PVector(0, 0);
// это новый метод для объекта «PVector»,
// еще не добавленный в JavaScript:
// velocity = PVector.random2D();
// временно воспользуемся этим кодом,
// чтобы этот скетч можно было запустить в JavaScript:
float angle = random(TWO_PI);
velocity = new PVector(cos(angle), sin(angle));
position = new PVector(x, y);
r = 2.0;
maxspeed = 2;
maxforce = 0.03;
}
void run(ArrayList<Boid> boids) {
flock(boids);
update();
borders();
render();
}
void applyForce(PVector force) {
// здесь, если необходимо, можно добавить массу (A = F / M):
acceleration.add(force);
}
// рассчитываем ускорение на основе 3 правил:
void flock(ArrayList<Boid> boids) {
PVector sep = separate(boids); // уклонение
PVector ali = align(boids); // выравнивание
PVector coh = cohesion(boids); // целостность
// задаем всем этим силам некоторые значения:
sep.mult(1.5);
ali.mult(1.0);
coh.mult(1.0);
// применяем к ускорению все три вектора силы:
applyForce(sep);
applyForce(ali);
applyForce(coh);
}
// функция для обновления данных о позиции:
void update() {
// обновляем скорость:
velocity.add(acceleration);
// ограничиваем скорость:
velocity.limit(maxspeed);
position.add(velocity);
// на каждом цикле сбрасываем скорость до «0»:
acceleration.mult(0);
}
// функция для расчета и применения силы поворота в сторону цели
// (сила поворота = вектор «desired» - вектор «velocity»):
PVector seek(PVector target) {
// вектор, указывающий от позиции к цели:
PVector desired = PVector.sub(target, position);
// масштабируем «desired» к максимальной скорости:
desired.normalize();
desired.mult(maxspeed);
// две строчки выше можно сократить с помощью
// нового метода setMag() для объекта PVector,
// но мы не будем его использовать,
// пока его не начнет поддерживать Processing.js:
// desired.setMag(maxspeed);
// сила поворота = вектор «desired» - вектор «velocity»:
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxforce); // ограничиваем до
// максимальной силы поворота:
return steer;
}
void render() {
// рисуем треугольник, поворачивающийся
// в направлении вектора «velocity»:
float theta = velocity.heading2D() + radians(90);
// метод heading2D() выше – это теперь heading(),
// но я решил оставить старый синтаксис,
// пока Processing.js не начнет поддерживать новый
fill(200, 100);
stroke(255);
pushMatrix();
translate(position.x, position.y);
rotate(theta);
beginShape(TRIANGLES);
vertex(0, -r*2);
vertex(-r, r*2);
vertex(r, r*2);
endShape();
popMatrix();
}
// делаем так, чтобы «птицы», вылетая за пределы экрана,
// появлялись в противоположной части экрана:
void borders() {
if (position.x < -r) position.x = width+r;
if (position.y < -r) position.y = height+r;
if (position.x > width+r) position.x = -r;
if (position.y > height+r) position.y = -r;
}
// Правило уклонения.
// Эта функция проверяет, есть ли рядом другие «птицы»,
// и если есть, то заставляет «птицу» уклониться от соседа:
PVector separate (ArrayList<Boid> boids) {
float desiredseparation = 25.0f;
PVector steer = new PVector(0, 0, 0);
int count = 0;
// проверяем каждую «птицу» в системе на предмет того,
// находится ли она близко к другой «птице»:
for (Boid other : boids) {
float d = PVector.dist(position, other.position);
// если дистанция больше «0» (это сама «птица»)
// и меньше значения в «desiredseparation»...
if ((d > 0) && (d < desiredseparation)) {
// рассчитываем вектор для уклонения от соседа:
PVector diff = PVector.sub(position, other.position);
diff.normalize();
diff.div(d); // подгоняем к дистанции между «птицами»
steer.add(diff);
count++; // отслеживаем, сколько раз
// выполнялось это условие
}
}
// Среднее значение – делим на то,
// сколько раз выполнялось это условие:
if (count > 0) {
steer.div((float)count);
}
// если длина вектора больше «0»...
if (steer.mag() > 0) {
// первые две строчки кода ниже
// можно заменить на новый метод setMag() для PVector;
// здесь мы его использовать не будем,
// пока его не начнет поддерживать Processing.js:
// steer.setMag(maxspeed);
// используем правило Рейнольдса:
// (сила поворота = вектор «desired» - вектор «velocity»):
steer.normalize();
steer.mult(maxspeed);
steer.sub(velocity);
steer.limit(maxforce);
}
return steer;
}
// Правило выравнивания.
// Рассчитываем среднюю скорость
// для каждой соседней «птицы» в системе:
PVector align (ArrayList<Boid> boids) {
float neighbordist = 50;
PVector sum = new PVector(0, 0);
int count = 0;
for (Boid other : boids) {
float d = PVector.dist(position, other.position);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.velocity);
count++;
}
}
if (count > 0) {
sum.div((float)count);
// первые две строчки кода ниже
// можно заменить на новый метод setMag() для PVector;
// здесь мы его использовать не будем,
// пока его не начнет поддерживать Processing.js:
// sum.setMag(maxspeed);
// используем правило Рейнольдса:
// (сила поворота = вектор «desired» - вектор «velocity»):
sum.normalize();
sum.mult(maxspeed);
PVector steer = PVector.sub(sum, velocity);
steer.limit(maxforce);
return steer;
}
else {
return new PVector(0, 0);
}
}
// Правило целостности.
// Рассчитываем вектор поворота
// в сторону средней (то есть центральной) позиции
// всех соседних «птиц»:
PVector cohesion (ArrayList<Boid> boids) {
float neighbordist = 50;
PVector sum = new PVector(0, 0); // начинаем с пустого вектора
// для сложения всех позиций
int count = 0;
for (Boid other : boids) {
float d = PVector.dist(position, other.position);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.position); // добавляем позицию
count++;
}
}
if (count > 0) {
sum.div(count);
return seek(sum); // поворачиваем в сторону позиции
}
else {
return new PVector(0, 0);
}
}
}
См.также
Внешние ссылки
Примеры на Processing | |
---|---|
Основы |
|
Продвинутые графические эффекты |
|
Примеры из сторонних библиотек |