Гра HellWorm. Історія розробки

Доброго часу доби! Я хотів би розповісти про свій досвід створення мобільної гри на Unity під назвою HellWorm. З назви можна зрозуміти, що гра про черв'яка. Повзаємо, їмо монетки, не врезаемся в перешкоди. Здавалося б, клон класичної гри, на якій більшість з нас виросли. Але, насправді, паралель зі змійкою на цьому закінчується.


Гра позиціонує себе як нескінченний світ, в якому йде постійний рух вперед, без можливості згорнути з вертикального маршруту. А сам черв'як, при всьому при цьому, може як завгодно звиватися (так-так, і навіть проповзати через себе). Внаслідок чого, хотілося б загострити увагу на труднощі, які я випробував намагаючись реалізувати рух такого нехитрого персонажа.

Управління досить просте і описується лише однією фразою:
«Куди клацнете, туди і повзе».


Перша реалізація алгоритму руху наочно продемонстровано на схемі нижче.


Суть руху така: зі швидкістю n зростає довжина першого сегменту (головного) і з тією ж швидкістю зменшується довжина останнього (хвостового), а в момент натискання на екран створюється новий сегмент. З'єднання йде по центральним лініях прямокутників. А на стиках, щоб згладити розрізи, розміщені окружності радіусом рівним ширині сегмента. Проте цей спосіб, на жаль, не підійшов, т. к. візуальний стиль гри не має на увазі використання закруглених кутів. Довелося винаходити велосипед далі, тому прошу ознайомитися з наступною реалізацією.


Стиковка відбувається не за центральним лініях, а по кутах сегментів: за лівим або правим, де сторона вибирається в залежності від того, по яку сторону від прямої AB лежить нова точка P.

(Bx — Ax) * (Py — Ay) — (By — Ay) * (Px — Ax), якщо вираз >0 то точка P лежить ліворуч.
Як знайти координату точки стикування я розписувати не буду, тому що нічого особливого в цьому немає, проста геометрія. Краще детальніше розповім про сам підхід.

Хочу відразу застерегти, що в даному випадку максимальний кут між сегментами повинен бути не більше 90 градусів, інакше, як можна здогадатися, кути вилізуть назовні. Тому під час крутих (>90 градусів) розворотів відбувається додавання перехідного сегмента.


Коли гравець натискає на екран, то запам'ятовується кут, на який черв'як повинен повернути і як тільки головний сегмент досягає допустимої довжини, то створюється новий сегмент під заданим кутом, за умови, що він не більше 90 градусів, у іншому випадку створюється перехідний сегмент, а коли він досягає заданої довжини, то відбувається остаточний поворот. Дана стратегія дозволяє уникнути хаотичних викривлень при дуже швидкому натисканні по екрану, а також робить рух більш природним. Хотілося б зауважити, візуальних затримок при управлінні зовсім не відчувається.

На цьому експерименти закінчилися і продовжилася розробка гри: додані різні види перешкод, генерація рівня, магазин зі скінами, бонусний монетний режим при підборі черепка, зміна кольору оточення в залежності від пройденого шляху і т. д. За цей час погляд пристойно «замилилося» і я просто не помічав осічки в своєму алгоритмі руху черв'яка. А саме, невеликих ривків при зміні напрямку руху. Як виявилося, вся справа в стикуванні сегментів по крайніх точках.


При створенні нового сегмента даним способом немає можливості з'єднати центри сторін сегментів(червону і синю точки на малюнку). А оскільки голова хробака завжди прив'язана до центральної точки першого сегменту (а хвіст прив'язаний до центру краю останнього), то відбувається ривок при повороті, і чим більше кут розвороту, тим сильніше помітний ривок. Звичайно, на завершительных етапах розробки стикатися з подібним дуже прикро, адже потрібно переробляти весь алгоритм. Але прагнення закінчити проект дошлифованным і без косяків взяло верх. Було вирішено повернутися до першої реалізації алгоритму, т. к. вести розрахунок щодо ламаної суцільної лінії, навколо якої будується тіло, набагато простіше і логічніше. Залишилося вирішити проблему зі стиками прямокутників, а саме, яким чином їх заповнити.


Трохи поміркувавши, прийшла ідея продовження сегментів назустріч один одному для усунення зазору.


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

  • визначаємо в яку сторону дивиться новий сегмент відносно попереднього (в ліву або в праву)
  • знаходимо кутові точки B, C і точки на іншому кінці сегментів B', C'
  • знаходимо точку перетину прямих A BB' CC':

    Ax = ((Bx * B'c y — By * B'c x) * (Cx — C 'x) — (Bx — B'c x) * (Cx * C' y — Cy * C 'x)) / ((Bx — B'c x) * (Cy — C' y) — (By — B'c y) * (Cx — C ' x))

    Ay = ((Bx * B'c y — By * B'c x) * (Cy — C 'y) — (By — B'c y) * (Cx * C' y — Cy * C 'x)) / ((Bx — B'c x) * (Cy — C' y) — (By — B'c y) * (Cx — C ' x))
  • подовжуємо сегменти на довжини відрізків BA і CA відповідно
В цілому, на цьому розробка алгоритму для руху черв'яка була успішно завершена. Окремо хотілося б зауважити, що цей алгоритм можна використовувати для створення хробака або змії з округленими (реалістичними) краями при вигинах. Для цього досить задати максимальний кут розвороту близько 10-15 градусів, а також зменшити допустиму довжину сегмента для розвороту. Результат зображено нижче:


До того ж, кілька слів хотілося б розповісти про генерації навколишнього світу. Всі об'єкти збережені в sprite sheet'и в чорно-білому вигляді. Білим кольором зафарбовані рамки, які змінюють забарвлення під час гри, тому що змінюючи колір у Sprite Renderer всі білі ділянки стають саме вибраного кольору.


(бічні стінки з доданим Polygon Collider 2D, і фонове зображення)

Та ж техніка використовується для ігрових перешкод і головного персонажа.


На скріншоті з перешкодами можна помітити сині і жовті ромбики, це місця можливої появи монетки (жовті) або черепка (сині). Вони є порожніми gameobject з обраної іконкою.


Я вибрав такий підхід, щоб, по-перше, спростити завдання по додаванню монет/черепів на сцену (не морочитися з рандомно генерацією) і, по-друге, щоб вони з'являлися в цікавих для геймплея місцях. А, по-кільки, в грі близько 70 різних тунелів, скель і проходів, то для гравця не так помітні зумовлені місця.

Розповім ще пару слів про хробака будь ласка, а саме його голові. У гравця є можливість її змінювати (як і інші частини тіл), і, в тому числі, колір шкіри і очей.


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


Є prefab голови worm_head, в якому знаходяться всі голови head+n, що складаються з об'єктів head (спрайт голови білого кольору) і eyes (спрайт з очима, розташований поверх голови, який також білого кольору). За замовчуванням, всі об'єкти head+n невидимі. Під час старту гри відбувається перевірка номери встановленої голови, щоб зробити об'єкт head+n видимим. Разом з цим застосовується обрана гравцем колірна схема: основний колір задається для спрайту голови, тіла, хвоста, а додаткові кольори задаються для очей і шипів.

head.GetComponent<SpriteRenderer>().color = headColor;
eyes.GetComponent<SpriteRenderer>().color = eyesColor;

На мій погляд, про найцікавіші і не очевидні моменти розробки я розповів, але якщо у когось є питання, буду радий на них відповісти!

Дякую за увагу. Пограти в гру можна в Google Play.

p.s. Паблишера у гри немає, намагаємося розкрутитися своїми силами.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.