Система частиц для игры в стиле ASCII-art

Система частиц — распространённый способ создания эффектов вроде огня, взрывов, дыма и так далее. В этой статье я расскажу, как разработал систему частиц для своей игры Unsigned Character, выполненной в стиле ASCII-art.

Цель: разработать универсальную систему частиц для эффектов пламени, взрыва, шлейфа от снарядов и так далее.

Частицы должны имитировать статичное пламя, а также формировать след за движущимся объектом. Вся графика в игре состоит из символов. Поэтому частицы тоже должны рисоваться символами. И разработать это придётся с нуля. В качестве вдохновения я использовал эти анимации из игры asciident:

1. Поток частиц

Я начал с простого. Частицы спавнятся в фиксированной точке. С некоторым интервалом они поднимаются на строку вверх и рандомно сдвигаются по горизонтали (от -1 до 1 символа). Попутно на каждом уровне (строке) выбирается нужный цвет и рандомный символ из заданного набора для этого уровня.

Распределение цветов и вариантов символа по уровням выглядело как-то так:

private static String _frameVariants[] = { "&@0", "&0", "o0*", "o*'", "*.,", "., ", ". ", ". " }; private static FormattedCharacter.Color _frameColors[] = { FormattedCharacter.Color.White, FormattedCharacter.Color.LightYellow, FormattedCharacter.Color.Yellow, FormattedCharacter.Color.Gray, FormattedCharacter.Color.Gray, FormattedCharacter.Color.Gray, FormattedCharacter.Color.Gray, FormattedCharacter.Color.Gray, };

2. Синхронизация потоков

Далее хочется получить более «объёмное» пламя. Поэтому спавним несколько частиц за раз (по умолчанию 3).

Частицы спавнятся в одной точке и могут попадать на одинаковые координаты на следующих уровнях. Поэтому отслеживаем все перекрывающиеся частицы и делаем видимой только одну из них (любую). В результате получаем что-то вроде этого.

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

3. Анимация частиц

Теперь для частицы рандомно выбирается не один символ, а последовательность символов, которую он должен проиграть прежде чем перейдёт на следующий уровень.

Заодно, я вынес конфигурацию эффектов в json. Вот так выглядит единый набор анимаций частиц:

"animations": [ ".oO0@*", "o0@*", "o&*", ".o&*", "o*'", ".o", ".c*'", ".*", ".,'", ".'", ".' ", ".", ".", " " ],

Каждая строка формирует последовательность «кадров». Можно заметить, что анимации изображают частицу, движущуюся вверх. Из нижнего положения (. или o) в верхнее (* или ').

4. Движение за объектом

У нас есть пламя со статичным источником. Теперь хочется, чтобы источник мог двигаться. В игре физические объекты могут двигаться попиксельно. Их координаты не привязаны к символьной сетке. Но для частиц это недопустимо. Если частицы не будут выровнены по сетке, они будут перекрываться друг с другом, что сильно испортит эффект. Поэтому частицы всегда спавнятся и движутся по символьным координатам. Это создаёт некий рассинхрон. Место спавна частиц не точно совпадает с источником пламени, но это не критично.

Другая проблема — при быстром движении источника огненный след получается очень редким. Решение — спавнить внеочередную частицу в момент, когда источник пламени меняет символьные координаты (строка/столбец).

Если раньше источник был в одном символе, а теперь в другом — спавним в новом месте новую частицу не дожидаясь, когда придёт время обычного спавна. И не просто спавним, а рандомно с заданной вероятностью. Эта вероятность зависит от количества потоков, то есть количества частиц, которое спавнится обычным образом (в пункте 2 мы дали этой величине значение по умолчанию 3).

Кроме того делаем дополнительные модификации, чтобы огонь в движении выглядел реалистичнее.

4.1. Длительность кадра

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

Я сделал так, чтобы при движении длительность кадра начиналась с более низкой величины и возвращалась к стандартному значению с ростом уровня. Проще говоря, при движении источника более низкие частицы «горят» быстрее. И чем быстрее движется источник, тем ниже начальная длительность кадра.

4.2. Вероятность подъёма

С ускорением «кадров» частицы начинают быстрее подниматься и формируют более размытый след. Чтобы избежать этого, я немного переделал движение частиц. Теперь при смене кадров они поднимаются не всегда, а рандомно с некоторой вероятностью. Вероятность эта равна 0 на самом первом уровне и поднимается до 1 на самом последнем.

Кроме того, перед подъёмом и сдвигом частицы проверяется пересечение с картой. Частицы не заходят на статичные занятые области уровня.
В итоге получился такой результат:

Этого факела нет в игре, но он отлично демонстрирует работу системы частиц

5. Затухающее пламя

Небольшая модификация позволит сделать пламя, которое разгорается по команде и затухает со временем. Достаточно уменьшать количество потоков (которое до этого всё время было равно 3) и задавать начальное смещение кадра.

То есть под конец «жизни» новые частицы создаются с более «холодным» цветом и более «тусклой» анимацией, как будто они находятся на более высоком уровне. Когда остаётся один поток, эта величина (начальное смещение кадра) постепенно увеличивается, пока пламя совсем не затухнет.

6. Одноразовый спавн частиц

Одноразовый спавн нужен, чтобы вручную сформировать пламя, которое не будет воспроизводиться. Это используется для эффекта взрыва. В заданных координатах создаётся частица. Она проживает свой жизненный цикл и исчезает. Следующие частицы продолжают спавниться в стандартном месте.

В этом взрыве также создаются два невидимых физических объекта, со своими системами частиц.

7. Ссылки

312312
31 комментарий

Ох, шикарно. Если честно, глядя на эти гифки, впервые захотелось поиграть в ASCII игру.

37

Dwarf fortress в помощь)

3

Welcome to the club, buddy

Это ж гениально, блин.

6

Комментарий недоступен

2

Игра уже есть. Отдалённо напоминает пещеру с необычной растительностью.

3