Керівництво по моделюванню 2d водної поверхні

Керівництво по моделюванню 2d водної поверхні

У цій статті ми розглянемо створення динамічної 2D води з найпростішої фізикою. Ми будемо використовувати рендер ліній, мешів. тригери і частки. Кінцевий результат з хвилями і бризками ви зможете додати в якусь свою гру. Я виклав те, що вийшло у мене на Unity (Unity 3D), але, використовуючи принципи з даної статті, ви зможете зробити те ж саме на будь-якому движку.

Отже, приступимо

Ми будемо створювати верхній контур води, використовуючи рендер ліній і багато вузлів, щоб це виглядало, як хвилі.

Нам потрібно буде зберігати позиції, швидкості і прискорення для кожного вузла, так що ми будемо використовувати масиви:

За допомогою LineRenderer ми створимо кордон нашої води, але нам все ще потрібна сама вода. Для цього ми будемо використовувати меши. Для їх зберігання нам також потрібен буде масив GameObject:

Ще нам потрібно буде відстежувати зіткнення з поверхнею води:

Тепер кілька констант:

Тут springconstant - «коефіцієнт жорсткості» наших хвиль (вони будуть рухатися подібно пружинному маятнику); damping - коефіцієнт гасіння (інакше хвиля, одного разу почавши гойдатися, не зупиниться ніколи); spread - коефіцієнт, який відповідає за швидкість поширення хвиль. Ви можете грати з цими значеннями, намагаючись досягти властивостей, найбільш нагадують справжню воду. Значення z (координату води по осі Z) ви можете змінювати в залежності від того, що повинно стояти ближче до переднього плану в вашому додатку.

Далі, нам потрібно зберігати розташування нашої води:

... І її зовнішній вигляд:

Об'єкт, в якому всі ці дані будуть зберігатися - щось на зразок менеджера, він же буде створювати воду. Створимо для цього функцію - SpawnWater (). У неї ми будемо передавати координату лівого краю води, ширину водного простору, а також його верхню і нижню межі:

створюємо вузли

Порахуємо кількість вузлів, яке нам потрібно:

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

Тепер саме час налаштувати наш LineRenderer:

Після инициализируем змінні, які ми створювали вище:

... І заповнимо їх реальними значеннями:

В кінці цієї ділянки коду, як ви бачите, ми поміщаємо кожен вузол з LineRenderer на його місце.

Створюємо саму воду

Тут починається найцікавіше. Зараз у нас є межа води, але самої води немає. Тому зараз ми створимо для неї меши:

Тепер в меш потрібно передати кілька значень. Для початку - координати його кутів.

Керівництво по моделюванню 2d водної поверхні

На діаграмі відзначені потрібні нам кути першого заважав. У загальному вигляді це буде виглядати так:

Як ви могли помітити, в нульовий осередку зберігається верхня ліва точка, в першій - верхня права, в другій - нижня ліва, а в третій - нижня права. Тепер нам потрібно вибрати, яку частину текстури ми хочемо використовувати для цього заважав. Ми хочемо використовувати всю, а значить нам потрібно просто написати:

Меши повинні складатися з трикутників. На щастя, будь-який чотирикутник можна розділити на два трикутника.

Керівництво по моделюванню 2d водної поверхні

Як ви можете бачити, трикутник A складається з вершин 0. 1. 3. а трикутник B - з вершин 3. 2. 0. Ці шість чисел нам потрібно записати в окремий масив, в цьому ж порядку:

Тепер три створених нами масиву потрібно передати в меш:

Тепер у нас є меши, але немає GameObject -ів, щоб їх рендерить. Виправимо це:

Всі ці меши тепер є нащадками менеджера.

Налаштування детекторів зіткнень

Тут ми створюємо кілька BoxCollider -ів, призначаємо їм імена і робимо їх нащадками менеджера. Також ми призначаємо їм позиції - посередині між вузлами, визначаємо їх розмір і додаємо їм WaterDetector.

Ну і наступна функція буде оновлювати позиції наших вузлів:

Додамо трохи фізики

Щоб знаходити прискорення, швидкості і нові позиції вузлів, нам нам знадобляться закон Гука і метод Ейлера.

Отже, закон Гука говорить, що F = -k * x. де F - сила, з якою пружина прагне повернутися в початковий стан (не забувайте, що наші хвилі будуть складатися з безлічі маленьких пружин); k - коефіцієнт жорсткості (пам'ятаєте, ми записували його в константах?); x - розтягнення. За цією формулою ми будемо вираховувати прискорення для вузлів, значення розтягування буде вираховуватися як різниця між поточною позицією води і її базовим рівнем. Ще до сили ми будемо додавати швидкість, помножену на коефіцієнт гасіння - щоб хвилі не були нескінченними.

Як бачите, метод Ейлера дуже простий - ми просто кожен раз додаємо до позиції вузла його швидкість, а до швидкості додаємо прискорення. Зрозуміло, для точних обчислень більше б підійшло інтегрування Верье. але через використання гасіння це насилу представляється можливим. До того ж метод Ейлера на порядок простіше.

Тепер додамо трохи поширення хвиль. Створимо два масиви, в яких ми будемо зберігати різницю між висотами сусідніх вузлів, помножену на коефіцієнт поширення:

... І заповнимо їх, попутно змінюючи швидкості і висоти:

Як ви помітили, вище ми здійснюємо дії 8 разів. Це зроблено для більшої плавності.

Додамо зіткнення

Тепер у нас є вода, яка може не просто відображатися, але і переливатися. Тепер нам потрібна можливість її сколихнути!

Для цього створимо метод Splash (). який буде перевіряти місце і швидкість удару по воді:

Для початку перевіримо коректність переданої координати по Іксу:

Тепер зробимо так, щоб зберігати не абсолютна координата, а відстань від першого вузла:

Тепер потрібно обчислити, на який вузол доводиться удар:

Тут ми робимо буквально наступне:

  • Ми беремо координату щодо першого вузла - xpos.
  • Ділимо її на різницю між позицією останнього і першого вузла.
  • Таким чином ми отримуємо дробове число, яке говорить нам, куди саме припав удар.
  • Множимо отримане число на кількість кутів і округляємо до цілого.

Тепер прирівнюємо швидкість знайденого вузла до швидкості удару:

Ви можете вчинити інакше, наприклад, додати швидкість удару до поточної швидкості вузла або дейсвовать відповідно до закону збереження імпульсу.

Тепер додамо бризки:

Ви можете здивуватися тому, що я призначив стартову швидкість двічі. Причина у вбудованій системі частинок. яка призначає початкову швидкість як випадкове число між двома константами. На жаль, у нас немає особливого доступу до неї через скрипти, тому доводиться діяти так.

Далі я додав одну лінію, але ви можете її пропустити:

Бризки НЕ знищаться при зіткненні з іншими об'єктами, тому вам варто або призначити їм Z координату фону (у мене - 5), або зробити так, щоб частинки завжди потрапляли назад на воду. Я вибрав щось середнє:

Начебто все, так?

виявлення зіткнень

Ні, не так! Нам потрібно відстежувати зіткнення об'єктів з водою, інакше все це було написано дарма. Пам'ятайте клас WaterDetector. який ми згадували вище? Зараз ми займемося саме їм. Все, що нам потрібно від нього - лише один метод:

Тепер вам залишилося лише викликати SpawnWater () де-небудь у вашому коді і насолоджуватися прекрасним хвилями і бризками!

Керівництво по моделюванню 2d водної поверхні