NPC Lag Compensation

It is not recommended to use machine translation without any corrections.
If the article is not corrected in the long term, it will be removed.
Also, please make sure the article complies with the alternate languages guide.
This notice is put here by LanguageBar template and if you want to remove it after updating the translation you can do so on this page.
如果没有延迟补偿 ,当您射击目标时,必须考虑您的网络延迟时间并相应地向前瞄准。例如,如果您的延迟是100毫秒,您必须瞄准目标头部在100毫秒后的位置,而不是当前看到的位置。
显然这并不理想,因此多人Source游戏实现了延迟补偿。在计算子弹是否命中时,会暂时将所有玩家的位置回退到射击者的延迟时间,从而根据射击者开火时看到的精确画面进行碰撞计算。该效果在NPC中缺失,但可以通过复制玩家延迟补偿代码来实现。
本教程详细说明了所有需要的修改——虽然涉及大量代码,但其中95%是从玩家延迟补偿代码中复制的。
ai_basenpc.h
在此行之前:
typedef CBitVec<MAX_CONDITIONS> CAI_ScheduleBits;
添加以下代码(取自player_lagcompensation.cpp
但已修改):
#define MAX_LAYER_RECORDS (CBaseAnimatingOverlay::MAX_OVERLAYS)
struct LayerRecordNPC
{
int m_sequence;
float m_cycle;
float m_weight;
int m_order;
LayerRecordNPC()
{
m_sequence = 0;
m_cycle = 0;
m_weight = 0;
m_order = 0;
}
LayerRecordNPC( const LayerRecordNPC& src )
{
m_sequence = src.m_sequence;
m_cycle = src.m_cycle;
m_weight = src.m_weight;
m_order = src.m_order;
}
};
struct LagRecordNPC
{
public:
LagRecordNPC()
{
m_fFlags = 0;
m_vecOrigin.Init();
m_vecAngles.Init();
m_vecMins.Init();
m_vecMaxs.Init();
m_flSimulationTime = -1;
m_masterSequence = 0;
m_masterCycle = 0;
}
LagRecordNPC( const LagRecordNPC& src )
{
m_fFlags = src.m_fFlags;
m_vecOrigin = src.m_vecOrigin;
m_vecAngles = src.m_vecAngles;
m_vecMins = src.m_vecMins;
m_vecMaxs = src.m_vecMaxs;
m_flSimulationTime = src.m_flSimulationTime;
for( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex )
{
m_layerRecords[layerIndex] = src.m_layerRecords[layerIndex];
}
m_masterSequence = src.m_masterSequence;
m_masterCycle = src.m_masterCycle;
}
// 玩家是否在本帧死亡
int m_fFlags;
// 玩家位置、朝向和包围盒
Vector m_vecOrigin;
QAngle m_vecAngles;
Vector m_vecMins;
Vector m_vecMaxs;
float m_flSimulationTime;
// 玩家动画细节,用于正确计算腿部位置
LayerRecordNPC m_layerRecords[MAX_LAYER_RECORDS];
int m_masterSequence;
float m_masterCycle;
};
我们将NPC的延迟记录直接附加到NPC上以避免混淆,因此NPC代码需要能访问这些记录。
在CAI_BaseNPC类定义中添加:
public:
CUtlFixedLinkedList<LagRecordNPC>* GetLagTrack() { return m_LagTrack; }
LagRecordNPC* GetLagRestoreData() { if ( m_RestoreData != NULL ) return m_RestoreData; else return new LagRecordNPC(); }
LagRecordNPC* GetLagChangeData() { if ( m_ChangeData != NULL ) return m_ChangeData; else return new LagRecordNPC(); }
void SetLagRestoreData(LagRecordNPC* l) { if ( m_RestoreData != NULL ) delete m_RestoreData; m_RestoreData = l; }
void SetLagChangeData(LagRecordNPC* l) { if ( m_ChangeData != NULL ) delete m_ChangeData; m_ChangeData = l; }
void FlagForLagCompensation( bool tempValue ) { m_bFlaggedForLagCompensation = tempValue; }
bool IsLagFlagged() { return m_bFlaggedForLagCompensation; }
private:
CUtlFixedLinkedList<LagRecordNPC>* m_LagTrack;
LagRecordNPC* m_RestoreData;
LagRecordNPC* m_ChangeData;
bool m_bFlaggedForLagCompensation;
这个"延迟记录"存储NPC的历史位置和动画信息。
ai_basenpc.cpp
在CAI_BaseNPC构造函数末尾添加:
m_LagTrack = new CUtlFixedLinkedList< LagRecordNPC >();
在析构函数开头添加:
m_LagTrack->Purge();
delete m_LagTrack;
player.cpp
/player.h
将函数WantsLagCompensationOnEntity的第一个参数从const CBasePlayer *pPlayer改为const CBaseEntity *pEntity。
在函数中将所有pPlayer引用改为pEntity,并替换以下行:
float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat();
改为:
float maxspeed;
CBasePlayer *pPlayer = ToBasePlayer((CBaseEntity*)pEntity);
if ( pPlayer )
maxspeed = pPlayer->MaxSpeed();
else
maxspeed = 600;
float maxDistance = 1.5 * maxspeed * sv_maxunlag.GetFloat();
hl2mp_player.cpp
/hl2mp_player.h
修改CHL2MP_Player::WantsLagCompensationOnEntity函数:
bool CHL2MP_Player::WantsLagCompensationOnEntity( const CBaseEntity *pEntity, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
{
if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) )
return false;
return BaseClass::WantsLagCompensationOnEntity(pEntity,pCmd,pEntityTransmitBits);
}
player_lagcompensation.cpp
添加头文件:
#include "ai_basenpc.h"
添加NPC回溯函数:
static void RestoreEntityTo( CAI_BaseNPC *pEntity, const Vector &vWantedPos )
{
// 尝试从当前位置移动到目标位置
trace_t tr;
UTIL_TraceEntity( pEntity, vWantedPos, vWantedPos, MASK_NPCSOLID, pEntity, COLLISION_GROUP_NPC, &tr );
if ( tr.startsolid || tr.allsolid )
{
// 回溯失败处理...
}
else
{
UTIL_SetOrigin( pEntity, tr.endpos, true );
}
}
在CLagCompensationManager类中添加:
void BacktrackEntity( CAI_BaseNPC *entity, float flTargetTime );
更新FrameUpdatePostEntityThink函数,添加NPC处理循环。
修改StartLagCompensation函数,添加NPC标记初始化。
在碰撞检测部分添加NPC处理分支。
实现BacktrackEntity函数,完整复制玩家回溯逻辑并适配NPC。
最后在FinishLagCompensation中添加NPC状态恢复代码。
验证方法
在控制台输入:
sv_cheats 1
sv_showlagcompensation 1
射击NPC时会出现蓝色骨骼框表示延迟补偿生效。