Как синхронизировать часы в многопользовательской игре?

8

У меня есть 2-3 клиента, которые могут обмениваться сообщениями через Apple Game Center.

Единственная синхронизация, которая мне нужна, - это запустить игру одновременно.

Я предполагаю, что это включает синхронизацию часов. Как это сделать?

GameCoder
источник
3
Краткий ответ: «Вы не можете». Неизбежная (и непоследовательная) задержка в обмене данными между устройствами означает, что даже если одно устройство скажет: «Я считаю, что сейчас 12: 00: 00.00», тогда оно может легко быть где-то с 12: 00: 00.10 до 12:00:05 время, когда это сообщение получено в другом месте. В серверной модели вы не можете сделать намного лучше, чем просто заставить сервер отправлять клиентам сообщение «Начать игру» одновременно.
Стивен Стадницкий,
Хорошо, что мне не нужно совершенство. Как сделать это лучше, чем ничего? Возможно: каждый игрок отправляет сообщение «START» и запускается, как только он получает сообщение START от всех других игроков
GameCoder
1
@StevenStadnicki Сетевые задержки не делают это невозможным. Общая проблема называется «синхронизация часов», и она хорошо изучена. (Однако, если вы контролируете только конечные точки, то невозможно учесть асимметрию задержек.)
Praxeolitic
Возможно, я неправильно понимаю вашу проблему, но вы не можете просто сделать так, чтобы сообщение, отправляемое клиентам, указывало на время начала в будущем? Если время истекло, nowи вы отправляете сообщение для запуска точно в now + halfSecondто время, пока все они получают сообщение в течение полсекунды и при условии, что их системные часы правильно синхронизированы, они все начнутся одновременно.
Mark

Ответы:

12

Комментарий Стивена прав: теоретически это невозможно сделать.

К счастью, на практике вы можете приблизиться, как работает NTP .

Например, лучше, чем просто отправить сообщение 3 клиентам с надписью «начать сейчас», вы можете заранее обмениваться несколькими сообщениями ping, чтобы измерить время, необходимое для получения сообщения клиенту, и когда вы отправляете начальное сообщение, вместо «начать сейчас» произнесите «начать в Х миллисекундах» и настройте X на разное время, необходимое для получения сообщения.

например.:

  • Вы отправляете сообщение клиенту 1 и получаете ответ через 20 мс. Вы предполагаете, что для получения сообщения клиенту требуется примерно 10 мс.
  • Вы делаете то же самое для Клиента 2 и получаете ответ через 28 мс - поэтому время передачи там, вероятно, будет около 14 мс.
  • Таким образом, вы отправляете сообщение клиенту 1 со словами «начать игру через 50 мс» и отправляете одно сообщение клиенту 2 со словами «начать игру через 46 мс». Клиент 2 получит сообщение примерно через 4 мс, но будет ждать 4 мс меньше перед началом игры.

Это не может гарантировать синхронизацию, потому что время, необходимое для отправки сообщения через Интернет, варьируется, и потому что оно может быть различным в каждом направлении. Первое, что вы можете уменьшить, выполнив измерения несколько раз и взяв среднее значение. Вторая сложнее, и ее теоретически невозможно решить (хотя сейчас я не могу вспомнить доказательства). Хорошей новостью является то, что вам, вероятно, не нужна такая большая точность.

Kylotan
источник
Возможно, мне понадобится ограничить активность моих приложений во время синхронизации - сейчас я даю некоторое время ЦП сетевой подсистеме в каждом кадре рисования. Если частота кадров составляет 30 кадров в секунду, я все равно отвечу на пинг в 1/30 = 33 мс. Я также мог бы добавить 33 мс, или разницу между «временем приема стека TCP» и моим текущим MilisecondTicks () ..
GameCoder
2
Если вы надеетесь, что система будет синхронизирована по кадрам во время игры, я думаю, что это проигрышная игра, и вы должны сейчас сдаться. Часы и задержки будут дрейфовать, а информация всегда будет занимать время, чтобы путешествовать. Что бы вы ни пытались сделать, вам, вероятно, не нужен этот уровень синхронизации.
Kylotan
Я думаю, что мы упускаем суть. Меня беспокоит не совершенство, а пользовательский опыт. Это может быть хорошо или лучше, и я хотел бы лучше. Вот и все.
GameCoder
2
Хороший пользовательский опыт не требует точной синхронизации. Вот почему большинство игр не делают этого.
Kylotan
1
Теоретически это невозможно, если каждый игрок контролирует свою собственную летучую мышь, потому что для того, чтобы эта информация достигла другого компьютера, требуется ненулевое количество времени, в течение которого две стороны имеют различную информацию. Вместо этого вы найдете способы справиться с различиями. Ваша проблема на самом деле ничем не отличается от любой другой быстро развивающейся онлайн-игры, и на этом сайте есть еще несколько вопросов о том, как управлять сетью для них. Один здесь: gamedev.stackexchange.com/questions/22444/…
Kylotan
3

Как уже упоминалось, это невозможно, поэтому я бы попробовал другой подход:

Если у вас нет выделенного сервера, выберите одного участвующего клиента, чтобы стать хостом (это можно перенести, если возникнет такая необходимость).

Хозяин теперь будет выполнять всю важную игровую логику, такую ​​как обнаружение попаданий, управление ИИ, обработка инвентаря и т. Д., А также отслеживание времени (то есть диктование времени игры).

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

Большинство игр (особенно FPS) скрывают этот факт, выполняя свои собственные локальные вычисления для собственного движения игрока, выстрелов и т. Д., Чтобы избежать ощущения задержки игры. Все по-прежнему исправляется на основе данных сервера. Это может привести к некоторой путанице, например, вы видите, как стреляете по врагу, но в тот же момент вы падаете замертво (без удара врага), но это все же гораздо лучший подход, чем полная синхронизация.


Если вы по-прежнему настаиваете на синхронизации всего, возможно, вы захотите создать какой-то счетчик шагов или кадров, чтобы все клиенты обрабатывали только один логический шаг, затем синхронизировали свои данные и т. Д. Имейте в виду, что это может быть как пропускная способность интенсивный, а также медленный, поэтому я бы не советовал делать это, если у вас нет большого количества данных, иначе ваш игровой процесс основан на пошаговом режиме (например, игры в стиле Artillery / Worms).

Марио
источник
1

Я рекомендую синхронизировать системные таймеры на всех клиентах и ​​сервере с помощью протокола NTP [Stratum 2], затем сервер отправляет команду на запуск игры в указанное время, например, когда все таймеры достигают 0:05:00. Такой подход должен дать вам 3-4 мс точной синхронизации, я считаю.

ivan866
источник