У меня возникли небольшие трудности с пониманием того, как я реализую Dead Reckoning в моей игре Winsock Server-Client.
Я искал в интернете достойное объяснение, которое точно объясняет:
Когда сообщение должно быть отправлено с сервера клиенту
Как должен действовать клиент, если он не получает обновленных сообщений, продолжает ли он использовать прогнозируемую позицию в качестве текущей позиции для расчета новой прогнозируемой позиции?
Я использую метод мертвого счета:
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, и что игра ведется только на сервере. Сервер отправляет сообщения об обновлении клиенту.
Кто-нибудь может помочь, ответив на мои вопросы, пожалуйста?
Спасибо
Мертвый расчет требует группу переменных для функции — называется кинематическое состояние — обычно содержит положение, скорость, ускорение, ориентацию и угловую скорость данного объекта. Вы можете игнорировать ориентацию и угловую скорость, если вы ищете только положение. Оставьте комментарий, если вы хотите предсказать ориентацию, а также положение, и я обновлю свой ответ.
Стандартный алгоритм мертвых расчетов для сетевых игр показан здесь:
Перечисленные выше переменные описаны так:
пT: Предполагаемое местоположение. Выход
пО: Самое последнее обновление позиции объекта
ВО: Последнее обновление скорости объекта
О: Самое последнее обновление ускорения объекта
T: прошедшие секунды между текущим временем и отметка времени последнего обновления — НЕ время, когда пакет был получен.
Это можно использовать для перемещения объекта до тех пор, пока обновление не будет получено с сервера. Затем у вас есть два кинематических состояния: предполагаемая позиция (самый последний результат вышеприведенного алгоритма) и только что полученная фактическая позиция. Реальное смешение этих двух состояний может быть трудным.
Одним из подходов является создание линии или, что еще лучше, кривой, такой как Сплайны Безье Сплайны Катмулла-Рома а также Кривые Эрмита (хороший список других методов Вот), между двумя государствами, все еще проецируя старую ориентацию в будущее. Итак, продолжайте использовать старое состояние, пока не получите новое — когда состояние, в которое вы переходите, становится старым.
Другой метод заключается в использовании проекционного смешивания скорости, которое представляет собой смешивание двух проекций — последнего известного состояния и текущего состояния — где текущее отображаемое положение представляет собой смесь последней известной и текущей скорости в течение заданного времени.
Эта веб-страница, цитирующая книгу «Game Engine Gems 2», является золотым прииском для расплаты:
Правдоподобная расплата за сетевые игры
РЕДАКТИРОВАТЬ: Все вышеперечисленное только для того, как клиент должен действовать, когда он не получает обновления. Что касается «Когда сообщение должно быть отправлено с сервера на клиент», Valve говорит хороший сервер должен отправлять обновления с интервалом примерно 15 миллисекунд, примерно 66,6 в секунду.
Обратите внимание «Valve говорит«link действительно имеет несколько хороших советов по работе с сетями, используя 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