ИДЗ / Движение стай (Boids) / 2502_БулгаковАА
.pdfСписок литературы
1. Стаи и толпы в играх: как программируют реалистичное стайное поведения // Skillbox Media. URL: https://skillbox.ru/media/gamedev/stai-i-tolpy-v- igrakh-kak-programmiruyut-realistichnoe-staynoe-povedenie/
(дата обращения: 07.12.2025).
2.Reynolds C.W. Flocks, herds, and schools: a distributed behavioral model / C.W. Reynolds // Computer Graphics, 1987. Вып. 21 (4, Июль). C. 25-34.
3.Boids – простой алгоритм перемещения групп юнитов // Habr. URL: https://habr.com/ru/articles/212721/ (дата обращения: 07.12.2025).
4.Boid'ы, птички и Unity3D // Habr. URL: https://habr.com/ru/articles/182382/
(дата обращения: 07.12.2025).
5. Bird–oid |
objects, |
поведение |
стаи |
// |
igonin.ru. |
URL: |
https://igonin.ru/help/algorithms/bird–oid–objects/ (дата обращения: 07.12.2025).
21
Приложение Листинг программы
%% Инициализация параметров clear; clc; close all;
% Параметры алгоритма
numBoids = 20; % Количество боидов cohesionWeight = 1.0; % Вес сцепления
cohesionDist = 2.0; % Расстояние сцепления separationWeight = 1.5; % Вес разделения separationDist = 1.0; % Расстояние разделения alignmentWeight = 1.0; % Вес выравнивания alignmentDist = 1.5; % Расстояние выравнивания
minSpeed = 0.5; % Минимальная скорость maxSpeed = 20; % Максимальная скорость
% Параметры симуляции
numIterations = 200; % Количество итераций
fieldSize = 50; |
% Размер поля |
dt = 0.1; |
% Шаг времени |
% Цвета для разных боидов colors = lines(numBoids);
%% Инициализация боидов
% Начальные позиции (случайные в пределах поля) positions = (rand(numBoids, 2) – 0.5) * fieldSize;
% Начальные скорости (случайные направления с фиксированной скоростью) initialSpeed = (minSpeed + maxSpeed) / 2;
angles = 2 * pi * rand(numBoids, 1);
velocities = initialSpeed * [cos(angles), sin(angles)];
% Массивы для хранения траекторий
trajectories = cell(numBoids, 1);
22
for i = 1:numBoids trajectories{i} = positions(i, :);
end
%% Создание графического окна figure('Position', [100, 100, 1200, 500]);
% Подграфик 1: Анимация движения subplot(1, 2, 1);
hAx1 = gca; hold on; axis equal;
xlim([–fieldSize, fieldSize]); ylim([–fieldSize, fieldSize]); grid on;
xlabel('X');
ylabel('Y');
title('Анимация движения боидов');
% Создание графических объектов для боидов boidPlots = gobjects(numBoids, 1); trajectoryPlots = gobjects(numBoids, 1); velocityQuivers = gobjects(numBoids, 1);
for i = 1:numBoids % Траектории
trajectoryPlots(i) = plot(NaN, NaN, '–', 'Color', colors(i, :), ...
'LineWidth', 0.5, 'DisplayName', sprintf('Боид %d', i)); % Векторы скорости
velocityQuivers(i) = quiver(positions(i,1), positions(i,2), ...
velocities(i,1), velocities(i,2), 'Color', colors(i, :), ...
'LineWidth', 2, 'MaxHeadSize', 2, 'AutoScale', 'off'); % Маркеры боидов
23
boidPlots(i) = plot(positions(i,1), positions(i,2), 'o', ...
'MarkerSize', 10, 'MarkerFaceColor', colors(i, :), ...
'MarkerEdgeColor', 'k', 'LineWidth', 1);
end
legend(trajectoryPlots, 'Location', 'bestoutside'); hold off;
% Подграфик 2: Визуализация влияния правил subplot(1, 2, 2);
hAx2 = gca; hold on; axis equal;
xlim([–fieldSize, fieldSize]); ylim([–fieldSize, fieldSize]); grid on;
xlabel('X');
ylabel('Y');
title('Визуализация влияния правил'); hold off;
%% Главный цикл симуляции for iteration = 1:numIterations
% Буферы для новых скоростей и позиций newVelocities = zeros(size(velocities));
for i = 1:numBoids
% Поиск соседей в пределах максимального радиуса взаимодействия maxInteractionDist = max([cohesionDist, separationDist, alignmentDist]); dists = sqrt(sum((positions – positions(i, :)).^2, 2));
neighbors = find(dists > 0 & dists < maxInteractionDist);
% Инициализация векторов правил separationVec = [0, 0];
24
cohesionVec = [0, 0]; alignmentVec = [0, 0]; numNeighbors = length(neighbors); if numNeighbors > 0
% Вычисление разделения (отталкивание от близких соседей) closeNeighbors = neighbors(dists(neighbors) < separationDist);
if ~isempty(closeNeighbors)
for j = 1:length(closeNeighbors) neighborIdx = closeNeighbors(j);
diff = positions(i, :) – positions(neighborIdx, :); dist = dists(neighborIdx);
if dist > 0
separationVec = separationVec + (diff / dist^2); end
end
separationVec = separationVec / length(closeNeighbors); end
%Вычисление сцепления (движение к центру масс соседей) cohesionNeighbors = neighbors(dists(neighbors) < cohesionDist); if ~isempty(cohesionNeighbors)
centerOfMass = mean(positions(cohesionNeighbors, :), 1); cohesionVec = (centerOfMass – positions(i, :)); distToCenter = norm(cohesionVec);
if distToCenter > 0
cohesionVec = cohesionVec / distToCenter; end
end
%Вычисление выравнивания (совпадение со средней скоростью соседей)
alignmentNeighbors = neighbors(dists(neighbors) < alignmentDist);
25
if ~isempty(alignmentNeighbors)
avgVelocity = mean(velocities(alignmentNeighbors, :), 1); alignmentVec = avgVelocity – velocities(i, :);
alignMag = norm(alignmentVec); if alignMag > 0
alignmentVec = alignmentVec / alignMag; end
end end
% Применение весов к правилам
separationVec = separationWeight * separationVec; cohesionVec = cohesionWeight * cohesionVec; alignmentVec = alignmentWeight * alignmentVec; % Вычисление новой скорости
acceleration = separationVec + cohesionVec + alignmentVec; % Нормализация ускорения
accelMag = norm(acceleration); if accelMag > 0
acceleration = acceleration / accelMag; end
% Обновление скорости
newVelocities(i, :) = velocities(i, :) + acceleration * dt; % Ограничение скорости
speed = norm(newVelocities(i, :)); if speed > maxSpeed
newVelocities(i, :) = (newVelocities(i, :) / speed) * maxSpeed; elseif speed < minSpeed
newVelocities(i, :) = (newVelocities(i, :) / max(speed, 0.001)) * minSpeed; end
26
% Обновление траектории
trajectories{i} = [trajectories{i}; positions(i, :)]; end
% Обновление позиций velocities = newVelocities;
positions = positions + velocities * dt;
% Обработка границ поля (отражение от стен) for i = 1:numBoids
if abs(positions(i, 1)) > fieldSize velocities(i, 1) = –velocities(i, 1);
positions(i, 1) = sign(positions(i, 1)) * fieldSize * 0.99; end
if abs(positions(i, 2)) > fieldSize velocities(i, 2) = –velocities(i, 2);
positions(i, 2) = sign(positions(i, 2)) * fieldSize * 0.99; end
end
%% Обновление графики
% Обновление анимации subplot(1, 2, 1);
for i = 1:numBoids
% Обновление маркера боида
set(boidPlots(i), 'XData', positions(i, 1), 'YData', positions(i, 2)); % Обновление траектории
set(trajectoryPlots(i), 'XData', trajectories{i}(:,1), ...
'YData', trajectories{i}(:,2));
% Обновление вектора скорости
set(velocityQuivers(i), 'XData', positions(i, 1), ...
'YData', positions(i, 2), ...
27
'UData', velocities(i, 1), 'VData', velocities(i, 2));
end
% Обновление визуализации влияния правил subplot(1, 2, 2);
cla; hold on;
axis equal;
xlim([–fieldSize, fieldSize]); ylim([–fieldSize, fieldSize]); grid on;
title(sprintf('Визуализация влияния правил (Итерация: %d)', iteration)); % Отображение зон влияния для первого боида
i = 1; % Анализируем первого боида
% Рисуем круги зон влияния theta = linspace(0, 2*pi, 100);
% Зона разделения (красная)
xSep = separationDist * cos(theta) + positions(i, 1); ySep = separationDist * sin(theta) + positions(i, 2); fill(xSep, ySep, [1, 0.8, 0.8], 'EdgeColor', [1, 0.5, 0.5], ...
'LineWidth', 1, 'FaceAlpha', 0.3);
text(positions(i,1), positions(i,2)+separationDist*1.1, ...
'Разделение', 'Color', [1, 0, 0], 'HorizontalAlignment', 'center'); % Зона выравнивания (синяя)
xAlign = alignmentDist * cos(theta) + positions(i, 1); yAlign = alignmentDist * sin(theta) + positions(i, 2); plot(xAlign, yAlign, 'b––', 'LineWidth', 1); text(positions(i,1), positions(i,2)+alignmentDist*1.1, ...
'Выравнивание', 'Color', [0, 0, 1], 'HorizontalAlignment', 'center'); % Зона сцепления (зелёная)
28
xCoh = cohesionDist * cos(theta) + positions(i, 1); yCoh = cohesionDist * sin(theta) + positions(i, 2); plot(xCoh, yCoh, 'g––', 'LineWidth', 1); text(positions(i,1), positions(i,2)+cohesionDist*1.1, ...
'Сцепление', 'Color', [0, 0.7, 0], 'HorizontalAlignment', 'center'); % Поиск соседей в разных зонах
dists = sqrt(sum((positions – positions(i, :)).^2, 2)); separationNeighbors = find(dists > 0 & dists < separationDist); alignmentNeighbors = find(dists > 0 & dists < alignmentDist); cohesionNeighbors = find(dists > 0 & dists < cohesionDist); % Отображение боидов
for b = 1:numBoids color = colors(b, :); markerSize = 8;
if b == i
color = 'k'; % Выделяем анализируемого боида markerSize = 12;
elseif ismember(b, separationNeighbors)
color = [1, 0, 0]; % Красный для соседей в зоне разделения elseif ismember(b, alignmentNeighbors)
color = [0, 0, 1]; % Синий для соседей в зоне выравнивания elseif ismember(b, cohesionNeighbors)
color = [0, 0.7, 0]; % Зелёный для соседей в зоне сцепления end
plot(positions(b, 1), positions(b, 2), 'o', ...
'MarkerSize', markerSize, 'MarkerFaceColor', color, ...
'MarkerEdgeColor', 'k', 'LineWidth', 1); % Векторы скорости quiver(positions(b,1), positions(b,2), ...
29
velocities(b,1), velocities(b,2), 0.5, ...
'Color', colors(b, :), 'LineWidth', 1.5, 'MaxHeadSize', 1);
end
% Отображение центра масс для зоны сцепления if ~isempty(cohesionNeighbors)
centerOfMass = mean(positions(cohesionNeighbors, :), 1);
plot(centerOfMass(1), centerOfMass(2), 'gx', ...
'MarkerSize', 15, 'LineWidth', 2);
plot([positions(i,1), centerOfMass(1)], ...
[positions(i,2), centerOfMass(2)], 'g–', 'LineWidth', 2);
end hold off;
% Пауза для анимации pause(0.05);
% Обновление заголовка первого графика subplot(1, 2, 1);
title(sprintf('Движение боидов (Итерация: %d/%d)', iteration, numIterations)); end
%% Отображение итоговой статистики
fprintf('=== СТАТИСТИКА СИМУЛЯЦИИ ===\n'); fprintf('Количество боидов: %d\n', numBoids); fprintf('Количество итераций: %d\n', numIterations); fprintf('Параметры алгоритма:\n');
fprintf(' Вес сцепления: %.2f\n', cohesionWeight); fprintf(' Вес разделения: %.2f\n', separationWeight); fprintf(' Вес выравнивания: %.2f\n', alignmentWeight);
fprintf(' Скорости: min=%.2f, max=%.2f\n', minSpeed, maxSpeed); % Вычисление средней скорости
avgSpeed = mean(sqrt(sum(velocities.^2, 2))); 30
