Скріншот з моєї гри
“Наскільки важко створити гру?”
Будучи занадто вільним і нудьгуючи під час канікул, я шукав щось, чим можна зайняти час. Нажаль, перша думка, яка прийшла мені в голову, була — створити 3D гру-стрілялку в космосі. Хоч я й маю певний досвід у програмуванні та базовому геймдеві, це було набагато складніше, ніж я уявляв. Оскільки гру я ще не завершив, вирішив задокументувати свій прогрес.
Що саме я хотів зробити?
Документ дизайну гри
Зазвичай, починаючи роботу над грою, розробники створюють документ дизайну гри, щоб чітко сформулювати свої ідеї та мати фіксований обсяг робіт. Це допомагає визначити напрямок гри і запобігає появі непотрібних функцій. Документ дизайну може містити багато речей, але найважливішими є: сюжет, естетика, основні механіки гри та ігровий процес.
Сюжет
Для своєї гри я вирішив почати з сюжету, оскільки це допомогло б мені визначити естетику ігри та її геймплей. У короткому викладі, моя гра розгортається в дистопійній галактиці, де гравець повинен скинути своїх правителів, щоб здобути свободу. Коли загальний сюжет був визначений, мені стало простіше розвинути більше деталей і придумати ігрові механіки.
Ігровий процес
Ідея полягала в тому, щоб гравець міг завойовувати планети, якщо вони не належать іншому гравцеві, або боротися з правителем планети за її володіння. Оволодіваючи планетою, можна отримувати великі ресурси залежно від типу планети. Наприклад, безлюдні планети багаті на чисті елементи, а планети з цивілізацією можуть забезпечити продукти та оброблені ресурси. Проте, Галактичний уряд (ваші правителі) намагатиметься зупинити ваш підйом, відправляючи бойові кораблі для вашого знищення.
Естетика
Спочатку я хотів зробити гру схожою на Freelancer, але без пікселізації. Однак під час розробки я натрапив на No Man’s Sky, що спричинило у мене фазу захоплення стилем та елементами цієї гри. Я намагався зробити свою гру схожою на No Man’s Sky. На щастя, я зрозумів, що не хочу і не можу створити No Man’s Sky 2. Потім я натрапив на Astroneer...
Якби я не зупинився і не обговорив естетику з другом, можу запевнити вас, що я досі вагався б із вибором. Зрештою, я зупинився на стилі з тонами тіней і тільки суцільними кольорами.
Назва?
Навіть після того, як я питав у ШІ та використовував генератори випадкових імен, я все ще не зміг придумати хорошу назву для гри. Найкращим варіантом на той момент було дати грі тимчасову назву, яка відображала б суть гри та можливо була б пов’язана з її сюжетом. Після довгих роздумів я обрав таку назву: Pew Pew.
Вибір інструментів для розробки
Тепер, коли всі ідеї організовані, можна перейти до вибору ігрового рушія та інших інструментів для розробки.
Ігровий рушій
Хоча я маю досвід роботи з Unity, я вирішив використовувати Godot для цієї гри. Оскільки я планую створити гру з простими графіками, мені не потрібні складні рендерингові пайплайни. Це означає, що я можу використати легкий рушій, такий як Godot, який займає лише близько півгігабайта. Це дозволить з часом мати менш захламлений проект, що, на мою думку, важливо для того, щоб не зійти з розуму під час розробки.
Дебатовано, але більш важливим є те, що Godot може працювати на моєму "картопляному" MacBook з i5 8-го покоління, 8 ГБ LPDDR3 оперативної пам'яті та без графічної карти.
По-друге, спільнота розробників Godot не настільки маленька порівняно з Unity. Насправді, значна частина розробників Godot прийшла з Unity. Це означало, що мені не буде важко вивчити Godot, маючи досвід з Unity.
Мова програмування
У мене було три варіанти для Godot: C#, GDScript або обидва. Я вирішив використовувати GDScript для більшої частини коду через його чудову інтеграцію з Godot та простоту. Якщо мені коли-небудь знадобиться покращити продуктивність, я можу переключитись на використання C# для конкретного скрипта.
Початок розробки
Політ
Тепер, коли я налаштував проект Godot для своїх потреб, настав час почати реальну розробку, повільне, виснажливе мучення.
Оскільки я створював гру про космічні кораблі, варто було почати проект з контролера польоту. Проте є маленька проблема: я не торкався до розробки ігор останні 3 роки, і я вперше використовую Godot і GDScript. Іншими словами, я не мав жодного уявлення, що робити. Тому логічно було б знайти туторіал Godot, як зробити 3D 6-осевий контролер польоту, що імітує керування космічним кораблем, правильно? НІ. Я адаптував код з Unity туторіалу на YouTube від gamesplusjames.
Код введення
Я знаю, я знаю, що GDScript використовує snakecase за умовчанням, але, чесно кажучи, мені все одно._
Перше, що треба було зробити — отримати ввід від користувача. Найпростіше — це стандартні клавіші WASD, які потрібні для руху. Також я хотів мати можливість виконувати ролл по локальній осі z. Це фактично означає, що я можу обертатись навколо своєї осі, як у Star Wars. І, нарешті, мені потрібна була клавіша для активації прискорення.
Код руху
Після цього, очевидно, потрібно було рухати космічний корабель відповідно до вводу. Я почав з перевірки, чи натиснута клавіша прискорення. Якщо так, і корабель рухається, я збільшував FOV для ефекту пришвидшення. Використовуючи CharacterBody від Godot, я міг встановити швидкість за допомогою Vector3, яку обчислював на основі введених клавіш та множника прискорення. Остаточні значення інтерполювались. Для руху корабля я використовував функцію move_and_slide()
, успадковану від CharacterBody3D
в Godot.
Код руху миші
Оскільки контролер корабля керувався мишею для обертання, я повинен був відобразити рух миші на фактичне обертання корабля відповідно.
Для цього я витратив значно більше часу, ніж слід було б, щоб зрозуміти, як правильно оновлювати обертання. В кінці все, що мені потрібно було зробити, це дві строки коду, які я помістив у функцію _process()
, що оновлюється на кожному кадрі. Для людей, знайомих з Unity, це аналогічно функції Update()
. Ці дві строки використовують позицію миші (змінні x і y) та змінну роллу для створення представлення обертання у вигляді Vector3, потім перетворюють його на Quaternion
, а потім конвертують цей Quaternion
у тип даних Basis
, який використовується для оновлення transform.basis
, що є обертанням.
Код слідування камери
Слідування камери. Одна з найважливіших функцій у будь-якій грі від третьої особи, як у моїй. І, як не дивно, вона також є надзвичайно заплутаною для реалізації. Після 2 надзвичайно коротких днів я зрозумів, як це зробити. Для цього потрібно налаштувати ноду Camera як дочірню ноду для батьківської ноди типу Node3D
.
Батьківська нода повинна мати увімкнену опцію top-level, що означає, що нода буде використовувати глобальні трансформації, а не трансформації, відносні до гравця.
Це означає, що я можу інтерполювати трансформацію батьківської ноди, щоб вона не була точною копією трансформації гравця. Оскільки сама нода камери є дочірньою, я можу відносно позиції гравця відсунути камеру для досягнення бажаного ефекту.
Тепер ви, мабуть, запитаєте мене: чому б просто не використати метод .translate()
на об'єкті трансформації перед інтерполяцією? Це була моя перша інтуїція. Однак на практиці це порушило обертання камери, і мені не залишалося нічого, окрім як використовувати цей метод.
Процедурна галактика
Нарешті, у мене були правильні контролі, рух і слідування камери. Тепер я міг перейти до наступної функції. Наступним логічним кроком було б реалізувати базову механіку стрільби, але замість цього я витратив чимало часу на створення процедурної галактики.
Тепер слова «процедурний» та «галактика» звучать страшно, коли йдеться про програмування, але якось саме програмування цієї системи стало найпростішим етапом у всьому процесі розробки.
На відміну від реальних галактик, які містять понад мільярди зоряних систем, моя галактика має лише близько 100 зоряних систем для спрощеного ігрового процесу. Очевидно, що відстані між цими зоряними системами не такі, як в реальній галактиці, де середня відстань складає 5 світлових років середня відстань 5 світлових років. Натомість у моїй процедурно згенерованій галактиці відстані між зоряними системами складають від 50 000 М до 1 000 000 М.
Процедурна генерація починається з того, що я випадковим чином генерую 100 зірок в межах сфери з малим довільним радіусом, скажімо, 300 000 000 М. Потім навколо зірки генеруються планети зі своїми орбітальними відстанями, які обертаються навколо зірки, створюючи зоряну систему. Різні конфігурації таких зоряних систем можуть бути створені та розміщені з належною відстанню одна від одної. Voilà, ви тепер маєте свою власну просту згенеровану галактику.
Спавнінг небесних тіл
Небесне тіло в моїй грі може бути або зіркою, або планетою, згодом, ймовірно, з'являться інші типи, як астероїди. На даний момент планети та зірки це, по суті, різні за розміром та кольором сфери з текстурами або ефектами світіння. Вони генеруються за допомогою шума від насіння, що означає, що один і той самий шум може бути згенерований при використанні одного й того ж насіння для генерації цього шуму. Це працює схоже на Minecraft для користувачів. Шум насіння використовується для визначення розміру, типу та глобальної позиції небесного тіла. Після того як параметри призначені для екземпляра об'єкта небесного тіла, він стає дочірнім елементом певної ноди, щоб з'явитись у грі.
Зоряні системи
Тепер, коли я можу спавнити зірки, потрібно додати планети до зірок. Я можу використовувати ту ж техніку спавнінгу небесних тіл, щоб створити планети навколо зірки. Однак через моментум (і деякі математичні символи, які я ще не розумію), планети зорі зазвичай вирівняні на одній площині, як і в нашій сонячній системі. Оскільки моя гра була абсолютно реалістичною (і не тому, що я шукав виправдання для спрощення орбіт планет), я вирішив спавнити планети на одній площині.
Коли зірка спавниться, планети знову спавняються на основі шуму від насіння і розташовуються відповідно до їхнього радіусу. Чим більша планета, тим більша відстань між нею та сусідніми небесними тілами. Наприклад, якщо найближча планета до зірки велика, відстань між зіркою та цією великою планетою буде більшою, ніж якщо б це була менша планета. Це не імітує жодну реальну поведінку, насправді орбіта залежить від гравітаційних сил через масу, а не від об'єму.
Орбіти планет
Оскільки всі планети спавняються на одній осі, тепер мені потрібно просто рухати планети відносно зірки. Коли я думав про найпростіший спосіб зробити це, я придумав схоже рішення на слідування камери. Батьківська нода Node3D
з тією ж позицією, що і у зірки, буде батьком для фактичної ноди небесного тіла. Потім батьківська нода буде обертатись по осі Y, оскільки планети спавняються на осі Z.
Це дозволить планетам мати ідеально сферичні орбіти навколо зірки.
Однак, знову ж таки, це не є реалістичним, оскільки це гра. В реальному житті орбіти майже ніколи не є повністю сферичними, і орбіти майже ніколи не є ідеально стабільними. Це означає, що якщо б я надав кожному з тіл маси і використав F = G * (m1 * m2) / r² для симуляції гравітації та орбіт, це було б непрактично, оскільки орбіти в моїй грі, через менший масштаб, можуть тривати значно менше, ніж в реальному житті. Більше того, навіть якщо я дійсно розрахую точну швидкість, необхідну для ідеальної орбіти, і симулюю це, в моєму випадку це не дасть ніякої різниці, окрім як погіршить продуктивність. Сподіваюся, це достатньо для виправдання моїх підозрілих методів.
Спавнінг зоряних систем
Тепер, коли я маю процедурно згенеровані зоряні системи, останнім кроком є їх спавнінг. Ця частина дуже схожа на те, як працює спавнінг планет, коли зірка вже спавнена. Сонячні системи спавняються в межах сфери з довільним радіусом, при цьому відстані розраховуються залежно від розміру всієї сонячної системи. Функція in_another_body()
, в цьому скрипті перевіряє, чи достатньо велика відстань між зоряними системами, що спавняються, відповідно до моїх параметрів. Це використовується для того, щоб спавнити зоряну систему в іншому місці, де вона буде достатньо далеко від інших зоряних систем.
Код справді сповільнюється, коли потрібно спавнити кілька сотень зоряних систем. Він також намагатиметься спавнити одну зоряну систему безкінечно, якщо відстань, необхідна для спавнінгу зоряних систем з заданими відстанями, більша за простір у межах сфери, в якій ці зоряні системи можуть бути спавнені. Однак, на даний момент це підходить, оскільки він все одно може згенерувати невелику галактику з 100 зірок досить швидко, навіть на моєму комп'ютері.
Висновок
І ось я ледь покрив половину того, що я зробив на канікулах для цієї гри. В наступному блозі я продовжу писати про інші функції, які я додав за цей час. Тим часом ви можете перевірити мій репозиторій на GitHub.
Перекладено з: Pew Pew