Кодування різних станів у пригодницьких іграх


12

Я планую пригодницьку гру, і не можу зрозуміти, який правильний спосіб реалізувати поведінку рівня в залежності від стану прогресії історії.

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

Я наївно думав використати оператор switch {} спочатку, щоб вирішити, що NPC повинен сказати або на що він може бути знайдений, і зробити цілі квесту взаємодіючими лише після перевірки стану глобальної змінної game_state. Але я зрозумів, що швидко натрапляю на безліч різних ігрових станів і випадків переключення, щоб змінити поведінку об'єкта. Цю операцію перемикання також важко буде налагодити, і я думаю, це також може бути важко використовувати в редакторі рівнів.

Тому я подумав, що замість того, щоб мати один об'єкт з декількома станами, можливо, у мене повинно бути кілька екземплярів одного і того ж об'єкта, з одним станом. Таким чином, якщо я використовую щось на зразок редактора рівня, я можу поставити екземпляр NPC у всіх місцях, де він коли-небудь може з’являтися, а також екземпляр для кожного стану розмови, який він має. Але це означає, що буде багато неактивних, невидимих ​​ігрових об’єктів, що плавають навколо рівня, що може спричинити проблеми з пам'яттю або просто важко побачити в редакторі рівнів, я не знаю.

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

Усі мої методи відчувають себе настільки неефективно, тому, щоб скласти своє запитання, чи є кращий або стандартизований спосіб реалізувати поведінку рівня залежно від стану розвитку історії?

PS: У мене ще немає редактора рівнів - я думаю про те, щоб використовувати щось на зразок JME SDK або зробити свій власний.

Відповіді:


9

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

Один приклад того, як виглядатиме інтерфейс стану (повністю складений - просто для ілюстрації рівня контролю, який дає вам ця схема):

interface NPCState {
    Scene whereAmI(NPC o);
    String saySomething(NPC o);
}

І два заняття:

class Busy implements NPCState {
    Scene whereAmI(NPC o) {
        return o.getWorkScene();
    }
    String saySomething(NPC o) {
        return "Can't talk now, I'm busy!";
    }
}

class Available implements NPCState {
    Scene whereAmI(NPC o) {
        return TAVERN;
    }
    String saySomething(NPC o) {
        String[] choices = o.getRandomChat();
        return choices[RANDOM.getInt(choices.length)];
    }
}

І перемикання станів:

// The time of day passed from "afternoon" to "evening"
NPCState available = new Available();
for ( NPC o : list ) {
    Scene oldScene = o.state.whereAmI(o);
    o.state = available;
    Scene newScene = o.state.whereAmI(o);
    moveGameObject(o, oldScene, newScene);
    ...

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

Це добре працює і з редакторами рівнів: ви можете мати простий комбінований вікно, щоб переключити "глобальний" стан рівня, а потім додати і розмістити ігрові об'єкти так, як ви хочете, щоб вони відображалися в такому стані. Ігровий движок несе відповідальність за те, що фактично "додає" цей об'єкт на сцену, коли він має правильний стан - але його параметри все одно можна редагувати зручним для користувача способом.

(Відмова: У мене мало досвіду в реальному світі з редакторами ігор, тому я можу з упевненістю сказати про те, як працюють професійні редактори; але мій погляд на державний зразок все-таки дотримується: організація вашого коду таким чином повинна бути чистою, бездоганною і не втрачати систему ресурси.)


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

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

Отже, як я розумію, існують чітке відображення btwn NPCState та GameState. Потім я поставив NPC в масив і повторював його, призначаючи новий NPCState, коли спостерігається зміна стану гри. NPCState має бути здатним знати, як поводитися з кожним різним NPC, що надсилається до нього, тому по суті NPCState містить поведінку всіх NPC для даного стану? Мені подобається, що всі форми поведінки зберігаються в єдиному NPCState, який чітко відображає реалізацію редактора ігор, але це ніби робить NPCState досить величезним.
Карден

О, я думаю, що я неправильно зрозумів ур ан. Я трохи змінив це, щоб включити спостерігачів. Таким чином, це один розрізний NPCState для кожного різного NPC, за винятком супер родових, таких як Crowd NPC, який може поділяти стан. Для кожного ігрового стану NPC зареєструє себе та свою NPCState з спостерігачем. Отже, спостерігач точно знатиме, який NPC зареєстрований, щоб змінити поведінку в якому стані гри, і просто перегляне їх. А з боку редактора ігор, редактор ігор повинен просто передати Спостережувачу сигнал про зміну стану всього рівня.
Кардін

1
Так, це ідея! Важливі NPC матимуть багато штатів, і перехід держави залежатиме здебільшого від завершених етапів. Загальні NPC можуть також часом реагувати на основні етапи, і навіть вибраний стан залежить від внутрішньої властивості (скажімо, всі NPC мають початковий стан за замовчуванням, і коли ви вперше розмовляєте з одним із них, він представляється, тоді введіть нормальний цикл перемикання стану).
mgibsonbr

2

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

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

if (state in behaviors) {
  behaviors[state]();
}

Чи був би цей файл даних якоюсь скриптовою мовою? Я думаю, що простого текстового файлу даних може бути недостатньо для опису поведінки. У будь-якому випадку ви маєте рацію, що вона повинна динамічно завантажуватися. Я не можу зовсім подумати про використання редактора ігор для створення дійсного коду Java, його обов'язково потрібно дещо проаналізувати.
Карден

1
Ну, це було моєю початковою думкою, але, побачивши відповідь mgibsonbr, я зрозумів, що ви можете кодувати різні форми поведінки як окремі класи, а потім у файлі даних просто скажіть, які класи поведінки йдуть у якому стані. Завантажте ці дані в асоціативний масив під час виконання.
поштовх

О, так, це, безумовно, простіше! : D Порівняно зі сценарієм вбудовування чогось на кшталт Луа хаха ..
Кардін

2

Що з використанням шаблону спостерігача для пошуку важливих етапів змін? Якщо відбудеться зміна, деякий клас розпізнає це і обробить, наприклад, зміну, яка повинна бути виконана в npc.

Замість згаданої структури дизайну стану я використовував би стратегію.

Якщо npc має n способів взаємодії з персонажем та m позиціями, де він міг би бути, вам потрібно розробити максимум (m * n) +1 класів. Використовуючи шаблон стратегії, ви отримаєте n + m + 1 класи, але ці стратегії також можуть бути використані іншими npcs.

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


Шаблон спостерігача здається цікавим. Я думаю, що це могло б чисто залишити всі обов'язки перед НПК, щоб зареєструвати себе державним спостерігачем. Шаблон стратегії дуже схожий на тригер і поведінку AI поведінки Unity Engine, який використовується для обміну поведінкою між різними ігровими об'єктами (я думаю). Це звучить здійсненно. Я не впевнений, які плюси та мінуси зараз є, але те, що Unity використовує той самий метод, також дещо заспокоює ха-ха ..
Кардін

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

0

Усі наведені підходи є дійсними. Це залежить від ситуації, в якій ви знаходитесь у будь-який момент. Багато пригод або ММО використовують їх поєднання.

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

ОТО, якщо персонажі ходять по карті і роблять різні речі в різних місцях, у вас часто є один актор, який обертається через різні об'єкти поведінки (наприклад, ходіть прямо вперед / немає розмов проти стояти тут / розмова про смерть Мітча), що може включати "приховано", якщо їх мета виконана.

Однак, копії об'єкта, який ви створюєте вручну, не повинні викликати проблем. Скільки об’єктів ви можете створити? Якщо ви можете створити більше об'єктів, ніж ваша гра може зациклитися, подивіться на їх "приховане" властивість і пропустіть, ваш двигун занадто повільний. Тому я б не переживав про це занадто. Багато онлайн-ігри насправді роблять це. Певні символи або предмети завжди є, але не відображаються символам, які не мають відповідної місії.

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

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