Як і раніше, ми збережемо професійну структуру для цієї версії, щоб усе було організовано та послідовно.
Давайте поринемо у створення 2D гри! Але спершу розпочнемо з прототипування. Я почну з створення об'єкта куба і назву його Player, із суцільним чорним фоном, щоб задати сцену.
Наступним кроком ми налаштуємо співвідношення сторін на 16:9, щоб наша гра відображалась однаково на різних розмірах екранів і роздільних здатностях.
Використовуючи C#, я призначу скрипт для нашого об'єкта гравця і розміщу його в координатах (0, 0, 0) для центру початкової точки.
Звертаючись до офіційної документації Unity, ми дізналися, як використовувати властивість transform. У цьому прикладі я створив новий Vector3, щоб наш гравець рухався вліво.
Для реалізації реального руху ми використовуємо Vector3.[direction] * [speed] * Time.deltaTime. Це гарантує, що об'єкт рухається з постійною швидкістю в метрах на секунду, незалежно від варіацій кадрової частоти.
Використовуючи функцію Unity Input.GetAxis, ми можемо створити змінні для горизонтального та вертикального вводу, які дозволяють гравцеві керувати рухом за допомогою стрілок або WASD. Горизонтальна ось рухає гравця вліво і вправо, а вертикальна — вгору та вниз.
Ось більш чистий і оптимізований спосіб написати той самий код руху, як і раніше.
Тепер ми встановили межі для нашого гравця, з функціональністю, яка дозволяє обертатись по екрану по осі X, коли гравець виходить за межі екрану.
Порада професіонала: 🛡️ Mathf.Clamp утримує значення в межах визначеного діапазону, запобігаючи помилкам виходу за межі та підтримуючи контрольований рух.
Порада професіонала: ✏️ Написання псевдокоду та чистого коду покращує читабельність, полегшує налагодження та допомагає створювати масштабовані функції.
Наступним кроком ми додамо лазер для нашого гравця! Я створив папку Prefabs для зберігання багаторазових ігрових об'єктів і призначив матеріал Laser_mat, щоб дати лазеру унікальний вигляд.
Тепер давайте спавнимо наш лазер, інстанціюючи його щоразу, коли гравець натискає пробіл!
Я додав напрямок руху до нашого лазера, щоб він рухався вгору з реальною швидкістю, використовуючи функцію:
transform.Translate(Vector3.up * _speed * Time.deltaTime);
Швидкість встановлена змінною:
private float _speed = 8.0f;
Наступним кроком я застосую зсув до інстанційованого лазерного променя, щоб вирівняти його позицію, так щоб він стріляв з правильного кута, а не зі сторін.
Я реалізував швидкість стрільби та охолодження для лазера мого гравця, використовуючи Time.time, щоб контролювати, коли лазер можна випустити.
Я не можу швидко натискати пробіл, щоб стріляти кількома лазерами через інтервал охолодження.
Я додав ворога до моєї гри з подібною поведінкою руху, як у гравця.
Ворог постійно з'являється знову вгорі екрану, коли досягає певної точки по осі Y.
Використовуючи Random.Range, ворог тепер з'являється в випадковій позиції по осі X, додаючи більше різноманітності до його патерну руху.
Порада професіонала: 📚 Перегляд офіційної документації є корисним способом знайти приклади синтаксису та краще зрозуміти, як реалізувати нові функції!
https://docs.unity3d.com/2019.4/Documentation/ScriptReference/Random.Range.html
Використовуючи OnTriggerEnter та Debug.Log(), ми тепер можемо виявляти, коли та з чим ворог стикається!
👀 Перевірте лівий нижній кут, щоб побачити результати зіткнень, що відображаються в консолі! 🖥️🔍
"Я натрапив на проблему, коли мої вороги взагалі не з'являлися…
Проблема виникла, коли я додав ще одного ворога, і вони з'являлися в тій самій позиції, стикаючись один з одним і ефективно знищуючи себе.
Давайте перемістимо ворогів і знову запустимо гру, щоб побачити зміни в дії.
“У програмному забезпеченні найпростіше рішення майже завжди є правильним.” — C.A.R. Hoare
Я додав метод GetComponent, щоб виявити, коли ворог стикається з гравцем і викликати функцію Damage() на об'єкті гравця. Коли ворог стикається з об'єктом, позначеним як 'Player', скрипт намагається отримати компонент Player з об'єкта. Якщо компонент успішно отримано (тобто player != null), викликається функція Damage(), і ворог знищується після нанесення шкоди.
Я реалізував систему життів, де гравець починає з 3 життів. Кожного разу, коли гравець отримує удар, функція Damage() зменшує кількість життів на 1. Коли життєвий запас гравця досягає 0, гравець вважається "мертвим" і видаляється зі сцени гри.
В інспекторі Unity ми можемо побачити, як кількість життів гравця зменшується на 1 кожного разу, коли його вражає ворог.
Після того, як гравець втрачає останнє життя, об'єкт гравця видаляється зі сцени, і його деталі зникають з інспектора.
У цій реалізації я створив Spawn_Manager, який використовує корутину для спавну ворогів у випадкових позиціях по осі X кожні 5 секунд. Корутіна запускає нескінченний цикл, інстанціюючи префаб ворога і роблячи паузу на певний період за допомогою yield return new WaitForSeconds(5.0f). Такий підхід дозволяє процесу спавну працювати незалежно, не блокуючи головний ігровий цикл.
Перекладено з: Day 5: 2D Game Development: Prototyping