Відокремлення ігрового двигуна від ігрового коду в подібних іграх з версією версій


15

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

Я хотів би, щоб основний код був розроблений окремо від гри, так що якщо скажу, що я виправляю помилку, знайдену в грі А, виправлення буде присутній у грі B.

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

  • Створіть engineмодуль / папку / все, що містить усе, що можна узагальнити і на 100% незалежне від решти гри. Сюди входить деякий код, а також загальні активи, якими ділиться між іграми.
  • Помістіть цей двигун у власне gitсховище, яке буде включено в ігри якgit submodule

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

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

У когось є якісь ідеї, досвід, щоб поділитися чи що-небудь з цього приводу?


На якій мові працює ваш двигун? Деякі мови мають спеціалізовані менеджери пакунків, які можуть мати більше сенсу, ніж використання підмодулів git. Наприклад, NodeJS має npm (який може орієнтуватися на Git repos як джерела).
Ден комора

Чи є ваше питання про те, як найкраще налаштувати керувати загальним кодом або як налаштувати керувати «напівгенеричним» кодом чи як архітектуру коду, як створити код чи що?
Данк

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

Як мати чисту історію однієї папки в одній гілці: Переробляйте двигун так, щоб окремі (майбутні) репо були в окремих папках, це ваше останнє зобов’язання. Потім створіть нову гілку, видаліть усе, що знаходиться поза цією папкою, і виконайте виконання. Потім перейдіть до першого зобов’язання РЕПО і з’єднайте це зі своєю новою філією. Тепер у вас є відділення лише з цією папкою: перетягніть його в інші проекти та / або об'єднайте його з існуючим проектом. Це мені дуже допомогло з розділенням двигунів за галузями, якщо ваш код уже відокремлений. Мені не потрібні модулі git.
Баррі Стейс

Відповіді:


13

Створіть модуль / папку двигуна / все, що містить усе, що можна узагальнити і на 100% незалежне від решти гри. Це включає деякий код, а також загальні активи, які поділяються між іграми.

Помістіть цей двигун у власне сховище git, яке буде включено в ігри як підмодуль git

Це саме те, що я роблю, і це дуже добре працює. У мене є програма застосування та бібліотека візуалізації, і кожна з них трактується як підмодулі моїх проектів. Я вважаю, що SourceTree корисний, коли мова йде про підмодулі, оскільки він добре управляє ними і не дасть вам нічого забути, наприклад, якщо ви оновили підмодуль двигуна в проекті A, він сповістить вас про те, щоб змінити проект B.

При досвіді з'являється знання про те, який код повинен бути в двигуні та який повинен бути проект. Я пропоную, якщо ви навіть злегка не впевнені, що ви дотримуєтеся цього в кожному проекті. З плином часу ви побачите серед різних проектів те, що залишається тим самим, і тоді ви зможете поступово враховувати це у свій код двигуна. Іншими словами: дублюйте код до тих пір, поки ви не будете майже на 100% впевнені, що він не змінюється дискретно за проектом, а потім узагальнюйте його.

Примітка про контроль над джерелами та бінарні файли

Просто пам’ятайте, що якщо ви очікуєте, що ваші бінарні активи часто змінюватимуться, ви, можливо, не захочете розміщувати їх у текстовому керуванні джерелами, як git. Скажіть просто ... Є кращі рішення для бінарних файлів. Найпростіша річ, яку ви можете зробити зараз, щоб зберегти ваше сховище "джерело двигуна" чистим та ефективним, - це мати окреме сховище "бінарні файли двигуна", яке містить лише бінарні файли, які ви також включаєте як підмодуль у свій проект. Таким чином ви пом'якшуєте пошкодження продуктивності, зроблені вашим сховищем "джерело двигуна", яке постійно змінюється і на якому вам, таким чином, потрібні швидкі ітерації: виконувати, натискати, витягувати і т.д. Системи управління джерелами, такі як git, працюють на текстових дельтах , і як тільки ви вводите двійкові файли, ви вводите масивні дельти з текстової точки зору - що, в кінцевому рахунку, коштує вам часу на розробку.GitLab Додаток . Ваш друг Google.


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

@Malharhak Відредаговано, щоб відповісти на ваш коментар.
Інженер

@Malharak Ось трохи цікавої інформації на цю тему.
Інженер

1
+1 для збереження речей у проекті якомога довше. Загальний код дає більш високу складність. Слід уникати, поки це абсолютно не потрібно.
Гусдор

1
@Malharhak Ні, тим більше, що ваша мета полягає лише у збереженні "копій" до тих пір, поки ви не зауважите, що зазначений код є незмінним і його можна вважати загальним. Гусдор повторив це - будьте попереджені - можна легко витрачати купу часу, фактуруючи речі занадто рано, потім намагаючись знайти способи зберегти цей код загальним, щоб залишатися загальним, але при цьому достатньо адаптованим, щоб підходити до різних проектів ... ви закінчуєте цілий ряд параметрів і комутаторів, і це перетворюється на потворний безлад, який все ще не те, що вам потрібно, тому що ви все одно змінюєте його на новий проект . Не розраховуйте занадто рано . Мати терпіння.
Інженер

6

У якийсь момент двигун ОБОВ'ЯЗКОВА спеціалізуватися та знати речі про гру. Я зійду тут по дотичній.

Візьміть ресурси в RTS. Одна гра може мати Creditsі Crystalіншу, MetalіPotatoes

Ви повинні правильно використовувати поняття OO і переходити до макс. повторне використання коду. Зрозуміло, що Resourceтут існує концепція .

Тож ми вирішуємо, що ресурси мають таке:

  1. Гачок в основній петлі для збільшення / зменшення
  2. Спосіб отримання поточної суми (повертає int)
  3. Спосіб віднімання / додавання довільно (гравці, що передають ресурси, покупки ....)

Зауважте, що це поняття Resourceможе означати вбивства або очки в грі! Це не дуже потужно.

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

Скажімо, ви додаєте InstantResourceклас подібними методами. Ви зараз (починаєте) забруднювати двигун ресурсами.


Проблема

Давайте знову візьмемо приклад РТС. Припустимо, гравець все, що пожертвує Crystalіншим гравцем. Ви хочете зробити щось на кшталт:

if(transfer.target == engine.getPlayerId()) {
    engine.hud.addIncoming("You got "+transfer.quantity+" of "+
        engine.resourceDictionary.getNameOf(transfer.resourceId)+
        " from "+engine.getPlayer(transfer.source).name);
}
engine.getPlayer(transfer.target).getResourceById(transfer.resourceId).add(transfer.quantity)
engine.getPlayer(transfer.source).getResourceById(transfer.resourceId).add(-transfer.quantity)

Однак це справді досить безладно. Це загальне призначення, але безладно. Хоча це нав'язує, resourceDictionaryщо означає, що тепер ваші ресурси мають мати імена! І це на кожного гравця, тому ви більше не можете мати ресурси команди.

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

engine.getPlayer(transfer.target).crystal().receiveDonation(transfer)
engine.getPlayer(transfer.source).crystal().sendDonation(transfer)

З класом Playerі класом , CurrentPlayerде CurrentPlayer«S crystalоб'єкт буде автоматично показувати матеріал на ІЛС для передачі / відправки пожертвувань.

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


Заключні зауваження

Справа з ресурсом не є блискучою. Я сподіваюся, що ви все ще можете побачити сенс. Якщо що-небудь я продемонстрував, що "ресурси не належать до двигуна", як те, що потрібно конкретній грі, і те, що стосується всіх понять про ресурси, це ДУЖЕ різні речі. Зазвичай ви знайдете 3 (або 4) "шари"

  1. "Основне" - це визначення підручника в двигуні, це графік сцени з гачками подій, він стосується шейдерів та мережевих пакетів та абстрактне поняття про гравців
  2. "GameCore" - Це досить загальне для типу гри, але не для всіх ігор - наприклад, ресурси в RTS або боєприпаси в FPS. Логіка гри тут починає проникати. Саме тут було б наше раніше уявлення про ресурси. Ми додали ці речі, які мають сенс для більшості ресурсів RTS.
  3. "GameLogic" ДУЖЕ специфічний для фактичної гри, що робиться. Ви знайдете змінні з іменами, як creatureабо shipабо squad. Використання успадкування ви отримаєте класи , які охоплюють всі 3 шари (наприклад , Crystal це Resource який є GameLoopEventListener скаже)
  4. "Активи" - це марно для будь-якої іншої гри. Візьмемо для прикладу комбіновані AI-скрипти в періоді напіввиведення 2, вони не збираються використовувати в RTS з тим же двигуном.

Створення нової гри зі старого двигуна

Це ДУЖЕ поширене. Етап 1 - це видобуток 3 та 4 шарів (і 2, якщо гра ВЕЛИКОБІЛЬКО іншого типу) Припустимо, ми робимо RTS зі старої RTS. У нас ще є ресурси, тільки не кришталь і інше - тому базові класи в шарах 2 і 1 все ще мають сенс, все те, про яке кристал посилається в 3 і 4, можна відкинути. Так ми і робимо. Однак ми можемо перевірити це як орієнтир того, що ми хочемо зробити.


Забруднення в шарі 1

Це може статися. Абстракція та продуктивність - вороги. Наприклад, UE4 забезпечує безліч оптимізованих випадків композиції (тому, якщо ви хочете, щоб X і Y хтось написав код, який робить X і Y разом дуже швидко - він знає, що це робить і те й інше), і як результат, дійсно досить великий. Це не погано, але це забирає багато часу. Шар 1 визначає такі речі, як "як ви передаєте дані в шейдери" та як анімувати речі. ЗРОБИТИ це найкращим чином для вашого проекту, ЗАВЖДИ добре. Просто спробуйте і плануйте майбутнє, повторно використовуючи код - ваш друг, успадкуйте там, де це має сенс.


Класифікація шарів

ОСТАННЯ (обіцяю) не бійтеся шарів. Двигун - це архаїчний термін із старих часів трубопроводів з фіксованою функцією, де двигуни в значній мірі працювали так само графічно (і в результаті мали багато спільного) програмований трубопровід повернув це на голову і як такий "шар 1" став забрудненим. будь-яких ефектів розробники хотіли досягти. AI був відмітною особливістю (через безліч підходів) двигунів, тепер це AI та графіка.

Ваш код не повинен бути поданий у цих шарах. Навіть у відомого двигуна Unreal є МНОГО різних версій, кожна специфічна для іншої гри. Мало файлів (окрім подібних структур даних), які б не змінилися. Це добре! Якщо ви хочете створити нову гру з іншої, це займе більше 30 хвилин. Ключ - спланувати, знати, які біти копіювати та вставляти, а що залишити.


1

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

Є три речі, які (майже) завжди присутні на сцені меню: фон, логотип гри та саме меню. Зазвичай ці речі залежать від гри. Що ви можете зробити для цього вмісту, це створити у своєму двигуні MenuScreenGenerator, який приймає 3 параметри об'єкта: BackGround, логотип та меню. Основна структура цих 3 частин також є частиною вашого двигуна, але ваш двигун насправді не говорить про те, як ці частини генеруються, лише які параметри ви повинні їм надати.

Потім у своєму фактичному ігровому коді ви створюєте об’єкти для BackGround, логотипу та меню, і передаєте це у свій MenuScreenGenerator. Знову ж, сама гра не справляється з тим, як створюється меню, це для двигуна. Вашій грі потрібно лише розповісти двигуну, як він повинен виглядати і де він повинен бути.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.