Найти тему
ZDG

Игра Pengu5: Симуляция волн

Оглавление

Как я сообщал ранее, прикупил клавиши с пингвинами, чтобы закончить игру про пингвина. Сейчас этими клавишами и пишу :)

Игра называется Pengu5, потому что Pengu это сокращённо Penguin, а 5 это ремейк Flash-игры на HTML5.

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

Итак, ключевая особенность игры это плавание льдины по волнам.

-2

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

Как сделать волну?

Берём обычную синусоиду:

-3

У неё есть амплитуда – это высота волны. И частота – это, соответственно, величина, обратная длине волны, а длина волны зависит от скорости распространения. Собственно, это всё, вот она, волна :)

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

-4

Далее, нужно заставить волны двигаться. Нам достаточно просто в каждый следующий момент времени рисовать синусоиду с каким-то смещением.

Вот теперь точно всё. Реально, это несложно сделать, и нужно только тщательно подобрать параметры по вкусу: сколько синусоид использовать, с какими амплитудами, частотами и сдвигами.

Практическая реализация

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

Так как вода это частицы, я хочу оттолкнуться от частиц. Где-то я читал, что при движении волны частицы воды движутся по эллиптическим траекториям. Попробую сделать по памяти.

Моделировать каждую частицу не надо. Представим поверхность воды как сетку точек с неким шагом.

-5

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

-6

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

-7

Теперь вернёмся к частоте. Частота в данном случае это количество оборотов, совершаемых частицей за единицу времени. Обороты измеряются в радианах, и 360° это 2𝜋. Назовём частоту frequency.

Сосредоточимся только на вертикальной координате частицы (height). Она будет равна амплитуде (назовём её amplitude), умноженной на косинус текущего угла поворота частицы в момент времени t:

height = amplitude * cos(2𝜋 * frequency * t)

Если применить такой расчёт к каждой частице и пустить время t в цикле, то частицы будут плавно подниматься и опускаться, но делать это cинхронно, поэтому мы увидим колебание не волны, а горизонтальной линии. Чтобы получилась волна, нужно добавить скорость её распространения в пространстве (velocity).

В пространственном измерении каждая следующая частица находится в другом моменте времени и значит её нужно дополнительно подкручивать относительно предыдущей. Величина подкрутки это скорость распространения волны (velocity) и измеряется она также в радианах. С пространственной координатой x полная формула выглядит так:

height = amplitude * cos(2𝜋 * frequency * t + 2𝜋 * velocity * x)

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

Теперь фазы частиц будут разными, и мы получим что-то вроде:

-8

Думаю, идею вы ухватили.

Первую синусоиду я делаю как бы "несущей", поэтому у неё низкая частота, скорость и большая длина волны:

-9

Затем накладываю на неё более мелкую и быструю.

-10

...и вижу, что уже хватит. Данная волна уже вполне устраивает.

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

-11

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

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

dx = amplitude * sin(2𝜋 * frequency * t + 2𝜋 * velocity * x)

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

Смотреть на это лучше всего в динамике, поэтому вот ссылка на онлайн-редактор:

Online Javascript Editor

Вы можете поменять параметры вот здесь и посмотреть, как они влияют:

-12

Обратите внимание, что frequencies и velocities сразу умножены на 2𝜋, чтобы не делать этого каждый раз в формуле.