Linear Dead Reckoning в приложении Winsock

У меня возникли некоторые трудности с пониманием того, как я реализую Dead Reckoning в моей игре Winsock Server-Client.

Я искал в интернете достойное объяснение, которое точно объясняет:

  1. Когда сообщение должно быть отправлено с сервера клиенту

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

Я использую метод мертвого счета:

path vector = oldPosition - oldestPosition
delta time = oldTime - oldestTime
delta velocity = path vector / delta time
new delta time = current time / oldest time
new prediction = oldPosition + new delta time * delta velocity

Надеюсь, что это правильная формула для использования!:)

Следует также отметить, что тип подключения - UDP, и что игра ведется только на сервере. Сервер отправляет сообщения об обновлении клиенту.

Кто-нибудь может помочь, ответив на мои вопросы, пожалуйста?

Спасибо

2 ответа

Решение

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

Стандартный алгоритм мертвых расчетов для сетевых игр показан здесь:

Мертвое уравнение счета

Перечисленные выше переменные описаны так:

Pt: расчетное местоположение. Выход

PO: самое последнее обновление позиции объекта

VO: самое последнее обновление скорости объекта

AO: самое последнее обновление ускорения объекта

T: истекшие секунды между текущим временем и отметкой времени последнего обновления, а НЕ временем, когда пакет был получен.

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


Один из подходов состоит в том, чтобы создать линию или, что еще лучше, кривую, такую ​​как сплайны Безье, сплайны Катмулла-Рома и кривые Эрмита (хороший список других методов здесь), между двумя состояниями, при этом все еще проецируя старую ориентацию в будущее. Итак, продолжайте использовать старое состояние, пока не получите новое - когда состояние, в которое вы переходите, становится старым.

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

Эта веб-страница, цитирующая книгу "Game Engine Gems 2", является золотым прииском для расплаты:

Правдоподобная расплата за сетевые игры

РЕДАКТИРОВАТЬ: Все вышеизложенное только для того, как клиент должен действовать, когда он не получает обновления. Что касается "Когда сообщение должно быть отправлено с сервера на клиент", Valve говорит, что хороший сервер должен отправлять обновления с интервалом примерно 15 миллисекунд, примерно 66,6 в секунду.

Примечание: ссылка " Valve говорит" на самом деле содержит несколько хороших советов по работе с сетью, используя Source Multiplayer Networking в качестве среды. Проверьте это, если у вас есть время.

РЕДАКТИРОВАТЬ 2 (обновление кода!):

Вот как я мог бы реализовать такой алгоритм в среде C++/DirectX:

struct kinematicState
{
     D3DXVECTOR3 position;
     D3DXVECTOR3 velocity;
     D3DXVECTOR3 acceleration;
};

void PredictPosition(kinematicState *old, kinematicState *prediction, float elapsedSeconds)
{
     prediction->position = old->position + (old->velocity * elapsedSeconds) + (0.5 * old->acceleration * (elapsedSeconds * elapsedSeconds));`
}

kinematicState *BlendKinematicStateLinear(kinematicState *olStated, kinematicState *newState, float percentageToNew)
{
     //Explanation of percentateToNew:
     //A value of 0.0 will return the exact same state as "oldState",
     //A value of 1.0 will return the exact same state as "newState",
     //A value of 0.5 will return a state with data exactly in the middle of that of "old" and "new".
     //Its value should never be outside of [0, 1].

     kinematicState *final = new kinematicState();

     //Many other interpolation algorithms would create a smoother blend,
     //But this is just a linear interpolation to keep it simple.

     //Implementation of a different algorithm should be straightforward.
     //I suggest starting with Catmull-Rom splines.

     float percentageToOld = 1.0 - percentageToNew;

     final->position = (percentageToOld * oldState->position) + (percentageToNew * new-State>position);
     final->velocity = (percentageToOld * oldState->velocity) + (percentageToNew * newState->velocity);
     final->acceleration = (percentageToOld * oldState->acceleration) + (percentageToNew * newState->acceleration);

     return final;
}

Удачи, и если вам удастся заработать миллионы на игре, попробуйте поставить меня в титры;)

Это общий и широкий вопрос.

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

Кстати мне кажется, что следующее

new delta time = current time / oldest time

должно быть что-то вроде

new delta time = current time - oldTime

чтобы получить время, прошедшее до последнего прогноза. В противном случае вы могли бы предположить, что система работала быстрее, когда прошло больше времени, и медленнее, когда прошло немного времени (по сравнению с самым старым временем, использованным в качестве единицы). Уравнение линейного движения (без ускорения): new_s = s_0 + vel * t

Другие вопросы по тегам