Прогноз руху для нестріляних


35

Я працюю над ізометричною 2D грою з середньомасштабним багатокористувацьким гравцем, приблизно 20-30 гравців, підключених одночасно до стійкого сервера. У мене виникли певні труднощі з можливістю гарного виконання прогнозування руху.

Фізика / Рух

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

У головному циклі гри перед оновленням називається "Оновити". Логіка оновлення викликає "завдання оновлення фізики", яке відстежує всі сутності з ненульовою швидкістю, використовуючи дуже базову інтеграцію для зміни положення сутності. Наприклад: сущность.Position + = суть.Velocity.Scale (ElapsedTime.Seconds) (де "Seconds" - це значення з плаваючою комою, але той самий підхід буде працювати і для цілих значень мілісекунд).

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

Пакет змін і оновлення штату

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

Оскільки оновлення швидкості та напряму трапляються набагато рідше в моїй грі, я можу втекти, надсилаючи кожну зміну стану. Хоча всі фізичні симуляції відбуваються з однаковою швидкістю і детерміновані, затримка все ще залишається проблемою. З цієї причини я надсилаю звичайні пакети оновлення позицій (подібні до 3D-гри), але набагато рідше - зараз кожні 250 мс, але я підозрюю, що при хорошому передбаченні я можу легко збільшити його до 500 мс. Найбільша проблема полягає в тому, що я зараз відхилився від норми - вся інша документація, путівники та зразки в Інтернеті надсилають звичайні оновлення та інтерполювати між двома державами. Це здається несумісним з моєю архітектурою, і мені потрібно придумати кращий алгоритм прогнозування руху, який ближче до (дуже базової) архітектури "мережевої фізики".

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

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

Прогнозування та компенсація відставання

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

Проблеми

Я боровся з різними алгоритмами і маю ряд питань і проблем:

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

  2. Коли пакет приходить, я намагався інтерполювати позицію пакета зі швидкістю пакета протягом певного періоду часу (скажімо, 200 мс). Потім я приймаю різницю між інтерпольованою позицією та поточною позицією "помилка", щоб обчислити новий вектор, і розміщую його на сутності замість швидкості, що була надіслана. Однак припущення полягає в тому, що інший пакет прийде в цей часовий проміжок, і "неймовірно важко" здогадатися ", коли наступний пакет надійде, тим більше, що всі вони не надходять через фіксований інтервал (тобто / зміни станів). Чи принципово хибна концепція, чи вона правильна, але потребує певних виправлень / коригувань?

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

  4. Що повинно статися, коли суб'єкти стикаються? Існує три сценарії - контрольний гравець зіштовхується локально, два об'єкти стикаються на сервері під час оновлення позиції або віддалене оновлення сутички стикається на локальному клієнті. У всіх випадках я не впевнений, як вчинити зіткнення - окрім обману, обидва стани є "правильними", але в різні періоди часу. У випадку з віддаленою сутністю не має сенсу малювати його, проходячи через стіну, тому я виконую виявлення зіткнень на локальному клієнті і змушую його "зупинитися". Виходячи з пункту №2 вище, я можу обчислити "виправлений вектор", який постійно намагається перемістити об'єкт "крізь стіну", який ніколи не матиме успіху - віддалений аватар застрягає там, поки помилка не стане занадто високою, і вона "забивається" в положення. Як обробляють ігри навколо цього?


1
Що стосується гри 3D або 2D із тим, який сервер ви використовуєте? і чому не сприятливий сервер працює для вашої гри?
AttackingHobo

1
@ Рой Т. Пропускна здатність. Пропускна здатність - це найцінніший ресурс у сучасних комп'ютерних системах.
FxIII

1
Це просто неправда, в онлайн-іграх значною мірою переважає час відповіді, наприклад, на лінії 10 Мбіт (1,25 МБ / с) затримка між сервером-клієнтом становить 20 мс, відправка пакету 1,25 КБ займе 20 мс + 1 мс. Відправлення пакету 12,5 кб займе 30 мс. У двократно швидкій лінії пакет 1,25 кб все ще займе 20 мс + 0,5 мс і 20 мс + 5 мс для пакета 12.кб. Затримка є обмежуючим фактором, а не пропускною здатністю. Так чи інакше, я не знаю, скільки даних є, але надсилання 50 вектор3 (25x позиція + 25x обертання) складає всього 600 байт, надсилання цього кожні 20 мс коштуватиме 30 кбіт / с. (+ пакет накладних витрат).
Рой Т.

2
Quake двигун передбачив з першої версії. Прогноз землетрусу описаний там і в деяких інших місцях. Перевір.
користувач712092

1
Ви робите це положення + = швидкість * deltatime для кожної сутності паралельно (обов'язково: у коді У вас є 2 масиви фізичних параметрів сутностей, один кадр один від одного, ви оновлюєте старіший, щоб він був новішим, і міняєте їх)? Є деякі проблеми з ітерацією Шона Барета, який створив базу двигуна Злодій 1 .
користувач712092

Відповіді:


3

Єдине, що потрібно сказати, це те, що 2D, ізометричні, 3D, вони всі однакові, коли мова йде про цю проблему. Оскільки ви бачите багато прикладів 3D, і ви використовуєте лише вхідну систему з обмеженою 2-октантовою швидкістю з миттєвою швидкістю, не означає, що ви можете викинути принципи мережевих зв'язків, що розвивалися протягом останніх 20+ років.

Принципи дизайну прокляті, коли гра порушена!

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

Вищезазначене є великою причиною того, чому сервери, здається, надсилають багато інформації про стан, а не контролюють входи. Ще одна велика причина базується на тому, який протокол ви використовуєте. UDP з прийнятою втратою пакету та доставкою замовлення? TCP із гарантованою доставкою та повторними спробами? За допомогою будь-якого протоколу ви збираєтеся отримувати пакети в дивні часи, затримуватися або збиратися один на одного під час шквалу діяльності. Усі ці дивні пакети повинні відповідати контексту, щоб клієнт міг зрозуміти, що відбувається.

Нарешті, навіть якщо ваші вклади обмежені 8 напрямками, фактична зміна може статися в будь-який момент - примусовий цикл в 250 м просто зірве швидких гравців. 30 гравців - це нічого великого для будь-якого сервера. Якщо ви говорите про тисячі ... навіть тоді їх групи розбиті на кілька боксерів, тому окремі сервери несуть лише розумне навантаження.

Ви коли-небудь профілювали двигун фізики, як працює Хавок або Куля? Вони справді досить оптимізовані і дуже, дуже швидкі. Ви можете потрапити в пастку припускати, що операція ABC буде повільною і оптимізує щось, що їй не потрібно.


Тут визначені мудреці-поради! Легко втратити зір велику картину. Я використовую TCP в цьому випадку. Питання "8 напрямків" - це не стільки проблема, що стосується вхідних даних - це більше проблема інтерполяції та екстраполяції. Графіка обмежена тими кутами і використовує анімовані спрайти - ігровий процес "виглядає дивно", якщо гравець рухається під іншим кутом або швидкістю, що занадто далеко від норми.
ShadowChaser

1

Отже, ваш сервер по суті є "арбітром"? У цьому випадку я вважаю, що все у вашому клієнті має бути детермінованим; вам потрібно переконатися, що все на кожному клієнті завжди дасть однаковий результат.

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

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

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

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


Дякую - це звучить досить близько до того, що мені потрібно, особливо на стороні сервера. Одним цікавим моментом є те, що хоча гравці заблоковані у 8 напрямках, внутрішній рух - це 3D-вектор. Я задумався про це трохи більше за минулу добу, і я думаю, що я борюся з тим, що у мене взагалі не реалізована інтерполяція - я просто використовую дуже базову інтеграцію, встановлюючи швидкість і оновлюючи позицію на основі векторне кожне оновлення.
ShadowChaser

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

Ви впевнені, що спочатку інтегруєте правильну позицію при t до t + 1, перш ніж інтегрувати неправильну позицію в потрібну правильну позицію при t + 1?
Джонатан Коннелл

Так - я ще раз перевірив, чи використовував я правильну позицію для вихідної інтеграції. Спочатку це була помилка, але її виправлення все ще не здавалося помітним покращенням. Моя підозра є "+1" - це має бути дуже залежним від часу між пакетами. Є дві проблеми: надсилати зміни стану на додаток до регулярних (250 мс) оновлень, і я не можу передбачити, коли вони відбудуться. Крім того, я не хочу замикатися на певний інтервал, оскільки має сенс для сервера надсилати менше оновлень для осіб, які знаходяться далі від програвача. Час між пакетами може змінюватися.
ShadowChaser

1
Так, мабуть, фіксований вид часового кроку - це, мабуть, не дуже гарна ідея. Я хоч переживаю, що нерівномірність руху в 8 напрямках буде дуже складно (якщо не неможливо?) Передбачити. Незважаючи на це, ви можете спробувати використати середню затримку клієнта для прогнозування t + 1 і мати поріг, вище якого ви завжди «телепортуєте» інших гравців на нові позиції.
Джонатан Коннелл

0

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

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


-1

Першими моїми запитаннями були б: Що не так із використанням моделі, коли сервер має повноваження? Чому не важливо, чи середовище 2D або 3D? Це значно полегшило б ваш захист від читів, якби ваш сервер був авторитетним.

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

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

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

Навіщо потрібно інтерполювати? Авторитетний сервер повинен перекривати будь-які помилкові рухи.

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

Це ситуації, коли між сервером і клієнтом виник конфлікт, і тому вам потрібно підтримувати стани на клієнті, щоб сервер міг виправити будь-які помилки.

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


Кілька відповідей: * Якщо сервер має повноваження, він несе відповідальність за відстеження всіх рухомих об'єктів та оновлення їх позицій на регулярному інтервалі. Іншими словами, йому потрібно запустити двигун фізики - що може дорого коштувати. Масштабованість є головною метою проектування шахти. * Мені потрібно інтерполювати на стороні клієнта, інакше будь-яке розумне оновлення кожного сервера, надіслане клієнтам, призведе до стрибків сутностей. Зараз моя інтерполяція робиться в двигуні фізики - вона просто встановлює швидкість. Немає штатів чи дельт.
ShadowChaser

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

1
-1. Те, що ви написали, лише невиразно стосується теми; відчуває себе неоднозначно. Відповіді вважаються підпунктними, коли вони, по суті, "читають цю довгу статтю", не містять корисної інформації з цієї статті.
AttackingHobo

1
@AttackingHobo Я повинен був би погодитися з вами. Я згадав, що поспішав, але це не привід. Якби я не встиг, було б краще залишити його в спокої. Заняття.
Gyan aka Gary Buyn
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.