Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Programming
Gamedevelopment

Сделайте всплеск с динамическими 2D-эффектами воды

by
Difficulty:IntermediateLength:LongLanguages:

Russian (Pусский) translation by Jenyia (you can also view the original English article)

Sploosh! В этом уроке я покажу вам, как вы можете использовать простые математические, физические и эффекты частиц, чтобы имитировать великолепные 2D волны и капли воды.



Примечание. Хотя этот учебник написан с использованием C # и XNA, вы должны использовать одни и те же методы и концепции практически в любой игровой среде разработки.


Предварительный просмотр результатов

Если у вас XNA, вы можете загрузить исходные файлы и скомпилировать демо. В противном случае ознакомьтесь с демо-видео ниже:

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


Создание волн

Чтобы сделать волны, мы будем моделировать поверхность воды как ряд вертикальных пружин, как показано на этой диаграмме:

Это позволит волнам подниматься и опускаться. Затем мы сделаем частицы воды потянутыми на соседние частицы, чтобы волны распространились.

Спрингс и закон Гука

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

Сила, обеспечиваемая весной, дана Законом Гука:

\[
F = -kx
\]

F - сила, создаваемая пружиной, k - постоянная пружина, а x - смещение пружин от ее естественной длины. Отрицательный знак указывает на то, что сила находится в противоположном направлении, к которому пружина смещена; если вы нажмёте пружину вниз, она будет отжиматься, и наоборот.

Постоянная пружины, k, определяет жесткость пружины.

Чтобы имитировать пружины, мы должны выяснить, как перемещать частицы вокруг по закона Гука. Для этого нам нужно еще несколько формул из физики. Во-первых, второй закон движения Ньютона:

\[
F = ma
\]

Здесь F - сила, m - масса, a - ускорение. Это означает, что сила сильнее надвигается на объект, и чем легче объект, тем больше он ускоряется.

Объединение этих двух формул и переупорядочение дает нам:

\[
a = -\frac{k}{m} x
\]

Это дает нам ускорение для наших частиц. Будем считать, что все наши частицы будут иметь одинаковую массу, поэтому мы можем объединить k / m в одну константу.

Чтобы определить положение от ускорения, нам нужно провести численное интегрирование. Мы собираемся использовать простейшую форму численного интегрирования - каждый кадр мы просто делаем следующее:

Это называется методом Эйлера. Это не самый точный тип численной интеграции, но он быстрый, простой и адекватный для наших целей.

Соединяя все это вместе, наши частицы на поверхности воды будут делать следующие кадры:

Здесь TargetHeight является естественным положением верхней части весны, когда он не растягивается и не сжимается. Вы должны установить это значение там, где вы хотите, чтобы поверхность воды была. Вы должны установить это значение там, где вы хотите, чтобы поверхность воды была.

Натяжение и Увлажнение

Я упомянул ранее, что постоянная пружина k регулирует жесткость пружины. Вы можете отрегулировать это значение, чтобы изменить свойства воды. Низкая постоянная пружины заставит пружина      освободиться. Это означает, что сила заставит большие волны колебаться медленно. И наоборот, высокая постоянная пружина увеличит натяжение пружины. Силы создадут небольшие волны, которые быстро колеблются. Высокая постоянная весна заставит воду походить на смешение Джелло.

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

До сих пор проблема с нашей весенней моделью. Как только пружина начнет колебаться, она никогда не прекратит. Чтобы решить эту проблему, мы должны применить некоторое увлажнение. Идея состоит в том, чтобы применить силу в противоположном направлении, чтобы наша пружина двигалась, чтобы замедлить ее. Это требует небольшой корректировки нашей весенней формулы:

\[
a = -\frac{k}{m} x - dv
\]

Здесь v - скорость, d - коэффициент затухания - другая константа, которую вы можете настроить, чтобы настроить ощущение воды. Это должно быть довольно мало, если вы хотите, чтобы ваши волны колебались. Демо использует коэффициент затухания 0,025. Высокий коэффициент затухания заставит воду выглядеть толстой, как меласса, в то время как низкое значение позволит колебаниям волн в течение длительного времени.

Продвижение волн

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

Сначала я покажу код, а потом перейду к нему:

Этот код будет вызываться каждый кадр из вашего метода Update(). Здесь springs представляют собой массив пружин, расположенных слева направо. leftDeltas - это массив поплавков, который сохраняет разницу в высоте между каждой пружиной и ее левым соседом. rightDeltas является эквивалентом для правых соседей. Мы сохраняем все эти разности высот в массивах, потому что последние два оператора if изменяют высоту пружин. Мы должны измерять разницу высот до того, как какая-либо из высот будет изменена.

Код начинается с запуска закона Гука по каждой пружине, как описано выше. Затем он смотрит на разницу высот между каждой пружиной и ее соседями, и каждая пружина тянет свои соседние пружины к себе, изменяя положения и скорость соседей. Шаг шага соседа повторяется восемь раз, чтобы волны быстрее распространялись.

Существует одно более настраиваемых значение, здесь называется Spread. Он контролирует скорость распространения волн. Он может принимать значения от 0 до 0,5, при этом большие значения увеличивают скорость распространения волн.

Чтобы начать движение волн, мы добавим простой метод Splash().

Каждый раз, когда вы хотите сделать волны, вызовите Splash(). Параметр index определяет, на какой пружине должен появиться всплеск, а параметр seed определяет, насколько велики будут волны.

Рендеринг

Мы будем использовать класс XNA PrimitiveBatch из XNA PrimitivesSample. Класс PrimitiveBatch помогает нам рисовать линии и треугольники непосредственно с графическим процессором. Вы используете его так:

Следует отметить, что по умолчанию вы должны указывать вершины треугольника по часовой стрелке. Если вы добавите их по часовой стрелке, треугольник будет отбракован, и вы его не увидите.

Нет необходимости иметь пружину для каждого пикселя ширины. В демо я использовал 201 источник, распространяющийся по окну шириной 800 пикселей. Это дает ровно 4 пикселя между каждой пружиной, причем первая пружина равна 0, а последняя - 800 пикселей. Вы, вероятно, могли бы использовать еще меньше пружин и все еще иметь гладкую воду.

То, что мы хотим сделать, это нарисовать тонкие высокие трапеции, которые простираются от нижней части экрана до поверхности воды и соединяют пружины, как показано на этой диаграмме:

Поскольку графические карты не рисуют трапецоиды напрямую, мы должны нарисовать каждую трапецию в виде двух треугольников. Чтобы это выглядело немного лучше, мы также сделаем воду темнее, когда она станет глубже, окрашивая нижние вершины синим цветом. GPU автоматически будет интерполировать цвета между вершинами.

Вот результат:


Создание брызга

Волны выглядят неплохо, но я хотел бы увидеть всплеск, когда камень попадет в воду. Эффекты частиц идеально подходят для этого.

Частичные эффекты

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

Первое, что нам нужно, это класс частиц:

Этот класс просто сохраняет свойства, которые может иметь частица. Затем мы создадим список частиц.

Каждый кадр мы должны обновлять и рисовать частицы.

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

Затем мы рисуем частицы.

Теперь, когда мы создаем всплеск, мы создаем кучу частиц.

Вы можете вызвать этот метод из метода Splash(), который мы используем для создания волн. Скорость параметра - это скорость, с которой камень попадает в воду. Мы будем делать большие всплески, если скала будет двигаться быстрее.

GetRandomVector2(40) возвращает вектор со случайным направлением и случайную длину между 0 и 40. Мы хотим добавить немного случайности к позициям, чтобы частицы не все отображались в одной точке. FromPolar() возвращает Vector2 с заданным направлением и длиной.

Вот результат:

Использование метабаллов в качестве частиц

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

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

Что мы инициализируем так:

Затем мы рисуем метабаллы:

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

Вот как это выглядит:

The water droplets now fuse together when they are close. Однако они не сливаются с поверхностью воды. Мы можем исправить это, добавив градиент к поверхности воды, что делает его постепенно исчезать и превращает его в нашу цель рендеринга метабалла.

Добавьте следующий код в метод выше до линии GraphicsDevice.SetRendertarget(null):

Теперь частицы будут сливаться с поверхностью воды

Добавление эффекта скоса

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

Для этого мы хотим захватить частицы метабалла в новой цели рендеринга. Затем мы нарисуем эту цель рендеринга один раз для каждого оттенка.

Во-первых, объявите новый RenderTarget2D так же, как и для метабаков:

Затем, вместо того, чтобы рисовать MetaballsTarget непосредственно в буферный буфер, мы хотим нарисовать его на particleTarget. Для этого перейдите к методу, где мы рисуем метабаллы и просто меняем эти строки:

... чтобы:

Затем используйте следующий код, чтобы рисовать частицы три раза с разными оттенками и смещениями:


Вывод

Это для базовой 2D воды. Для демонстрации я добавил камень, который вы можете сбросить в воду. Я рисую воду с некоторой прозрачностью на вершине скалы, чтобы она выглядела так, будто она под водой, и замедляет ее, когда она под водой из-за сопротивления воды.

Чтобы сделать демонстрационный пример немного приятнее, я пошел на opengameart.org и нашел изображение для скалы и фона неба. Вы можете найти скалу и небо по адресу http://opengameart.org/content/rocks и opengameart.org/content/sky-backdrop соответственно.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.