Интерполяция позиций в многопользовательской игре

14

Чтобы сэкономить пропускную способность в моей многопользовательской игре , я не обновляю каждый объект каждый тик сервера, вместо этого у каждого объекта есть updateRate, который сообщает игре, что этот объект будет обновляться после каждого тика X-сервера.

Когда я получаю сообщение об обновлении для объекта, я рассчитываю время, которое я ожидаю следующего обновления:

origin = serverCurrentPosition
diff = serverNextPosition - origin
arriveTime = now + timeBetweenTicks * updateRate

Когда я рисую объект, я вычисляю время, оставшееся до следующего обновления, и интерполирую позицию соответственно:

step = 100 / timeBetweenTicks * updateRate
delta = 1 - step * ((arriveTime - now) / 100)
position = origin + diff * delta

Это работает ... но на рисунке все еще есть немного дрожания, хотя в моей теории все должно работать хорошо, так как масштабирование должно заботиться о некоторой задержке, не так ли?

Таким образом, вопрос здесь заключается в том, это лучший подход? Должен ли я поставить фактическое отставание в вычислениях? Если так, как бы я это сделал? Я провел несколько экспериментов, но дрожание только ухудшалось.

Иво Ветцель
источник
Привет, Иво. Я думаю, что это хорошая тема, но неясно, что делает ваш код - например, откуда взялись serverCurrentPosition, serverNextPosition, timeBetweenTicks?
CiscoIPPhone
Это отправить в обновлении данные, которые поступают с сервера.
Иво Ветцель

Ответы:

11

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

Таким образом, когда вы полагаетесь на получение обновления точно в указанное время, вы постоянно прибываете в пункт назначения до / после фактического обновления. Следовательно, дрожание.

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

Еще одна идея уменьшить джиттер в вашей настройке: поскольку вы передаете как «текущие», так и «следующие» координаты, вы можете рассчитать скорость объекта. Затем, когда обновление запаздывает, вы не останавливаете объект в месте его назначения (то есть в «следующей» позиции), а продолжаете перемещать его с той же скоростью. Если ваши объекты не изменяют свои скорости резко, это действительно улучшит плавность движения на клиенте.

Ничего
источник
Это уже так, что объекты не останавливаются, они продолжают двигаться, пока не получат следующее обновление. Кроме того, использование аппаратного ускорения на холсте HTML, по-видимому, немного уменьшает эффект дрожания. Может быть, я просто сошел с ума после долгой работы над этим.
Иво Ветцель
Это вполне возможно. Если при включении ускорения частота кадров увеличивается, то вероятность обработки информации об обновлении в нужный момент возрастает.
Nevermind
9

Я решил эту проблему прежде с некоторым успехом с подходом, который я называю "тенями сети". Я не знаю, делают ли это другие люди, но это всегда работает для меня.

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

Я включил много деталей об этом подходе в свой предыдущий ответ здесь

Мартин
источник
Хм, я делал что-то подобное в более ранней версии, неточность с плавающей запятой иногда делала это действительно плохим, у меня довольно большие промежутки времени между обновлениями, до 300 мс для некоторых объектов, но, может быть, я просто сделал это неправильно, я дам это выстрел, когда я нахожу немного свободного времени :)
Ivo Wetzel
Точность с плавающей запятой на самом деле вообще не должна быть здесь! Вы читали мой связанный ответ на stackoverflow? Он охватывает все детали реализации такого рода вещей.
Мартин