Мережа для ігор зі стратегіями в реальному часі


16

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

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

Зараз я роблю тонких клієнтів, які просто надсилають пакети на сервер і чекають відповіді. Однак є кілька проблем.

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

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

TL; DR робить RTS з> 50 подіями в секунду, як синхронізувати клієнтів?


Ви можете, можливо, реалізувати те, що робить Єва-онлайн, і "уповільнити" час, щоб все дозволило нормально обробити.
Райан Ерб

3
Ось обов’язкове посилання на модель клієнта / сервера планетарного знищення: forrestthewoods.ghost.io/… Це альтернатива моделі блокування кроків, яка, здається, працює для них дуже добре.
DallonF

Подумайте про зменшення кількості подій, надіславши одне оновлення для всіх знятих плиток замість оновлень для кожної плитки або, як відповів Ілмарі, шляхом децентралізації дій не гравців.
Ліліенталь

Відповіді:


12

Ваша мета - синхронізувати 50 подій в секунду в режимі реального часу, мені здається, що це нереально. Ось чому підхід із блокувальним кроком, про який розповідають у статті про 1500 лучників, - це ж, про що говорили!

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


6

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

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

Індивідуальні ігри в реальному часі мають інші переваги:

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

Ви також згадали, що у вас виникають проблеми, коли відбувається> 50 подій або є затримки до секунд. Це набагато менше за масштабом, ніж сценарій, описаний у 1500 лучників , тому подивіться, чи зможете ви профілювати гру та дізнатися, де знаходиться уповільнення.


1
+1: Кадр - це правильний вибір, а не час. Звичайно, ви можете спробувати зберегти N кадрів в секунду. Невеликий зачіп краще, ніж повноцінне десинхронізація.
ПатрікБ

@PatrickB: Я бачу, що в багатьох іграх використовується "імітований" час, який не прив'язаний до відеокадрів. World of Warcraft оновлює лише такі речі, як мана кожні 100 мс, а фортеця Карликів за замовчуванням до 10 кліщів на кадр відео.
Mooing Duck

@Mooing Duck: Мій коментар був специфічним для RTS. Щось таке, де невеликі помилки можуть бути допущені та виправлені пізніше (наприклад, MMORPG, FPS), то використання безперервних значень є не лише точним, але й критичним. Однак детерміновані імітації, які мають бути синхронізовані на кількох машинах? Дотримуйтесь рамки.
ПатрікБ

4

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

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

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


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

  • Зробіть свою гру внутрішньо покроково, з кожним ходом або "галочкою", займаючи, скажімо, 1/50 секунд. (Насправді, ви, ймовірно, можете піти на 1/10 секунди або більше.) Будь-які дії гравця, що відбуваються під час одного ходу, слід розглядати як одночасні. Усі повідомлення, принаймні від сервера до клієнтів, мають позначатись номером повороту, щоб кожен клієнт знав, на яку чергу відбувається кожна подія.

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

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

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

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


1
Крім того, синхронізуйте RNG для запуску і витягніть з синхронізованої RNG лише тоді, коли сервер вам скаже. Starcraft1 тривалий час мав помилку, коли насіння RNG не зберігалось під час повторів, тому повтори повільно відхиляться від реальних ігор.
Mooing Duck

1
@MooingDuck: Добре. Насправді я б запропонував передавати поточне насіння RNG на кожному кроці, щоб негайно виявити десинхронізацію RNG. Крім того, якщо ваш код інтерфейсу потребує будь-якої випадковості, не перетягуйте його з того самого екземпляра RNG, який використовується для логіки гри.
Ільмарі Каронен

3

Ви повинні зробити свою логіку гри абсолютно незалежною від реального часу і по суті зробити її покроковою. Таким чином ви точно знаєте чергу, за якою відбувається "зміна енергії плитки". У вашому випадку кожен поворот становить лише 1/50 секунди.

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


1

Перш за все, ви повинні зрозуміти, що плаваюча / подвійна математика на ПК НЕ є детермінованою, якщо ви не вкажете строго використовувати IEEE-754 для свого обчислення (буде повільним)

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

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

Клієнти матимуть 2 "світу": один синхронізується з кінцевою чергою, інший обчислюється починаючи з кінцевої черги, додаючи дії, що надійшли в чергу, до поточного повороту / часової позначки клієнта.

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

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

Я використовую цей алгоритм у грі в режимі реального часу, і він працює чудово (з клієнтом і без того, щоб клієнт чатував власну дію, але з низьким пінг-сервером як 20/50 мс), також кожен сервер X кінцевого повороту надсилає спеціальне "все пакет клієнтської карти ", щоб виправити перенесені значення.


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

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