👾 Физика столкновений: зачем разбираться, если движок всё делает сам?
Коллайдеры Unity или Godot — это удобно. Ставишь Box Collider, игра работает.
Но рано или поздно случается одно из двух:
• Персонаж проваливается сквозь пол на высокой скорости
• Хитбоксы не совпадают с визуалом и игроки злятся
• Нужна кастомная физика — платформер кастомной физикой
top-down с трением, пинбол-механика
У меня было такое, что мне это пригодилось для кастомных рейкастов в VR, когда нужно было переключаться по тысячам вершин графа которые движутся в пространстве.
Тогда понимание математики за коллайдерами — не академизм, а рабочий инструмент.
На этой неделе разбираем:
— AABB: самый быстрый прямоугольный коллайдер
— Circle: идеален для снарядов и мячей
— Вектор отражения: отскок без магии
#мат_геймдев #МатРазбор #физика
Коллайдеры Unity или Godot — это удобно. Ставишь Box Collider, игра работает.
Но рано или поздно случается одно из двух:
• Персонаж проваливается сквозь пол на высокой скорости
• Хитбоксы не совпадают с визуалом и игроки злятся
• Нужна кастомная физика — платформер кастомной физикой
top-down с трением, пинбол-механика
У меня было такое, что мне это пригодилось для кастомных рейкастов в VR, когда нужно было переключаться по тысячам вершин графа которые движутся в пространстве.
Тогда понимание математики за коллайдерами — не академизм, а рабочий инструмент.
На этой неделе разбираем:
— AABB: самый быстрый прямоугольный коллайдер
— Circle: идеален для снарядов и мячей
— Вектор отражения: отскок без магии
#мат_геймдев #МатРазбор #физика
🔥13❤🔥3❤1
📊 AABB — прямоугольник против прямоугольника без тригонометрии
AABB (Axis-Aligned Bounding Box) — прямоугольник, стороны которого
параллельны осям координат. Никакого поворота.
Проверка столкновения двух AABB — четыре сравнения чисел:
Никакого корня квадратного, никаких синусов. Четыре сравнения.
Это причина, по которой большинство игровых движков используют AABB
для первичной проверки (broad phase) даже у сложных объектов.
Минус: не работает если объект повёрнут. Для поворота — OBB или SAT.
Но для большинства платформеров и top-down игр AABB хватает с головой.
#мат_геймдев #МатРазбор #физика #коллайдеры
AABB (Axis-Aligned Bounding Box) — прямоугольник, стороны которого
параллельны осям координат. Никакого поворота.
Проверка столкновения двух AABB — четыре сравнения чисел:
struct AABB
{
public float minX, maxX, minY, maxY;
}
bool Overlaps(AABB a, AABB b)
{
return a.maxX > b.minX && // a не левее b
a.minX < b.maxX && // a не правее b
a.maxY > b.minY && // a не ниже b
a.minY < b.maxY; // a не выше b
}
Никакого корня квадратного, никаких синусов. Четыре сравнения.
Это причина, по которой большинство игровых движков используют AABB
для первичной проверки (broad phase) даже у сложных объектов.
Минус: не работает если объект повёрнут. Для поворота — OBB или SAT.
Но для большинства платформеров и top-down игр AABB хватает с головой.
#мат_геймдев #МатРазбор #физика #коллайдеры
🔥10❤🔥1
🤔 Загадка: почему быстрый персонаж проваливается сквозь пол?
Коллайдеры настроены правильно. На низкой скорости всё работает.
Но разогнать персонажа — и он прошивает платформу насквозь.
Пауза — подумай секунду.
...
Ответ: Tunneling (туннелирование).
Физика в играх дискретная: каждый кадр объект перемещается на velocity * deltaTime.
Если объект за один кадр перемещается дальше, чем толщина коллайдера —
движок просто не замечает пересечения. Объект был над полом, стал под ним.
Столкновения не было.
Три решения:
1. Continuous collision detection (CCD) — движок проверяет весь путь, не точку.
Дорого по производительности.
2. Ограничить максимальную скорость так, чтобы за кадр объект не мог
пролететь сквозь коллайдер.
3. Raycast в направлении движения перед шагом — рукопашная CCD.
Знал о туннелировании раньше? 👇
#мат_геймдев #физика #коллайдеры #ОшибкаНедели
Коллайдеры настроены правильно. На низкой скорости всё работает.
Но разогнать персонажа — и он прошивает платформу насквозь.
Пауза — подумай секунду.
...
Ответ: Tunneling (туннелирование).
Физика в играх дискретная: каждый кадр объект перемещается на velocity * deltaTime.
Если объект за один кадр перемещается дальше, чем толщина коллайдера —
движок просто не замечает пересечения. Объект был над полом, стал под ним.
Столкновения не было.
Три решения:
1. Continuous collision detection (CCD) — движок проверяет весь путь, не точку.
Дорого по производительности.
2. Ограничить максимальную скорость так, чтобы за кадр объект не мог
пролететь сквозь коллайдер.
3. Raycast в направлении движения перед шагом — рукопашная CCD.
Знал о туннелировании раньше? 👇
#мат_геймдев #физика #коллайдеры #ОшибкаНедели
🔥4❤🔥2
📊 Circle-коллайдер: почему он лучше AABB для снарядов
AABB — быстрый, но у него слабое место: он прямоугольный.
Пуля, мяч, персонаж в top-down — у них нет углов. AABB даёт ложные
столкновения в углах, которых визуально нет.
Circle решает это элегантно. Проверка столкновения двух окружностей —
одно вычитание и одно сравнение:
Никакой тригонометрии. Корень не нужен — сравниваем квадраты.
Когда выбирать Circle:
• снаряды (пули, фаерболы) — форма симметрична
• мячи, монеты, бонусы — очевидно
• персонажи в top-down — нет чёткой «передней» стороны
Когда выбирать AABB:
• платформы, стены, тайлы — прямоугольные по природе
• хитбоксы, которые должны «прижиматься» к форме спрайта
На практике движки используют их вместе: AABB — быстрая первичная
проверка (broad phase), Circle или Polygon — точная (narrow phase).
#мат_геймдев #МатРазбор #физика #коллайдеры
AABB — быстрый, но у него слабое место: он прямоугольный.
Пуля, мяч, персонаж в top-down — у них нет углов. AABB даёт ложные
столкновения в углах, которых визуально нет.
Circle решает это элегантно. Проверка столкновения двух окружностей —
одно вычитание и одно сравнение:
bool CirclesOverlap(Vector2 posA, float rA, Vector2 posB, float rB)
{
float dx = posA.x - posB.x;
float dy = posA.y - posB.y;
float distSq = dx * dx + dy * dy; // квадрат расстояния
float radiusSum = rA + rB;
return distSq < radiusSum * radiusSum; // без корня — быстрее
}
Никакой тригонометрии. Корень не нужен — сравниваем квадраты.
Когда выбирать Circle:
• снаряды (пули, фаерболы) — форма симметрична
• мячи, монеты, бонусы — очевидно
• персонажи в top-down — нет чёткой «передней» стороны
Когда выбирать AABB:
• платформы, стены, тайлы — прямоугольные по природе
• хитбоксы, которые должны «прижиматься» к форме спрайта
На практике движки используют их вместе: AABB — быстрая первичная
проверка (broad phase), Circle или Polygon — точная (narrow phase).
#мат_геймдев #МатРазбор #физика #коллайдеры
❤🔥7
📊 Вектор отражения: как математика делает отскок
Мяч летит в стену. Как посчитать направление после отскока?
Интуиция подсказывает: «отразить угол». Но угол — это арктангенс,
градусы, неудобно. Есть формула, которая работает через векторы:
R = V - 2 * (V · N) * N
Где:
V — вектор направления до удара
N — нормаль поверхности (перпендикуляр к стене, единичный вектор)
R — вектор направления после отскока
В Unity это уже встроено:
Но понимать формулу важно — потому что Reflect работает для любого угла.
Не только для горизонтальных/вертикальных стен, но и для наклонных
поверхностей, движущихся платформ, рикошетов.
Откуда формула? V · N — это «насколько вектор V направлен вдоль N».
2 * (V · N) * N — это та часть V, которую нужно «отзеркалить».
Вычитаем её из V дважды — получаем отражение.
#мат_геймдев #МатРазбор #физика #векторы
Мяч летит в стену. Как посчитать направление после отскока?
Интуиция подсказывает: «отразить угол». Но угол — это арктангенс,
градусы, неудобно. Есть формула, которая работает через векторы:
R = V - 2 * (V · N) * N
Где:
V — вектор направления до удара
N — нормаль поверхности (перпендикуляр к стене, единичный вектор)
R — вектор направления после отскока
Vector2 Reflect(Vector2 velocity, Vector2 normal)
{
// normal должен быть нормализован
float dot = Vector2.Dot(velocity, normal);
return velocity - 2f * dot * normal;
}
// Примеры нормалей:
// Пол (горизонтальная поверхность) → normal = Vector2.up (0, 1)
// Стена справа → normal = Vector2.left (-1, 0)
// Наклонная платформа 45° → normal = new Vector2(-0.707f, 0.707f)
// Использование:
ballVelocity = Reflect(ballVelocity, wallNormal);
В Unity это уже встроено:
ballVelocity = Vector2.Reflect(ballVelocity, wallNormal);
Но понимать формулу важно — потому что Reflect работает для любого угла.
Не только для горизонтальных/вертикальных стен, но и для наклонных
поверхностей, движущихся платформ, рикошетов.
Откуда формула? V · N — это «насколько вектор V направлен вдоль N».
2 * (V · N) * N — это та часть V, которую нужно «отзеркалить».
Вычитаем её из V дважды — получаем отражение.
#мат_геймдев #МатРазбор #физика #векторы
👍3🔥3❤🔥1
Отражение векторов - Интерактив
https://wreath-violet-94758009.figma.site/
Я пока думаю в каком формате делать всякие интерактивные штуки чтобы было проще воспринимать формулы и код. Гифки там или интерактивные сайты. Для последнего надо бюджет на тачку и домен для этого дела выделить, а пока не до того) Экспериментально из любопытства собрал в ИИ фигмы интерактив объясняющий пост выше.
Как решение задачи стоящей не совсем подходит, но в целом инструмент забавный кстати говоря.
#мат_геймдев #МатРазбор #физика #векторы
https://wreath-violet-94758009.figma.site/
Я пока думаю в каком формате делать всякие интерактивные штуки чтобы было проще воспринимать формулы и код. Гифки там или интерактивные сайты. Для последнего надо бюджет на тачку и домен для этого дела выделить, а пока не до того) Экспериментально из любопытства собрал в ИИ фигмы интерактив объясняющий пост выше.
Как решение задачи стоящей не совсем подходит, но в целом инструмент забавный кстати говоря.
#мат_геймдев #МатРазбор #физика #векторы
🔥3❤1👍1👎1
🎲 Откуда берётся «случайность» в компьютере?
Спойлер: её нет. Компьютер генерирует числа по формуле — детерминированно,
шаг за шагом. Такая последовательность называется псевдослучайной (PRNG).
Простейший PRNG — линейный конгруэнтный генератор (LCG):
Три магических константы a, c, m — и каждый вызов выдаёт «новое» число.
Пример на пальцах (a=1664525, c=1013904223, m=2^32):
Вызови три раза — получишь три разных числа. Вызови снова с state=42 — те же три.
Почему это важно для геймдева:
→ Ты контролируешь «случайность» через начальное значение (seed)
→ Один и тот же seed = один и тот же мир / один и тот же дроп
→ Это основа воспроизводимых миров, реплеев и читов через save-scumming
Unity использует System.Random под капотом — тот же принцип, чуть сложнее.
Godot — свой Mersenne Twister. Но идея одна: формула + стартовое число.
Существуют ГСЧ, основанные на сборе энтропии — они «более случайные», и главная задача таких генераторов — быть непредсказуемыми. Применяются они там, где взлом или предсказуемость недопустимы: в криптографии, защите данных, онлайн-сервисах. Это область весьма сложных идей и алгоритмов: получить по-настоящему случайное, равновероятно распределённое число от 0 до 1 — задача совсем не тривиальная.
Вообще важно понимать природу случайности — это абстрактное понятие, придуманное человеком. Я когда-то задумывался: бросить монетку 50 на 50 — спорное утверждение. Ведь по сути невозможно поставить чистый эксперимент, который бы это достоверно доказал. Так что под случайностью мы скорее понимаем непредсказуемость. И псевдослучайность по примеру выше работает как настоящая случайность ровно до тех пор, пока ты не знаешь, как она устроена.
Если тема интересна — расскажу про ТГСЧ и КГПСЧ: генераторы, которые используются в защите данных и криптографии. Ну и хорошими постами делись с друзьями — «нам нужно большезолота подписчиков».
#мат_геймдев #МатРазбор #рандом
Спойлер: её нет. Компьютер генерирует числа по формуле — детерминированно,
шаг за шагом. Такая последовательность называется псевдослучайной (PRNG).
Простейший PRNG — линейный конгруэнтный генератор (LCG):
next = (a * current + c) % m
Три магических константы a, c, m — и каждый вызов выдаёт «новое» число.
Пример на пальцах (a=1664525, c=1013904223, m=2^32):
uint state = 42; // начальное значение (сид)
uint NextRandom()
{
state = 1664525u * state + 1013904223u;
return state;
}
Вызови три раза — получишь три разных числа. Вызови снова с state=42 — те же три.
Почему это важно для геймдева:
→ Ты контролируешь «случайность» через начальное значение (seed)
→ Один и тот же seed = один и тот же мир / один и тот же дроп
→ Это основа воспроизводимых миров, реплеев и читов через save-scumming
Unity использует System.Random под капотом — тот же принцип, чуть сложнее.
Godot — свой Mersenne Twister. Но идея одна: формула + стартовое число.
Существуют ГСЧ, основанные на сборе энтропии — они «более случайные», и главная задача таких генераторов — быть непредсказуемыми. Применяются они там, где взлом или предсказуемость недопустимы: в криптографии, защите данных, онлайн-сервисах. Это область весьма сложных идей и алгоритмов: получить по-настоящему случайное, равновероятно распределённое число от 0 до 1 — задача совсем не тривиальная.
Вообще важно понимать природу случайности — это абстрактное понятие, придуманное человеком. Я когда-то задумывался: бросить монетку 50 на 50 — спорное утверждение. Ведь по сути невозможно поставить чистый эксперимент, который бы это достоверно доказал. Так что под случайностью мы скорее понимаем непредсказуемость. И псевдослучайность по примеру выше работает как настоящая случайность ровно до тех пор, пока ты не знаешь, как она устроена.
Если тема интересна — расскажу про ТГСЧ и КГПСЧ: генераторы, которые используются в защите данных и криптографии. Ну и хорошими постами делись с друзьями — «нам нужно больше
#мат_геймдев #МатРазбор #рандом
🔥13❤🔥1❤1
🌱 Seed-рандом: один номер — весь мир
Minecraft. Ты вводишь число при создании мира и получаешь конкретный ландшафт.
Через год вводишь то же число — тот же ландшафт. Это seed.
Seed — это просто начальное значение для PRNG. Пока генератор тот же,
последовательность всегда идентична.
Практический паттерн: отдельный генератор для каждой системы
XOR с разными константами гарантирует, что генераторы идут по разным дорожкам,
но оба воспроизводимы при известном worldSeed.
Три применения:
→ Процедурные карты — один seed = один мир
→ Реплеи — записываешь только ввод игрока + seed, а не весь стейт
→ Отладка — нашёл баг? Скажи seed разработчику, он воспроизведёт
#мат_геймдев #МатРазбор #рандом
Minecraft. Ты вводишь число при создании мира и получаешь конкретный ландшафт.
Через год вводишь то же число — тот же ландшафт. Это seed.
Seed — это просто начальное значение для PRNG. Пока генератор тот же,
последовательность всегда идентична.
Практический паттерн: отдельный генератор для каждой системы
public class SeededRandom
{
private System.Random rng;
public int Seed { get; private set; }
public SeededRandom(int seed)
{
Seed = seed;
rng = new System.Random(seed);
}
public int Next(int min, int max) => rng.Next(min, max);
public float NextFloat() => (float)rng.NextDouble();
}
// Каждый регион карты — свой генератор:
var dungeonRng = new SeededRandom(worldSeed ^ 0xDEAD);
var lootRng = new SeededRandom(worldSeed ^ 0xBEEF);
var enemyRng = new SeededRandom(worldSeed ^ 0xCAFE);
XOR с разными константами гарантирует, что генераторы идут по разным дорожкам,
но оба воспроизводимы при известном worldSeed.
Три применения:
→ Процедурные карты — один seed = один мир
→ Реплеи — записываешь только ввод игрока + seed, а не весь стейт
→ Отладка — нашёл баг? Скажи seed разработчику, он воспроизведёт
#мат_геймдев #МатРазбор #рандом
🔥4
🏰 Процедурная генерация: рандом с правилами
«Процедурная генерация» — не просто случайные числа. Это рандом,
ограниченный правилами так, чтобы результат был играбелен.
Простейший пример — комнатный dungeon:
Шаг 1. Создай сетку N×M, заполни стенами (1).
Шаг 2. Попробуй разместить K случайных прямоугольников-комнат.
Если прямоугольник не пересекается с уже размещёнными — добавь.
Шаг 3. Соедини центры соседних комнат коридорами (L-образный тоннель).
Шаг 4. Поставь старт в первую комнату, выход — в последнюю.
Секрет «не сломанного» уровня: правила ограничивают, рандом заполняет.
Именно это делает Spelunky, Enter the Gungeon, Dead Cells — каждый запуск свой,
но всегда проходимый.
#мат_геймдев #МатРазбор #процедурнаяГенерация
«Процедурная генерация» — не просто случайные числа. Это рандом,
ограниченный правилами так, чтобы результат был играбелен.
Простейший пример — комнатный dungeon:
Шаг 1. Создай сетку N×M, заполни стенами (1).
Шаг 2. Попробуй разместить K случайных прямоугольников-комнат.
Если прямоугольник не пересекается с уже размещёнными — добавь.
Шаг 3. Соедини центры соседних комнат коридорами (L-образный тоннель).
Шаг 4. Поставь старт в первую комнату, выход — в последнюю.
void GenerateDungeon(int seed)
{
var rng = new SeededRandom(seed);
var rooms = new List<RectInt>();
for (int i = 0; i < MAX_ROOMS; i++)
{
int w = rng.Next(MIN_SIZE, MAX_SIZE);
int h = rng.Next(MIN_SIZE, MAX_SIZE);
int x = rng.Next(1, width - w - 1);
int y = rng.Next(1, height - h - 1);
var room = new RectInt(x, y, w, h);
if (!rooms.Any(r => r.Overlaps(room)))
{
CarveRoom(room);
if (rooms.Count > 0)
CarveCorridors(rooms.Last(), room, rng);
rooms.Add(room);
}
}
}
Секрет «не сломанного» уровня: правила ограничивают, рандом заполняет.
Именно это делает Spelunky, Enter the Gungeon, Dead Cells — каждый запуск свой,
но всегда проходимый.
#мат_геймдев #МатРазбор #процедурнаяГенерация
🔥5❤1
🗺️ Два других способа генерировать уровни
Комнатный dungeon — самый простой метод.
Вот ещё два, которые дают другой визуальный стиль.
BSP (Binary Space Partitioning)
Делит пространство пополам рекурсивно, как оригами.
Каждая «половинка» становится комнатой. Переходы между ними — двери.
Результат: более прямоугольные, «архитектурные» уровни.
Используют: Nethack, классические roguelike.
Cellular Automata (клеточные автоматы)
Заполни сетку случайными стенами/полом (50/50).
Потом повтори правило N раз:
«Если у клетки ≥ 5 соседей-стен — она стена, иначе — пол»
Результат: органичные пещеры, плавные очертания.
Используют: Dwarf Fortress, Don't Starve для биомов.
Выбор метода зависит от стиля игры:
→ Dungeon / RPG → комнаты или BSP
→ Survival / Open world → cellular automata
#мат_геймдев #МатРазбор #процедурнаяГенерация
Комнатный dungeon — самый простой метод.
Вот ещё два, которые дают другой визуальный стиль.
BSP (Binary Space Partitioning)
Делит пространство пополам рекурсивно, как оригами.
Каждая «половинка» становится комнатой. Переходы между ними — двери.
Результат: более прямоугольные, «архитектурные» уровни.
Используют: Nethack, классические roguelike.
Корень → [левая половина] [правая половина]
↓ ↓
[комн.] [комн.] [комн.] [комн.]
Cellular Automata (клеточные автоматы)
Заполни сетку случайными стенами/полом (50/50).
Потом повтори правило N раз:
«Если у клетки ≥ 5 соседей-стен — она стена, иначе — пол»
Результат: органичные пещеры, плавные очертания.
Используют: Dwarf Fortress, Don't Starve для биомов.
// Один шаг клеточного автомата
for (int x = 1; x < w-1; x++)
for (int y = 1; y < h-1; y++)
{
int walls = CountWallNeighbors(x, y);
nextGrid[x,y] = walls >= 5 ? 1 : 0;
}
Выбор метода зависит от стиля игры:
→ Dungeon / RPG → комнаты или BSP
→ Survival / Open world → cellular automata
#мат_геймдев #МатРазбор #процедурнаяГенерация
🔥6❤1
🌊 Wave Function Collapse: квантовая физика в генерации уровней
В квантовой механике частица до измерения находится в суперпозиции — она одновременно может быть в нескольких состояниях. Как только ты её измеряешь, волновая функция «схлопывается» в одно конкретное.
В алгоритме ровно та же метафора.
Каждая клетка сетки до заполнения — это суперпозиция всех возможных тайлов: лес, вода, скала, дорога... Как только ты её «наблюдаешь» (выбираешь тайл) — она схлопывается в одно состояние. И это схлопывание мгновенно влияет на соседей — сужает их варианты. Точь-в-точь как квантовая запутанность.
Берёшь набор тайлов и правила: «рядом с водой — только берег, рядом с берегом — трава». Потом заполняешь сетку, соблюдая эти правила. Результат: уровень выглядит как нарисованный вручную — потому что правила берутся из реального образца.
Алгоритм в трёх шагах:
1. Наблюдение — выбери клетку с минимальной энтропией (меньше всего вариантов) и «схлопни» её.
2. Распространение — пройдись по соседям и удали варианты, которые теперь противоречат правилам.
3. Повтори — пока вся сетка не схлопнется.
Физики здесь нет — это чистая метафора. Но название точное: именно так это и ощущается изнутри.
Используют: Caves of Qud, Bad North, Townscaper.
#мат_геймдев #МатРазбор #процедурнаяГенерация
В квантовой механике частица до измерения находится в суперпозиции — она одновременно может быть в нескольких состояниях. Как только ты её измеряешь, волновая функция «схлопывается» в одно конкретное.
В алгоритме ровно та же метафора.
Каждая клетка сетки до заполнения — это суперпозиция всех возможных тайлов: лес, вода, скала, дорога... Как только ты её «наблюдаешь» (выбираешь тайл) — она схлопывается в одно состояние. И это схлопывание мгновенно влияет на соседей — сужает их варианты. Точь-в-точь как квантовая запутанность.
Берёшь набор тайлов и правила: «рядом с водой — только берег, рядом с берегом — трава». Потом заполняешь сетку, соблюдая эти правила. Результат: уровень выглядит как нарисованный вручную — потому что правила берутся из реального образца.
Алгоритм в трёх шагах:
1. Наблюдение — выбери клетку с минимальной энтропией (меньше всего вариантов) и «схлопни» её.
2. Распространение — пройдись по соседям и удали варианты, которые теперь противоречат правилам.
3. Повтори — пока вся сетка не схлопнется.
Физики здесь нет — это чистая метафора. Но название точное: именно так это и ощущается изнутри.
Используют: Caves of Qud, Bad North, Townscaper.
#мат_геймдев #МатРазбор #процедурнаяГенерация
🔥11
🎮 3 игры с крутой процедурной генерацией — что под капотом
Spelunky (2009)
Уровень = 4×4 блока 10×8 клеток.
Каждый блок — один из заранее нарисованных шаблонов (rooms), выбранный рандомно.
Гарантия проходимости: один «опорный» маршрут через блоки всегда есть.
Вывод: случайность + ручные шаблоны = игровой баланс сохранён.
Dead Cells (2018)
Уровни — не случайные, а сборные из готовых секций.
Секции заранее дизайнерски проработаны, порядок и детали — рандомные.
Вывод: меньше математики, больше контроля над feel.
No Man's Sky (2016)
Планеты, существа, растения — из параметрических «рецептов».
Один seed → 18 квинтиллионов планет. Математика через noise-функции и L-системы.
Вывод: глубокая процедурка требует огромных систем параметров.
Общий урок: в хороших играх процедурка никогда не работает одна —
всегда есть ручная работа, которая задаёт рамки. Даже когда я участвовал в разработке Vector 2, там были огромные Yaml генераторы задающие правила генерации уровней, чтобы рандом был интересным.
#мат_геймдев #МатРазбор #процедурнаяГенерация
Spelunky (2009)
Уровень = 4×4 блока 10×8 клеток.
Каждый блок — один из заранее нарисованных шаблонов (rooms), выбранный рандомно.
Гарантия проходимости: один «опорный» маршрут через блоки всегда есть.
Вывод: случайность + ручные шаблоны = игровой баланс сохранён.
Dead Cells (2018)
Уровни — не случайные, а сборные из готовых секций.
Секции заранее дизайнерски проработаны, порядок и детали — рандомные.
Вывод: меньше математики, больше контроля над feel.
No Man's Sky (2016)
Планеты, существа, растения — из параметрических «рецептов».
Один seed → 18 квинтиллионов планет. Математика через noise-функции и L-системы.
Вывод: глубокая процедурка требует огромных систем параметров.
Общий урок: в хороших играх процедурка никогда не работает одна —
всегда есть ручная работа, которая задаёт рамки. Даже когда я участвовал в разработке Vector 2, там были огромные Yaml генераторы задающие правила генерации уровней, чтобы рандом был интересным.
#мат_геймдев #МатРазбор #процедурнаяГенерация
🔥2
⚙️ Rigidbody изнутри: что реально происходит под капотом
Добавляешь Rigidbody — объект начинает падать. Три переменных делают это.
Mass (масса) — сопротивление изменению скорости.
F = m × a. Одна и та же сила разгоняет лёгкий мяч быстрее, чем танк.
В Unity mass не влияет на скорость свободного падения. Зато влияет при AddForce и столкновениях.
Drag — «воздушное сопротивление», гасит линейную скорость.
Каждый кадр: velocity *= (1 - drag × deltaTime)
Drag = 0 → летит вечно. Drag = 5 → тормозит быстро.
Angular drag — то же самое, но для вращения.
Angular velocity — скорость вращения в рад/сек.
#мат_геймдев #МатРазбор #физика #rigidbody
Добавляешь Rigidbody — объект начинает падать. Три переменных делают это.
Mass (масса) — сопротивление изменению скорости.
F = m × a. Одна и та же сила разгоняет лёгкий мяч быстрее, чем танк.
В Unity mass не влияет на скорость свободного падения. Зато влияет при AddForce и столкновениях.
Drag — «воздушное сопротивление», гасит линейную скорость.
Каждый кадр: velocity *= (1 - drag × deltaTime)
Drag = 0 → летит вечно. Drag = 5 → тормозит быстро.
Angular drag — то же самое, но для вращения.
Angular velocity — скорость вращения в рад/сек.
// Снаряд с закруткой
var rb = bullet.GetComponent<Rigidbody>();
rb.mass = 0.02f;
rb.drag = 0.01f;
rb.angularDrag = 0f;
rb.linearVelocity = transform.forward * 50f;
rb.angularVelocity= transform.forward * 30f; // закрутка
#мат_геймдев #МатРазбор #физика #rigidbody
🔥4❤1
🎯 Rigidbody: настройки для пяти типовых объектов
Одни параметры для пули и для пера — не работает. Вот справочная таблица.
Пуля / снаряд
Mass: 0.01–0.05 · Drag: 0.01 · Angular drag: 0
Gravity: off (или своя) · Collision Detection: Continuous
Игровой персонаж (без CharacterController)
Mass: 70 · Drag: 3–5 · Angular drag: высокий (нет кувырков)
Gravity: on · Constraints: заморозить вращение X и Z
Физический куб / ящик
Mass: 1–10 · Drag: 0.1 · Angular drag: 0.1
Обычные настройки, всё разрешено
Тяжёлый объект (валун, машина)
Mass: 500+ · Drag: 0.5 · Angular drag: 0.5
Высокая масса = сложнее сдвинуть
Перо / листок
Mass: 0.001 · Drag: 10–15 · Angular drag: 10
Высокий drag имитирует сопротивление воздуха
#мат_геймдев #КодПодСкопой #физика #rigidbody
Одни параметры для пули и для пера — не работает. Вот справочная таблица.
Пуля / снаряд
Mass: 0.01–0.05 · Drag: 0.01 · Angular drag: 0
Gravity: off (или своя) · Collision Detection: Continuous
Игровой персонаж (без CharacterController)
Mass: 70 · Drag: 3–5 · Angular drag: высокий (нет кувырков)
Gravity: on · Constraints: заморозить вращение X и Z
Физический куб / ящик
Mass: 1–10 · Drag: 0.1 · Angular drag: 0.1
Обычные настройки, всё разрешено
Тяжёлый объект (валун, машина)
Mass: 500+ · Drag: 0.5 · Angular drag: 0.5
Высокая масса = сложнее сдвинуть
Перо / листок
Mass: 0.001 · Drag: 10–15 · Angular drag: 10
Высокий drag имитирует сопротивление воздуха
#мат_геймдев #КодПодСкопой #физика #rigidbody
🔥5👍4👎1
🐛 #ОшибкаНедели: Is Kinematic — что это вообще значит
Новички часто видят Is Kinematic и включают его, не понимая зачем.
Разберём один раз и навсегда.
Обычный Rigidbody: управляется физикой.
Гравитация тянет вниз, AddForce работает, другие тела толкают его.
Is Kinematic = true: объект управляется кодом/анимацией, не физикой.
Гравитация игнорируется. AddForce не работает.
Но он всё ещё участвует в коллизиях — отталкивает другие тела.
❌ Типичная ошибка: включить Is Kinematic у персонажа, потом удивляться,
почему прыжок не работает.
✅ Когда нужен Kinematic:
→ Платформа, которая движется по скрипту
→ Дверь, управляемая анимацией
→ Враг, которого двигают по NavMesh, но у которого есть коллайдер
→ Объект, управляемый в FixedUpdate вручную
Правило: Kinematic = «я сам двигаю объект», обычный = «физика двигает объект».
#мат_геймдев #ОшибкаНедели #физика #rigidbody
Новички часто видят Is Kinematic и включают его, не понимая зачем.
Разберём один раз и навсегда.
Обычный Rigidbody: управляется физикой.
Гравитация тянет вниз, AddForce работает, другие тела толкают его.
Is Kinematic = true: объект управляется кодом/анимацией, не физикой.
Гравитация игнорируется. AddForce не работает.
Но он всё ещё участвует в коллизиях — отталкивает другие тела.
❌ Типичная ошибка: включить Is Kinematic у персонажа, потом удивляться,
почему прыжок не работает.
✅ Когда нужен Kinematic:
→ Платформа, которая движется по скрипту
→ Дверь, управляемая анимацией
→ Враг, которого двигают по NavMesh, но у которого есть коллайдер
→ Объект, управляемый в FixedUpdate вручную
Правило: Kinematic = «я сам двигаю объект», обычный = «физика двигает объект».
#мат_геймдев #ОшибкаНедели #физика #rigidbody
❤3👍1🔥1
🚀 Velocity, acceleration, friction: Ньютон в аркадной игре
transform.position += speed — работает, но ощущается как деревяшка.
Вот почему и как это исправить.
Три переменных физически честной модели движения:
Каждый кадр (интеграция Эйлера):
Платформер без Rigidbody — конкретный код:
Результат: персонаж разгоняется и плавно тормозит.
Это и есть основа game feel.
#мат_геймдев #МатРазбор #физика
transform.position += speed — работает, но ощущается как деревяшка.
Вот почему и как это исправить.
Три переменных физически честной модели движения:
position — где объект сейчас
velocity — скорость и направление движения
acceleration — как быстро меняется скорость
Каждый кадр (интеграция Эйлера):
velocity += acceleration * Time.deltaTime;
velocity *= (1f - friction * Time.deltaTime); // трение гасит
position += velocity * Time.deltaTime;
Платформер без Rigidbody — конкретный код:
Vector2 velocity = Vector2.zero;
const float ACCEL = 80f;
const float FRICTION= 12f;
const float GRAVITY = 30f;
const float JUMP = 15f;
void Update()
{
float input = Input.GetAxis("Horizontal");
velocity.x += input * ACCEL * Time.deltaTime;
velocity.x *= 1f - FRICTION * Time.deltaTime;
if (!isGrounded) velocity.y -= GRAVITY * Time.deltaTime;
if (isGrounded && Input.GetKeyDown(KeyCode.Space))
velocity.y = JUMP;
transform.position += (Vector3)(velocity * Time.deltaTime);
}
Результат: персонаж разгоняется и плавно тормозит.
Это и есть основа game feel.
#мат_геймдев #МатРазбор #физика
🫡7🔥5👌1
🎲 Демо к статье: WFC + Entropy Bias
Рисуете тепловую карту — получаете уровень.
- W_d — плотность врагов
- W_r — распределение лута
- W_e — точки входа/выхода
Алгоритм решает CSP с весами из вашей карты. Коллапс волновой функции смещается энтропийным биасом под заданные зоны.
👉 Открыть мини-апп по кнопке
🖥 С ПК в фулскрине удобнее.
Рисуете тепловую карту — получаете уровень.
- W_d — плотность врагов
- W_r — распределение лута
- W_e — точки входа/выхода
Алгоритм решает CSP с весами из вашей карты. Коллапс волновой функции смещается энтропийным биасом под заданные зоны.
👉 Открыть мини-апп по кнопке
🖥 С ПК в фулскрине удобнее.
🔥3
Драматургия через математику: как одна формула превращает генератор карт в дизайнера уровней
https://habr.com/ru/articles/1026506/
Вы просили что-то посложнее. Ну держитесь 🙂 Страшные слова, формулы, интерактивное демо. Я заморочился. Так что жду огоньков и репостов друзьям😆
Классический алгоритм Wave Function Collapse создаёт технически валидные, но драматургически «мёртвые» карты, потому что не различает пространство: для него вход, босс-арена и выход равнозначны. Я предлагаю добавить в формулу выбора клетки семантический множитель f(W(x,y)), где W — поле весов, задающее важность каждой зоны: ключевые точки коллапсируют первыми и через propagation определяют структуру всей карты. Разбив поле W на слои (опасность, награда, направление к выходу, нарратив) и добавив модуляцию вероятности тайлов в зависимости от позиции, дизайнер получает инструмент, где задуманная идея уровня воспроизводится в каждом прогоне, а конкретная геометрия остаётся уникальной.
#мат_геймдев #МатРазбор #алгоритмы
https://habr.com/ru/articles/1026506/
Вы просили что-то посложнее. Ну держитесь 🙂 Страшные слова, формулы, интерактивное демо. Я заморочился. Так что жду огоньков и репостов друзьям😆
Классический алгоритм Wave Function Collapse создаёт технически валидные, но драматургически «мёртвые» карты, потому что не различает пространство: для него вход, босс-арена и выход равнозначны. Я предлагаю добавить в формулу выбора клетки семантический множитель f(W(x,y)), где W — поле весов, задающее важность каждой зоны: ключевые точки коллапсируют первыми и через propagation определяют структуру всей карты. Разбив поле W на слои (опасность, награда, направление к выходу, нарратив) и добавив модуляцию вероятности тайлов в зависимости от позиции, дизайнер получает инструмент, где задуманная идея уровня воспроизводится в каждом прогоне, а конкретная геометрия остаётся уникальной.
#мат_геймдев #МатРазбор #алгоритмы
Хабр
Драматургия через математику: WFC + Entropy Bias
Как одна модификация формулы превращает генератор карт в дизайнера уровней Всем привет! Меня зовут Григорий Дядиченко, и я технический продюсер. Играли в Hades? Там дизайнер уровней не бросает кубики....
🔥9❤2
Сделал викторину по математике геймдева
https://dev-math.ru/quiz/
Давно хотел собрать в одном месте те вещи, которые постоянно всплывают на собесах и в рабочих задачах. Начал с короткого списка для себя — и затянуло. Да и захотелось прикольно сделать :)
Если коротко — вопросы про то, что реально ломается в проде: почему персонаж дёргается на больших координатах, когда Lerp превращается в «телепорт», зачем кватернионы, если есть углы Эйлера, и десятки подобных вещей. Если найдёте косяки — кидайте в комменты, поправлю.
Ставим 🔥 и делитесь с друзьями, если зашло. Интересно кто куда пройдет. Постарался всё сделать кайфово, в ностальгической эстетике.
#мат_геймдев #викторина
https://dev-math.ru/quiz/
Давно хотел собрать в одном месте те вещи, которые постоянно всплывают на собесах и в рабочих задачах. Начал с короткого списка для себя — и затянуло. Да и захотелось прикольно сделать :)
Если коротко — вопросы про то, что реально ломается в проде: почему персонаж дёргается на больших координатах, когда Lerp превращается в «телепорт», зачем кватернионы, если есть углы Эйлера, и десятки подобных вещей. Если найдёте косяки — кидайте в комменты, поправлю.
Ставим 🔥 и делитесь с друзьями, если зашло. Интересно кто куда пройдет. Постарался всё сделать кайфово, в ностальгической эстетике.
#мат_геймдев #викторина
🔥16❤1
Математика в Gamedev по-простому pinned «Драматургия через математику: как одна формула превращает генератор карт в дизайнера уровней https://habr.com/ru/articles/1026506/ Вы просили что-то посложнее. Ну держитесь 🙂 Страшные слова, формулы, интерактивное демо. Я заморочился. Так что жду огоньков…»
❓ Загадка недели
Ты написал платформер. Нажимаешь стрелку — персонаж идёт.
Отпускаешь — он продолжает скользить.
Код выглядит вот так:
Почему персонаж не останавливается мгновенно?
Ответ: velocity.x никогда не обнуляется.
При input = 0 скорость не уменьшается, а накапливается сессиями нажатий.
Фикс — добавить трение: velocity.x *= (1 - friction * Time.deltaTime)
или просто обнулять при input == 0:
```csharp
float input = Input.GetAxis("Horizontal");
if (Mathf.Abs(input) > 0.01f)
velocity.x = Mathf.MoveTowards(velocity.x, input * speed, accel * Time.deltaTime);
else
velocity.x = Mathf.MoveTowards(velocity.x, 0f, friction * Time.deltaTime);
```
#мат_геймдев #МатРазбор #физика
Ты написал платформер. Нажимаешь стрелку — персонаж идёт.
Отпускаешь — он продолжает скользить.
Код выглядит вот так:
velocity.x += Input.GetAxis("Horizontal") * speed;
transform.position += velocity * Time.deltaTime;
Почему персонаж не останавливается мгновенно?
При input = 0 скорость не уменьшается, а накапливается сессиями нажатий.
Фикс — добавить трение: velocity.x *= (1 - friction * Time.deltaTime)
или просто обнулять при input == 0:
```csharp
float input = Input.GetAxis("Horizontal");
if (Mathf.Abs(input) > 0.01f)
velocity.x = Mathf.MoveTowards(velocity.x, input * speed, accel * Time.deltaTime);
else
velocity.x = Mathf.MoveTowards(velocity.x, 0f, friction * Time.deltaTime);
```
#мат_геймдев #МатРазбор #физика
❤6👍3🔥2