Скачиваний:
0
Добавлен:
12.02.2026
Размер:
1.23 Mб
Скачать

Список литературы

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).

Приложение Листинг программы

%% Инициализация параметров

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);

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');

% Маркеры боидов

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];

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);

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

% Обновление траектории

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), ...

'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');

% Зона сцепления (зелёная)

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), ...

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)));

fprintf('Средняя конечная скорость: %.3f\n', avgSpeed);

% Вычисление степени когерентности движения

if numBoids > 1

normalizedVelocities = velocities ./ sqrt(sum(velocities.^2, 2));

coherence = abs(sum(normalizedVelocities(:,1)) + 1i*sum(normalizedVelocities(:,2))) / numBoids;

fprintf('Степень когерентности движения: %.3f (1 = идеально согласовано)\n', coherence);

end

Соседние файлы в папке Движение стай (Boids)