4

我在理解如何在我的服务器客户端 Winsock 游戏中实现航位推算时遇到了一点困难。

我一直在互联网上寻找一个可以准确解释的体面解释:

  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,并且游戏仅在服务器上进行。服务器向客户端发送更新消息。

任何人都可以通过回答我的问题来帮忙吗?

谢谢

4

2 回答 2

10

航位推算需要一组变量来运行——称为运动状态——通常包含给定对象的位置、速度、加速度、方向和角速度。如果您只是在寻找位置,您可以选择忽略方向和角速度。如果您想预测方向和位置,请发表评论,我会更新我的答案。

网络游戏的标准航位推算算法如下所示:

航位推算方程

上述变量描述如下:

P t:估计的位置。输出

P O : 对象最近一次的位置更新

V O : 物体最近的速度更新

A O : 对象最近的加速度更新

T:当前时间和上次更新时间戳之间经过的秒数- 不是收到数据包的时间

这可用于移动对象,直到从服务器接收到更新。然后,您有两个运动学状态:估计位置(上述算法的最新输出)和刚刚收到的实际位置。实际混合这两种状态可能很困难。


一种方法是在两种状态之间创建一条线,甚至更好,一条曲线,例如Bézier 样条 Catmull-Rom 样条Hermite 曲线(其他方法的一个很好的列表在这里),同时仍将旧方向投影到未来。所以,继续使用旧状态,直到你得到一个新状态——当你正在混合的状态变成旧状态时。

另一种技术是使用投影速度混合,它是两个投影的混合——最后已知状态和当前状态——其中当前渲染位置是给定时间内最后已知速度和当前速度的混合。

该网页引用了《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;
}

祝你好运,呃,如果你碰巧在游戏中赚了几百万,试着把我放在学分中;)

于 2014-01-03T22:37:11.933 回答
1

这是一个需要回答的普遍而广泛的问题。

如果您要在客户端实现具有航位推算的游戏服务器(我假设您正在这样做),那么只要您没有从服务器获得新的输入,您就应该继续估计这些值。此时,您应该强制刷新新位置/时间/存储的任何内容。没有服务器响应意味着您必须根据最新的估算值自行估算。

顺便说一句,在我看来,以下

new delta time = current time / oldest time

应该是类似的东西

new delta time = current time - oldTime

为了得到上次预测之前经过的时间。否则,您会假设系统在经过更多时间时运行得更快,而在经过很少时间(与用作单位的最旧时间相比)时变慢。线性运动方程(未加速)为 new_s = s_0 + vel * t

于 2014-01-03T22:28:39.367 回答