Математика в Gamedev по-простому
443 subscribers
34 photos
1 video
17 links
Как на самом деле работают стрельба, толпа NPC, графика, физика тканей.

Канал про то, что ИИ не заменит: понимание.

Разборы на пальцах, рабочий код, интерактивы. dev-math.ru

Сотрудничество: @it_bizdev
Download Telegram
Толпа без AI: сотни юнитов на трёх правилах

По голосованию победитель очевиден. Так что начнём. У парижан в Assassin's Creed Unity нет интеллекта. У каждого крысака в Vermintide и зомби из World War Z — тоже. Когда в кадре одновременно сотни персонажей, если бы у каждого крутилось полноценное behavior tree, FPS умер бы.

Так что там — не AI, а несколько локальных правил: «не наступай на ноги соседям», «иди примерно туда же, куда соседи», «не отставай». Собственно, это и есть толпа в играх — эмерджентность. Крейг Рейнольдс описал это в 1986 году и назвал boids.

Дальше начинается инженерия. Days Gone крутит орду из сотен Freakers. World War Z — Swarm Engine с зомби, текущими по уровню как жидкость. Под капотом везде одно и то же: boids, spatial hashing, ORCA, плюс LOD AI на дальних юнитах.

На неделе разберём: три формулы Рейнольдса, почему наивная реализация умирает на 500 юнитах, как ORCA не даёт юнитам сталкиваться в узких местах. 🔥

#мат_геймдев #МатРазбор #AI #algorithms
❤‍🔥18🔥13👍1
Boids: три правила стаи

Когда вы смотрите на стаю скворцов на закате, кажется, что это один организм с общей волей. На самом деле там нет лидера: каждая птица следит примерно за семью ближайшими соседями — независимо от того, как далеко они оказались. Это измерили Ballerini и соавторы в 2008 году по стереосъёмкам стай над Римом (PNAS).

Сам алгоритм старше. Крейг Рейнольдс собрал три правила ещё в 1987-м (SIGGRAPH '87). Само слово boids — от bird-oid object:

separation — отталкивайся от того, кто оказался слишком близко.
alignment — подстраивай свою скорость под среднюю скорость соседей.
cohesion — тянись к их центру масс.

Собственно, в Unity это делается так:


Vector3 sep = Vector3.zero, ali = Vector3.zero, coh = Vector3.zero;
int count = neighbors.Count;

foreach (var other in neighbors) {
Vector3 d = transform.position - other.position;
if (d.sqrMagnitude < sepRadius * sepRadius) sep += d.normalized;
ali += other.velocity;
coh += other.position;
}

velocity += wSep * sep
+ wAli * (ali / count - velocity)
+ wCoh * (coh / count - transform.position);
velocity = Vector3.ClampMagnitude(velocity, maxSpeed);
transform.position += velocity * Time.deltaTime;


Магия — в балансе весов. Большой wSep — юниты разлетаются в пыль. Большой wCoh — слипаются в шарик и стоят. Всё интересное — где-то между.

В играх это стаи в Subnautica, орда Freakers в Days Gone, Swarm Engine в World War Z. Конечно же, в боевой реализации есть ещё seek к цели и avoidance препятствий — но базовая стая держится на этих трёх правилах. 🔥

#мат_геймдев #МатРазбор #AI #biomimicry
🔥12❤‍🔥7👍4
Толпа на сетке: несколько клеток, а не миллион проверок

В boids каждому юниту нужны его ближайшие соседи — без них не сработают ни separation, ни alignment, ни cohesion. Наивный ответ — пройтись по всем и проверить расстояние:


// плохо: O(N²) на каждом кадре
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (i == j) continue;
if ((boid[i].position - boid[j].position).sqrMagnitude < r * r) {
// учесть соседа
}
}
}


Считаем по головам. 100 юнитов — 10 000 проверок за кадр. 500 — 250 000. 1000 — миллион. И всё это 60 раз в секунду.

Итак, spatial hash. Соседи по определению рядом — значит, проверять надо только тех, кто в той же зоне пространства. Разбиваем сцену на клетки фиксированного размера, раскладываем юнитов по клеткам и смотрим рядом:


// каждый кадр пересобираем
var grid = new Dictionary<Vector2Int, List<Boid>>();
foreach (var b in boids) {
var cell = new Vector2Int(
(int)(b.position.x / cellSize),
(int)(b.position.z / cellSize));
if (!grid.TryGetValue(cell, out var list))
grid[cell] = list = new List<Boid>();
list.Add(b);
}


Размер клетки чаще всего берут равными радиусу видимости соседей.

По сути алгоритм boids — это часть задачи. Ещё часть — найти соседей достаточно быстро. Без этого игры с сотнями юнитов на экране просто не существовали бы. 🔥 Кроме того их надо отрендерить, а об этом уже поговорим позже.


#мат_геймдев #МатРазбор #algorithms #optimization
🔥12
Толпа без AI: как три правила, хеш-сетка и ORCA делают массовку живой
https://dev-math.ru/articles/crowd/

Вы просили про толпу. Ну держитесь 🙂 Семь разделов, шесть интерактивов и тысяча юнитов в одном экране браузера. Я заморочился — получилось так объёмно, что статьи скоро можно сшивать в книжку (Поэтому немного задержался. Хотел в пятницу, а получилось в субботу). Жду огоньков и репостов друзьям 🔥

Симуляция массовки упирается в две проблемы: общий behavior tree «толпа делает X» не масштабируется, а наивный поиск соседей даёт O(N²) и кладёт сцену уже на пятистах юнитах. В статье разбираю архитектуру по слоям: три правила Рейнольдса для группового движения, uniform spatial hash для поиска соседей за O(N), упрощённый ORCA для встречных потоков в коридоре, steering behaviors для целей и обхода препятствий, плюс отдельная часть про GPU instancing для рендера тысячи юнитов одним draw call'ом. В финале — разбор под капотом AC Unity, World War Z, They Are Billions, Vermintide 2 и RTS-обобщённо.

#мат_геймдев #МатРазбор #algorithms #AI
🔥22❤‍🔥11
Please open Telegram to view this post
VIEW IN TELEGRAM
Тысяча юнитов в кадре — это, конечно, эффектно. Но в AAA это только начало.

Давайте пройдемся до конца по толпе тогда (по результатам опроса). А потом уже пойдем либо к лутбоксам, либо к ELO и MMR.

В прошлой статье мы разобрали, как оживляют тысячу юнитов через boids + spatial hash на CPU. При этом в Assassin's Creed Unity на улицах Парижа — до 10 000 NPC одновременно. В массовых битвах Total War: Warhammer 2 — тоже десять тысяч, у каждого свой коллайдер и анимация. В Helldivers 2 — плотные волны термидов на одного игрока. They Are Billions — название говорит за себя.

По сути, любая CPU-архитектура решения из прошлой статьи на этом масштабе отвалится. У проблемы два слоя, которые мы не разобрали — глобальная навигация через flow fields (много готовых реализаций, но для такой задачи уже лучше написать свою) и перенос симуляции на GPU (compute shaders + indirect rendering).

Разберем на этой неделе. Всё как обычно со статьей в конце недели. Данная тема прикольна ещё тем, что если углублятся в детали реализации (особенно рендера), то станет понятнее как работает та же Nvidia Flex и подобные технологии.

Ну и делимся постами, ставим реакции, пишем чего хотелось бы ещё в комменты. Короче статьи, больше каких-то примеров. Хочется понимать, что интересно, а что нет. А то по SDF статью посмотрело меньше народу, чем даже по толпе, а вышла она уже неделю как.

#мат_геймдев #МатРазбор #algorithms #performance
👍16🔥4
Flow fields: один расчёт вместо тысячи A*

Тысяча юнитов идёт к одной точке. Если для каждого считать A* — тысяча независимых поисков за кадр. Дороговато.

Как нам сделать меньше расчётов? По своей сути, идея проста. Можно сделать один расчёт вместо тысячи. Посчитали поле — все юниты просто читают из него «куда идти из своей клетки».

Разбиваем карту на сетку. От цели обратной волной (Dijkstra) в каждой клетке считаем дистанцию до цели и записываем вектор на соседа — это и есть flow field. Юниту достаточно прочитать вектор из своей клетки. O(1) на запрос, без зависимости от длины пути и количества юнитов.

«А если цель движется?» В RTS — почти никогда. Клик в точку, поход к зданию — цель статична, поле живёт минутами. При движущейся цели перерсчитать получается в любом случае не так дорого. Одну дейкстру пустить по карте не так долго, как для каждого юнита считать А*. Тем более с шумом от boids там не обязательно моментальная реакция на пользовательский ввод или триггеры.

Проблема возникает если много юнитов и у каждого своя цель — A* снова в выигрыше. Flow fields — это про движение толпы к общей цели. Канонический разбор — Elijah Emerson, AI Game Programming Wisdom 5 (2008).

#мат_геймдев #МатРазбор #algorithms #AI
👍11🔥3
Толпа на максималках: flow fields, GPU и десятки тысяч юнитов в кадре
https://dev-math.ru/articles/crowd-gpu/

Помните прошлую статью про толпу? Я остановился на тысяче юнитов и оставил четыре вещи «на потом» — flow fields, симуляцию на GPU, indirect draw и LOD AI. Обещал продолжение, да и вы за него проголосовали — и вот оно 🙂 Четыре интерактива (один — честная 3D-сцена на three.js, с реальным счётчиком draw call'ов), и снова получилось так объёмно, что впору сшивать в книжку. Жду огоньков и репостов друзьям 🔥

Тысяча юнитов на CPU — это разминка. Десятки тысяч в кадре (Париж в AC Unity, битвы Total War, орды нежити) — уже другая архитектура из четырёх слоёв. Глобальный путь перестаёт быть тысячей независимых A* и становится одним flow field — сеткой направлений на всю толпу за O(1). Симуляция агентов уезжает с ядер CPU на тысячи дорожек GPU (compute shaders + StructuredBuffer). Рендер перестаёт быть draw call'ом на агента и становится indirect draw, где команды отрисовки генерит сам GPU. А то, что не видно вблизи, не считается честно — LOD AI и recycling. В финале — разбор под капотом Planetary Annihilation, Supreme Commander 2, AC Unity, Total War, They Are Billions и Helldivers 2.

Первая часть, если пропустили: https://dev-math.ru/articles/crowd/ Думаю на этом тема толп в играх раскрыта окончательно :)

#мат_геймдев #МатРазбор #algorithms #GPU
🔥17
Скрытое число

В голосовании за тему на втором месте шло ELO и MMR, так что это неделя будет про них. Прямо сейчас у вас есть рейтинг. Даже если вы его ни разу не видели и не сыграли ни одной ранкед-игры — система всё равно держит про вас число.

Заходите вечером в CS или Dota, жмёте «Найти игру» — и через минуту перед вами девять других людей. Откуда сервер знает, что подходят именно эти девять?

Под капотом — математика, которую придумали для шахмат в 1960-х. Та самая, по которой считают рейтинг Магнусу Карлсену: FIDE приняла её в 1970-м, отсюда слово ELO (в честь Арпада Эло, который и придумал эту систему). А дальше она расползлась по всему соревновательному геймдеву — Halo, Rocket League, Lichess, LoL. Движки рейтинга у всех разные, идея одна: у игрока есть скрытое число (MMR), алгоритм сводит близких и пересчитывает всех после матча.

И вот тут начинается интересное. Шахматист играет 1v1 годами. А матчмейкер в шутере должен за секунды собрать из миллионов человек онлайн партию 5v5 с исходом близким к 50/50 — да ещё с поправкой на регион, пинг и штрафы за поведение. По сути это уже не сортировка по рейтингу, а вероятностная модель.

На неделе разберём её целиком: формула ELO в две строчки, почему её перестало хватать, и как Glicko с TrueSkill раздают рейтинг команде из пяти человек после одного матча.

🔥 — если хочется узнать, откуда берётся ваш MMR или написать свою систему рейтинга игроков.

#мат_геймдев #МатРазбор #матчмейкинг #геймдизайн
🔥28👍2🙏21
Рейтинг — это не скилл, а оценка скилла

Ваш истинный скилл — это число, которого никто никогда не видел. Ни вы, ни сервер. Измерить напрямую нельзя — только догадываться по результатам матчей.

А один матч — это плохие данные. Выиграли, потому что сильнее? Или соперник тильтанул, лагнул, один раз зевнул в CS? В одной игре сигнал (ваш скилл) намертво смешан с шумом (везение). Это всего один бит информации о вещественном числе — всерьёз оценивать по нему нечего.

И тут немного надо понимать логику сигнал-шум. Вы сыграли 1000 матчей. У вас есть ваш скилл (сигнал), а есть удача (шум) в любой игре. Тут уже сигнал будет виден ярче, так как всегда чем больше выборка, тем меньше влияние шума на сигнал. Но кроме этого шум мы умеем математически фильтровать, в попытках найти истинное значение сигнала.

Собственно, поэтому ELO не «считает» скилл, а фильтрует его из шума:


E_a = 1 / (1 + 10 ** ((R_b - R_a) / 400)) # модель предсказывает исход
R_a = R_a + K * (S_a - E_a) # факт минус прогноз → поправка


Первая строка — прогноз модели. Вторая — поправка: та же механика, что у экспоненциального сглаживания, новая оценка = старая плюс доля ошибки прогноза S_a − E_a. K — размер этой доли, по сути learning rate: насколько доверять одному матчу.

И тут весь фокус фильтра. Большой K хватается за каждый результат: два равных игрока, каждый матч — монетка, но при K=40 рейтинг скачет на ±20 за партию, хотя скилл не двигался. Это шум, просочившийся в число. Маленький K усредняет много игр — гладко, но реальный рост догоняет медленно. Поэтому FIDE душит шум топам, опуская K до 10: у Карлсена скилл давно известен, дёргать рейтинг из-за одного зевка незачем.

Загвоздка в том, что K фиксированный. Умный фильтр сам бы понимал, когда он не уверен — и доверял данным, а когда уверен — глушил шум. А вот об этом мы и поговорим в статье.

🔥 — если поняли «скилл = сигнал, отфильтрованный из шума». Ну и не забываем делиться с друзьями, это помогает развитию канала.

#мат_геймдев #МатРазбор #матчмейкинг #алгоритмы
🔥251
Одного числа мало. Рейтингу нужна погрешность

В среду мы оставили ELO с фиксированным K — фильтром, который не знает, когда сам себе не доверяет. Новичок и ветеран с рейтингом 1500 для него одинаковы, хотя первое — это «без понятия», а второе — «проверено сотней матчей».

Лечится одним движением: добавьте к рейтингу второе число — насколько система в нём уверена. Это и есть Glicko (Марк Гликман, 1995): рядом с рейтингом живёт RD, разброс оценки. И вот тут сигнал/шум возвращается красиво — RD это, по сути, адаптивный K. Большой RD (новичок или вернулись после года) → рейтинг идёт крупными шагами, жадно ловит сигнал; маленький (тысяча партий за плечами) → шаги мелкие, шум давится. K больше не подбирают руками, он вытекает из уверенности. И сам RD падает, когда вы играете, и растёт за время простоя — старые данные протухают.

TrueSkill (Microsoft Research, 2006, под Halo на Xbox Live) доводит идею до предела: скилл — не точка, а нормальное распределение N(μ, σ), где σ — та же неуверенность, что RD. Матч смещает и сужает колокол. И — чего ELO не умел в принципе — перформанс команды складывается из распределений игроков, так что через factor graph система раздаёт +/− по всей пятёрке после одного матча 5v5.

Ни RD, ни σ не спасают от всего. Они не знают, что вы тильтуете, не отличают соло от стака с друзьями и не ловят смурфов — тот же опытный игрок на новом аккаунте в CS для них просто «новичок с большим σ», которого математика догонит за десяток матчей, испорченных всем остальным. Это уже поведенческие системы, не рейтинг.

В статье — полный разбор: апдейт TrueSkill через factor graph на одном примере, Glicko-2 с псевдокодом и интерактив, где вы сами крутите рейтинги и σ и смотрите, кого алгоритм считает честной парой.

#мат_геймдев #МатРазбор #матчмейкинг #алгоритмы
🔥9
Матчмейкинг по-простому: Сигнал и шум
https://dev-math.ru/articles/matchmaking/

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

Главная мысль — на все полвека одна. Истинный скилл это скрытый сигнал, который нельзя увидеть напрямую. Один матч — шумная однобитная выборка из него. А рейтинг — это фильтр, который оценивает и сам сигнал, и собственную неуверенность в нём. Вся эволюция ELO → Glicko → TrueSkill ровно про это: как сделать фильтр умнее. ELO — фиксированное доверие к одному матчу (тот самый K) и логистическая сигмоида, откуда берётся «400 очков = ×10 шансов». Glicko добавляет второе число RD — адаптивный K, который сам понимает, про кого мы знаем мало. TrueSkill описывает скилл целым гауссовым колоколом N(μ, σ) и раздаёт его по команде через factor graph. В финале — кто это крутит в проде: Chess.com и Lichess на Glicko, Rocket League, Halo на TrueSkill.

Не забываем делиться статьей с друзьями, если она понравится — это помогает развитию канала. Больше подписчиков богам подписчиков. Ну и ставим огоньки естественно. Автор работает за классы :) Я стараюсь в одиночку на пару с клодом делать качественные материалы и хочется чтобы их увидело как можно больше людей.

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

#мат_геймдев #МатРазбор #algorithms #matchmaking
🔥16
Дейв Математик #1. Физика прыжков.

Я решил попробовать сделать комикс. Посмотрим как зайдет. Встречайте Дейв Математик! Олдскульный разработчик игр, который в процессе дебага попадает в свои игровые миры и решает в них проблемы с помощью математики.

Ставьте огоньки, если формат зайдет, поделаю разных комиксов раз в неделю или под вдохновение.

Вообще изначально хотел делать всё в этом нарративе. Так как мне кажется прикольным представлять закулисье игр, как общение с персонажами игр и решением их проблем разработчиком. Может потом из этого можно сделать какую-то маленькую игрушку.

P.S. И накидайте бустов https://t.me/easy_dev_math?boost чтобы можно было публиковать больше стори на канале :)

#матразбор #комикс
🔥21👎72
«Ну сейчас-то повезёт» — это ошибка игрока. А в Dota — фича.

Чтож. Начнём неделю лутбоксов и дропрейта.

Предмет с «25% крита» на первом ударе критует примерно в 8,5% случаев. Цифра в тултипе не врёт — врёт интуиция о том, как она считается.

В Dota крит — не честная монетка, а система с памятью (pseudo-random distribution). То самое «ну сейчас-то повезёт» здесь буквально зашито в формулу: каждый удар без крита поднимает шанс следующего. Старт ~8,5%, дальше выше, к двенадцатому удару крит гарантирован. В среднем по дистанции выходят те самые 25%.

Зачем так? Честные 25% дают жирный хвост невезения: не критануть восемь раз подряд — это 10% случаев, и встречается оно сплошь и рядом. Каждый такой стрик игрок читает как «игра жульничает». PRD режет именно хвост: дольше без крита — ближе крит. Но за это вы слабее в начале серии — короткие неудачные полосы как раз учащаются. По сути, вы не стали критовать чаще, вы перестали попадать в катастрофические серии. Это обмен дисперсии на предсказуемость, который продаётся как «честность».

В статье — вся кухня: как «случайность» умещается в одну строку кода, почему «1% дропа» это совсем не «1 из 100», и где эта математика правит уже не восприятие, а ваш кошелёк 🙂

#мат_геймдев #МатРазбор #геймдизайн
🔥15👎2
По иконке текстурпаков фанаты вычислили seed мира Minecraft через десять лет после скриншота

pack.png видел каждый фанат майнкрафта: холм, озеро, водопад. Откуда кадр — было неизвестно, пока в 2020-м ютубер SalC1 не спросил об этом вслух. Дальше сообщество устроило расследование.

По облакам и ориентации текстур блоков восстановили ракурс, заодно вычислили способ съёмки: Print Screen и кроп до 512×512. Ключевой факт: рельеф зависит только от нижних 48 бит сида — пространство перебора 2⁴⁸, 281 триллион вариантов. Фильтром стало перемешивание песка с землёй в кадре: сгенерировал кусок мира — сравнил. Подняли распределённый счёт на BOINC, 3700 добровольцев, три дня — seed найден: 3257840388504953787. В этот мир можно зайти и встать в точку, откуда сделан кадр.

Что важно для геймдева: детерминированную генерацию игроки вскрывают реверс инжинирингом. Мод SeedCrackerX вычисляет seed сервера по паре данжей, после чего руды и крепости видны насквозь. Это цена детерминизма — правда, он слишком удобен для разработки, без него не было бы ни сидов, ни RNG-манипуляций в спидранах.

В статье — что у таких генераторов внутри.

#мат_геймдев #МатРазбор #алгоритмы
👍5🔥2
Шанс 50/50 выбить нож в CS стоит 660 $

Шанс ножа в кейсе — 0,26%. Эту цифру Valve не публиковала: сообщество годами восстанавливало её по статистике анбоксингов. В 2017-м Министерство культуры Китая обязало раскрывать вероятности лутбоксов, и таблицу выложил Perfect World — издатель, через которого Valve работает в Китае.

Чтож, посчитаем. Памяти у кейсов нет, попытки независимы: шанс хотя бы одного ножа за N кейсов — 1−(1−0,0026)^N. Сотня кейсов — должно же хватить? Конечно же нет: 23%. Монетка 50/50 набегает только к 267-му кейсу — те самые 660 $ ключами. Уверенные 99% — 1769 кейсов, около 4 400 $.

Ну и моё любимое: остаться без ножа после двух тысяч кейсов вдвое вероятнее, чем выбить его из первого же. Не верите — посчитайте. Пустой кейс — это 99,74%. Возведите в степень 2000: остаётся 0,55%. А нож с первой попытки — те самые 0,26%. Шанс «всё ещё пусто» тает вдвое каждые 266 кейсов и нуля не достигает никогда: из 100 000 человек, открывших по 2000 кейсов, около 550 останутся ни с чем — хотя «в среднем» нож выпадает на 385-м.

И вот что я в этой истории так же люблю: та же Valve в Dota подкручивает криты в вашу пользу, лишь бы серия промахов не испортила вам вечер. В кейсах серия неудач продаёт ключи, поэтому кейс номер 2000 ничем не отличается от первого. Даже Genshin даёт гарантию на 90-й крутке. Тут — ни на какой.

Шансы при этом честные, жульничать тут и не нужно.

#мат_геймдев #МатРазбор #геймдизайн
🔥6😁3👎1
Математика невезения: как игры подкручивают случайность, чтобы она ощущалась честной
https://dev-math.ru/articles/lootboxes/

Всю неделю мы разбирали рандом по кусочкам — сегодня забираем всю кухню разом. Внутри вывод констант, пять интерактивов прямо в браузере и цифры, после которых надпись «шанс 1%» читается иначе. Жду 🔥 и репостов друзьям 😆

Игроки читают «1% дропа» как «1 из 100», а random в игре считают честным броском — оба утверждения неверны, и на этом зазоре держатся и фрустрация, и часть монетизации. В статье случайность разобрана как три слоя.

Первый — генератор: LCG в одну строку, детерминизм seed и история pack.png с перебором 2⁴⁸, спектральный тест Кнута и провал RANDU.

Второй — искажение распределения под восприятие: PRD в Dota с численным выводом константы C, pity-системы Genshin и Hearthstone, true hit Fire Emblem, aim assist XCOM и shuffle bag Тетриса.

Третий — экономика дропрейта: геометрическое распределение, 385 против 267 кейсов до ножа CS, кривая 1−(1−p)^N и регулирование лутбоксов от Китая 2017-го до отменённого штрафа EA в Нидерландах. Разработчик получает карту выбора инструмента — голый PRNG, PRD, pity или shuffle bag — и честный список цен, которыми оплачивается каждое сглаживание дисперсии.

#мат_геймдев #МатРазбор #алгоритмы #геймдизайн
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8