【问题标题】:Linear Dead Reckoning in Winsock applicationWinsock 应用程序中的线性航位推算
【发布时间】:2014-01-03 22:10:18
【问题描述】:

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

谁能帮忙回答我的问题?

谢谢

【问题讨论】:

    标签: c++ networking winsock dead-reckoning


    【解决方案1】:

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

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

    以上变量描述如下:

    Pt:估计的位置。输出

    PO:物体最近的位置更新

    VO:物体最近的速度更新

    AO:物体最近一次加速更新

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

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


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

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

    此网页引用了《Game Engine Gems 2》一书,是航位推算的金矿:

    Believable Dead Reckoning for Networked Games

    编辑:以上所有内容仅针对客户端在未获得更新时应如何操作。至于“何时应该从服务器向客户端发送消息”,Valve says 一个好的服务器应该以大约 15 毫秒的间隔发送更新,大约每秒 66.6 个。

    注意:“Valve says”链接实际上也有一些很好的网络提示,使用 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;
    }
    

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

    【讨论】:

    • 非常感谢!一定会为你赢得一些学分;)
    • @user2990037 只要确保elapsedSecondsnew - old 而不是new / old xD
    • 哈哈是的,我意识到这只是一个错字,我的意思是新旧 :) 非常感谢您的示例代码顺便说一句。我实际上并没有在我的游戏中使用加速,所以代码会与您发布的内容有很大不同吗?或者我可以存储之前的三个位置并使用这些位置计算加速度吗?
    • 抱歉耽搁了 - 作业:P。无论如何,要摆脱加速,只需将PredictPosition 代码替换为:prediction->position = old->position + (old->velocity * (float)elapsedSeconds); 小菜一碟。另外,请注意elapsedSeconds 的变化——它的类型从 DWORD 变为浮动。我的大错误 - 我打算使用经过的毫秒然后改变主意并使用秒而不更新代码。
    【解决方案2】:

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

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

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

    new delta time = current time / oldest time
    

    应该是这样的

    new delta time = current time - oldTime
    

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

    【讨论】:

    • 感谢您的回答!但是我将不得不接受代理的回答:)
    猜你喜欢
    • 2017-02-10
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多