Як буде реалізована система знімків стану ігор для мережевих ігор у режимі реального часу?


12

Я хочу створити просту багатокористувацьку гру в реальному часі клієнт-сервер як проект для мого мережевого класу.

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

Я хочу зробити щось подібне до моделі мережі Quake 3: в основному, сервер зберігає знімок всього стану гри; отримуючи вхід від клієнтів, сервер створює новий знімок, що відображає зміни. Потім він обчислює різниці між новим знімком та останнім та надсилає їх клієнтам, щоб вони могли синхронізуватися.

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

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

  • Всі дані відокремлені від логіки
  • Відмінності можна обчислити між знімками ігрових станів
  • Ігровими об'єктами все ще легко управляти за допомогою коду

Як реалізується клас знімків ? Як зберігаються сутності та їх дані? Чи має кожен суб'єкт клієнта ідентифікатор, який відповідає ідентифікатору на сервері?

Як обчислюються різниці знімків?

Загалом: як би була реалізована система знімків стану гри?


4
+1. Це трохи занадто широко для одного запитання, але ІМО - це цікава тема, яку можна грубо висвітлити у відповіді.
Кромстер

Чому б вам просто не зберегти 1 Знімок (власне світ), зберегти всі вхідні зміни у цей звичайний світовий стан І зберегти зміни у списку чи щось таке. Тоді, коли прийде час надсилати зміни всім клієнтам, просто надсилайте вміст списку всім і очищайте список, починайте з нуля (зміни). Можливо, це не так добре, як зберігати 2 знімки, але при такому підході вам не потрібно турбуватися про алгоритми про те, як швидко розрізнити 2 знімки.
tkausl

Ви читали це: fabiensanglard.net/quake3/network.php - огляд мережевої моделі землетрусу 3 включає дискусію щодо реалізації.
Стівен

Яку гру намагаються побудувати? Налаштування мережі сильно залежить від типу гри, яку ви робите. RTS не поводиться як FPS з точки зору роботи в мережі.
AturSams

Відповіді:


3

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

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

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

Цей підхід може бути простішим у здійсненні, особливо за допомогою рефлексії (якщо у вас така розкіш), але може бути повільним, навіть якщо ви сильно узаконюєте частину рефлексії (побудувавши свою схему даних для кешування більшості дзвінків на роздуми). Головним чином тому, що вам потрібно порівняти дві копії потенційно великого стану. Звичайно, це залежить від того, як ви реалізуєте порівняння та свою мову. Це може бути швидко на C ++ за допомогою жорсткого кодування порівняльника, але не настільки гнучко: будь-яка зміна вашої глобальної структури держави вимагає модифікації цього компаратора, і ці зміни настільки часті на початкових етапах проекту.

Ще один підхід - використовувати брудні прапори. Щоразу, коли клієнт надходить, ви застосовуєте його до вашої єдиної копії глобального стану та відповідних полів, як брудні. Тоді, коли настав час синхронізувати клієнтів, ви серіалізуєте брудні поля (рекурсивно), використовуючи ті ж унікальні ідентифікатори. (Незначний) недолік полягає в тому, що іноді ви надсилаєте більше даних, ніж суворо потрібно: наприклад, int field1спочатку було 0, потім було призначено 1 (і позначено брудно), а після цього присвоєно 0 знову (але залишається брудним). Перевага полягає в тому, що маючи величезну ієрархічну структуру даних, вам не потрібно її повністю аналізувати, щоб обчислити дельту, лише брудні шляхи.

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


2

Спочатку потрібно знати, як представити відповідні дані у відповідності з протоколом. Це залежить від даних, що стосуються гри. Я буду використовувати приклад гри РТС.

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

Гравці повинні мати відповідні їм дані (наприклад, всі видимі одиниці):

  • Живі чи мертві?
  • Якого типу вони?
  • Скільки здоров’я їм залишилось?
  • Поточне положення, обертання, швидкість (швидкість + напрямок), шлях у найближчому майбутньому ...
  • Діяльність: Напади, ходьба, будівництво, закріплення, загоєння тощо ...
  • ефекти статусу баф / налагодження
  • і, можливо, інші статистичні дані, такі як мана, щити та що ні?

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

Кожна одиниця має цілий ідентифікатор. Атрибути перераховуються, тому вони також мають цілісні ідентифікатори. У ідентифікаторів одиниць не повинно бути 32 біта (це може бути, якщо ми не є ощадливими). Це може бути 20 біт (атрибути залишаються 10 біт). Ідентифікатор одиниць повинен бути унікальним, він може бути дуже присвоєний лічильником, коли пристрій інстанціюється та / або додається до ігрового світу (будівлі та ресурси вважаються нерухомими одиницями, а ресурсам може бути призначений ідентифікатор, коли карта завантажується).

Сервер зберігає поточний глобальний стан. Кожен останній оновлений стан гравця представлений вказівником на listостанні зміни (усі зміни після того, як покажчик ще не був надісланий цьому гравцю). Зміни додаються до тих listвипадків, коли вони відбуваються. Після того, як сервер завершить відправлення останнього оновлення, він може почати перебирати список: сервер переміщує вказівник гравця по списку до хвоста, збираючи всі зміни по дорозі і поміщаючи їх у буфер, який буде надісланий програвач (тобто формат протоколу може бути приблизно таким: unit_id; attr_id; new_value) Нові одиниці також вважаються змінами і надсилаються з усіма своїми значеннями атрибутів гравцям, що приймають.

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

Деякі питання, які ви не ставили, і, я думаю, цікаві, це:

  1. Чи повинні клієнти отримувати знімок з усіма даними в першу чергу? А що з предметами поза їх лінією зору? Що з туманом війни в іграх RTS? Якщо ви надсилаєте всі дані, клієнт може бути зламаний для відображення даних, які не повинні бути доступні гравцю (залежно від інших заходів безпеки, які ви вживаєте). Якщо ви надсилаєте лише відповідні дані, проблема вирішується.
  2. Коли важливо надсилати зміни, а не надсилати всю інформацію? Враховуючи пропускну здатність, доступну на сучасних машинах, чи отримуємо ми що-небудь від надсилання «дельти» замість надсилання всієї інформації, якщо так, коли?
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.