Adding Ironsights
翻译: 233wer
译者:我已按照原文做过一遍,部分地方是按照自己的想法来翻译
这里是完成之后的效果[1]
这是一个关于如何添加机瞄系统的教程,根据 Jorg的代码 .
实现他的途径是使用NetWork变量,尝试着让这个系统更容易影响到游戏的逻辑(比如说允许机瞄影响到子弹散射角度大小之类的),这个机瞄系统还允许更改v模型的角度和玩家镜头的FOV
武器脚本文件
一开始,你需要通过 武器脚本 来添加脚本变量才可以调整武器模型的坐标位置改变来实现机瞄效果
weapon_parse.h
添加 FileWeaponInfo_t: 的变量(public)
Vector vecIronsightPosOffset;
QAngle angIronsightAngOffset;
float flIronsightFOVOffset;
weapon_parse.cpp
在 FileWeaponInfo_t::Parse: 里添加如下代码
KeyValues *pSights = pKeyValuesData->FindKey( "IronSight" );
if (pSights)
{
vecIronsightPosOffset.x = pSights->GetFloat( "forward", 0.0f );
vecIronsightPosOffset.y = pSights->GetFloat( "right", 0.0f );
vecIronsightPosOffset.z = pSights->GetFloat( "up", 0.0f );
angIronsightAngOffset[PITCH] = pSights->GetFloat( "pitch", 0.0f );
angIronsightAngOffset[YAW] = pSights->GetFloat( "yaw", 0.0f );
angIronsightAngOffset[ROLL] = pSights->GetFloat( "roll", 0.0f );
flIronsightFOVOffset = pSights->GetFloat( "fov", 0.0f );
}
else
{
//note: you can set a bool here if you'd like to disable ironsights for weapons with no IronSight-key
vecIronsightPosOffset = vec3_origin;
angIronsightAngOffset.Init();
flIronsightFOVOffset = 0.0f;
}
weapon_smg1.txt
这是一个如何在你的武器脚本里添加武器机瞄系统位移量数值的例子
IronSight { "forward" "-10" "right" "-6.91" "up" "0.185" "roll" "-20" "fov" "-20" }
获取位移量
现在添加一些功能性代码来得到我们从武器脚本文件里解析到的位移量数值 额外添加一些 控制台变量 来覆写掉旧的解析的数值,这样就可以很容易的通过 控制台来改变新的武器模型偏移量数值。
在那之前, 我们需要在开头包含 "c_baseplayer.h" 头文件来允许使用一些代码,因为要在客户端进行一些处理,
基本的, 你需要在包含 "c_baseplayer.h" 头文件时加上 #ifdef CLIENT_DLL 标签。不然编译的时候会出错。
#ifdef CLIENT_DLL
#include "c_baseplayer.h"
#endif
你应该加在这段代码的下面:(译者注:似乎不需要?)
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
#include "tf_shareddefs.h"
#endif
添加在 public 段
Vector GetIronsightPositionOffset( void ) const;
QAngle GetIronsightAngleOffset( void ) const;
float GetIronsightFOVOffset( void ) const;
添加这些功能性代码 (直接做这一步会导致错误)
Vector CBaseCombatWeapon::GetIronsightPositionOffset( void ) const
{
if( viewmodel_adjust_enabled.GetBool() )
return Vector( viewmodel_adjust_forward.GetFloat(), viewmodel_adjust_right.GetFloat(), viewmodel_adjust_up.GetFloat() );
return GetWpnData().vecIronsightPosOffset;
}
QAngle CBaseCombatWeapon::GetIronsightAngleOffset( void ) const
{
if( viewmodel_adjust_enabled.GetBool() )
return QAngle( viewmodel_adjust_pitch.GetFloat(), viewmodel_adjust_yaw.GetFloat(), viewmodel_adjust_roll.GetFloat() );
return GetWpnData().angIronsightAngOffset;
}
float CBaseCombatWeapon::GetIronsightFOVOffset( void ) const
{
if( viewmodel_adjust_enabled.GetBool() )
return viewmodel_adjust_fov.GetFloat();
return GetWpnData().flIronsightFOVOffset;
}
这些变量通常在包含头文件后定义
//forward declarations of callbacks used by viewmodel_adjust_enable and viewmodel_adjust_fov
void vm_adjust_enable_callback( IConVar *pConVar, char const *pOldString, float flOldValue );
void vm_adjust_fov_callback( IConVar *pConVar, const char *pOldString, float flOldValue );
ConVar viewmodel_adjust_forward( "viewmodel_adjust_forward", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_right( "viewmodel_adjust_right", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_up( "viewmodel_adjust_up", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_pitch( "viewmodel_adjust_pitch", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_yaw( "viewmodel_adjust_yaw", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_roll( "viewmodel_adjust_roll", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_fov( "viewmodel_adjust_fov", "0", FCVAR_REPLICATED, "Note: this feature is not available during any kind of zoom", vm_adjust_fov_callback );
ConVar viewmodel_adjust_enabled( "viewmodel_adjust_enabled", "0", FCVAR_REPLICATED|FCVAR_CHEAT, "enabled viewmodel adjusting", vm_adjust_enable_callback );
包括这些:
void vm_adjust_enable_callback( IConVar *pConVar, char const *pOldString, float flOldValue )
{
ConVarRef sv_cheats( "sv_cheats" );
if( !sv_cheats.IsValid() || sv_cheats.GetBool() )
return;
ConVarRef var( pConVar );
if( var.GetBool() )
var.SetValue( "0" );
}
void vm_adjust_fov_callback( IConVar *pConVar, char const *pOldString, float flOldValue )
{
if( !viewmodel_adjust_enabled.GetBool() )
return;
ConVarRef var( pConVar );
CBasePlayer *pPlayer =
#ifdef GAME_DLL
UTIL_GetCommandClient();
#else
C_BasePlayer::GetLocalPlayer();
#endif
if( !pPlayer )
return;
if( !pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV() + var.GetFloat(), 0.1f ) )
{
Warning( "Could not set FOV\n" );
var.SetValue( "0" );
}
}
添加开启机瞄的函数系统
你肯定想要能使用上面定义好的机瞄系统
添加这两个NetWork变量
CNetworkVar( bool, m_bIsIronsighted );
CNetworkVar( float, m_flIronsightedTime );
在 CBaseCombatWeapon::CBaseCombatWeapon() 里添加两个构造函数,并给他们默认值
m_bIsIronsighted = false;
m_flIronsightedTime = 0.0f;
链接表 (位于DT_BaseCombatWeapon):
SendPropBool( SENDINFO( m_bIsIronsighted ) ),
SendPropFloat( SENDINFO( m_flIronsightedTime ) ),
并且添加
RecvPropInt( RECVINFO( m_bIsIronsighted ), 0, RecvProxy_ToggleSights ), //note: RecvPropBool is actually RecvPropInt (see its implementation), but we need a proxy
RecvPropFloat( RECVINFO( m_flIronsightedTime ) ),
(译者注:SendProp应该和原先的同类定义放在一起,RecvPropInt也是如此)
我们需要一个 RecvProxy 在这个变量上,我们不想要布尔值更新,但我们想要通过在 服务器 端上的变化来开启机瞄
proxy(代理)系统的代码:
#ifdef CLIENT_DLL
void RecvProxy_ToggleSights( const CRecvProxyData* pData, void* pStruct, void* pOut )
{
CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pStruct;
if( pData->m_Value.m_Int )
pWeapon->EnableIronsights();
else
pWeapon->DisableIronsights();
}
#endif
预测(?)表(CBaseCombatWeapon):
DEFINE_PRED_FIELD( m_bIsIronsighted, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_flIronsightedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
如果你想要在游戏里保存变量, 在数据描述里添加:
DEFINE_FIELD( m_bIsIronsighted, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flIronsightedTime, FIELD_FLOAT ),
添加机瞄变量的参数:
virtual bool HasIronsights( void ) { return true; } //default yes; override and return false for weapons with no ironsights (like weapon_crowbar)
bool IsIronsighted( void );
void ToggleIronsights( void );
void EnableIronsights( void );
void DisableIronsights( void );
void SetIronsightTime( void );
同时定义他们的函数:
bool CBaseCombatWeapon::IsIronsighted( void )
{
return ( m_bIsIronsighted || viewmodel_adjust_enabled.GetBool() );
}
void CBaseCombatWeapon::ToggleIronsights( void )
{
if( m_bIsIronsighted )
DisableIronsights();
else
EnableIronsights();
}
void CBaseCombatWeapon::EnableIronsights( void )
{
#ifdef CLIENT_DLL
if( !prediction->IsFirstTimePredicted() )
return;
#endif
if( !HasIronsights() || m_bIsIronsighted )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if( !pOwner )
return;
if( pOwner->SetFOV( this, pOwner->GetDefaultFOV() + GetIronsightFOVOffset(), 1.0f ) ) //modify the last value to adjust how fast the fov is applied
{
m_bIsIronsighted = true;
SetIronsightTime();
}
}
void CBaseCombatWeapon::DisableIronsights( void )
{
#ifdef CLIENT_DLL
if( !prediction->IsFirstTimePredicted() )
return;
#endif
if( !HasIronsights() || !m_bIsIronsighted )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if( !pOwner )
return;
if( pOwner->SetFOV( this, 0, 0.4f ) ) //modify the last value to adjust how fast the fov is applied
{
m_bIsIronsighted = false;
SetIronsightTime();
}
}
void CBaseCombatWeapon::SetIronsightTime( void )
{
m_flIronsightedTime = gpGlobals->curtime;
}
prediction 的使用需要在client端上包含 prediction.h 头文件。
Toggle-command
ConCommand
你也许需要设置一个 命令 来在普通和机瞄模式中切换,你把它放在哪里都没关系(我是放在上面定义过的ConVar变量的后面),只要你已经包含了CBaseCombatWeapon和CBasePlayer的头文件(推荐就放在basecombatweapon_shared.cpp里)
#ifdef CLIENT_DLL
void CC_ToggleIronSights( void )
{
CBasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
if( pPlayer == NULL )
return;
CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
if( pWeapon == NULL )
return;
pWeapon->ToggleIronsights();
engine->ServerCmd( "toggle_ironsight" ); //forward to server
}
static ConCommand toggle_ironsight("toggle_ironsight", CC_ToggleIronSights);
#endif
player.cpp
接着在CBasePlayer::ClientCommand里, 在返回false前添加:
else if( stricmp( cmd, "toggle_ironsight" ) == 0 )
{
CBaseCombatWeapon *pWeapon = GetActiveWeapon();
if( pWeapon != NULL )
pWeapon->ToggleIronsights();
return true;
}
自动切换机瞄模式
添加
DisableIronsights();
到下面的函数中:
- bool CBaseCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) 切换武器
- bool CBaseCombatWeapon::DefaultReload( int iClipSize1, int iClipSize2, int iActivity ) 武器装填
- void CBaseCombatWeapon::Drop( const Vector &vecVelocity ) 扔掉武器(也许添加了扔武器的功能可以用?这个反正不是必须,只为上面两个添加,最后的效果也挺好

weapon_shotgun.cpp
Find: bool CWeaponShotgun::StartReload( void )
在某处添加
DisableIronsights();
调整viewmodel
接下来最后一步就是将武器的手臂移动到我们想要的位置
仍然有一些小问题要调整, 尽管Viewmodel-bob(不太清楚这是什么)在使用机瞄时仍然在运作并且当viewmodel启用机瞄并旋转时,viewmodel会缓慢移动得太多
void CBaseViewModel::CalcIronsights( Vector &pos, QAngle &ang )
{
CBaseCombatWeapon *pWeapon = GetOwningWeapon();
if ( !pWeapon )
return;
//get delta time for interpolation
float delta = ( gpGlobals->curtime - pWeapon->m_flIronsightedTime ) * 2.5f; //modify this value to adjust how fast the interpolation is
float exp = ( pWeapon->IsIronsighted() ) ?
( delta > 1.0f ) ? 1.0f : delta : //normal blending
( delta > 1.0f ) ? 0.0f : 1.0f - delta; //reverse interpolation
if( exp <= 0.001f ) //fully not ironsighted; save performance
return;
Vector newPos = pos;
QAngle newAng = ang;
Vector vForward, vRight, vUp, vOffset;
AngleVectors( newAng, &vForward, &vRight, &vUp );
vOffset = pWeapon->GetIronsightPositionOffset();
newPos += vForward * vOffset.x;
newPos += vRight * vOffset.y;
newPos += vUp * vOffset.z;
newAng += pWeapon->GetIronsightAngleOffset();
//fov is handled by CBaseCombatWeapon
pos += ( newPos - pos ) * exp;
ang += ( newAng - ang ) * exp;
}
让这段代码在CBaseViewModel::CalcViewModelView里运作:
void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles )
{
// UNDONE: Calc this on the server? Disabled for now as it seems unnecessary to have this info on the server
#if defined( CLIENT_DLL )
QAngle vmangoriginal = eyeAngles;
QAngle vmangles = eyeAngles;
Vector vmorigin = eyePosition;
CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
//Allow weapon lagging
//only if not in ironsight-mode
if( pWeapon == NULL || !pWeapon->IsIronsighted() )
{
if ( pWeapon != NULL )
{
#if defined( CLIENT_DLL )
if ( !prediction->InPrediction() )
#endif
{
// add weapon-specific bob
pWeapon->AddViewmodelBob( this, vmorigin, vmangles );
}
}
// Add model-specific bob even if no weapon associated (for head bob for off hand models)
AddViewModelBob( owner, vmorigin, vmangles );
// Add lag
CalcViewModelLag( vmorigin, vmangles, vmangoriginal );
#if defined( CLIENT_DLL )
if ( !prediction->InPrediction() )
{
// Let the viewmodel shake at about 10% of the amplitude of the player's view
vieweffects->ApplyShake( vmorigin, vmangles, 0.1 );
}
#endif
}
CalcIronsights( vmorigin, vmangles );
SetLocalOrigin( vmorigin );
SetLocalAngles( vmangles );
#endif
}
别忘了定义新的函数功能:
void CalcIronsights( Vector &pos, QAngle &ang );
单人模式修复 (武器模型不固定在机瞄视角上)
大多数时候武器模型(viewmodel) 启用机瞄后并不保持在绝对的位置上
修复挺简单(武器混合和混合出).
转到 basecombatweapon_shared.cpp
当你添加完EnableIronsight 和 DisableIronsight后,把他们修改成:
void CBaseCombatWeapon::EnableIronsights( void )
{
/*
#ifdef CLIENT_DLL
if( !prediction->IsFirstTimePredicted() )
return;
#endif*/
if( !HasIronsights() || m_bIsIronsighted )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if( !pOwner )
return;
if( pOwner->SetFOV( this, pOwner->GetDefaultFOV() + GetIronsightFOVOffset(), 0.4f ) ) //modify the last value to adjust how fast the fov is applied
{
m_bIsIronsighted = true;
SetIronsightTime();
}
}
void CBaseCombatWeapon::DisableIronsights( void )
{
/*
#ifdef CLIENT_DLL
if( !prediction->IsFirstTimePredicted() )
return;
#endif*/
// We are not using prediction in singleplayer
if( !HasIronsights() || !m_bIsIronsighted )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if( !pOwner )
return;
if( pOwner->SetFOV( this, 0, 0.2f ) ) //modify the last value to adjust how fast the fov is applied
{
m_bIsIronsighted = false;
SetIronsightTime();
}
}
--Dexter127 2012 年 7 月 23 日 , 3 点 43 分 (PDT)
半条命2:第一章 引擎修复
(用于旧版SDK,暂不做翻译,新版代码应该没有这个问题了)
In the Episode 1-engine, SetFOV is server-side only. A simple fix would be to set the FOV on the server only, but here's a client-side implementation, partially derived from OB code.
m_hZoomOwner
Set m_hZoomOwner up for networking in player.cpp/.h by using a CNetworkHandle and adding it to the send-table.
In c_baseplayer.cpp/.h, add the m_hZoomOwner to the class-declaration, so it can be received. Set it to NULL in the constructor and add it to the recv- and the prediction-table.
Also add a SetFOV-function to C_BasePlayer and define it like this:
bool C_BasePlayer::SetFOV( C_BaseEntity *pRequester, int FOV, float zoomRate )
{
//NOTENOTE: You MUST specify who is requesting the zoom change
assert( pRequester != NULL );
if ( pRequester == NULL )
return false;
if( ( m_hZoomOwner.Get() != NULL ) && ( m_hZoomOwner.Get() != pRequester ) )
return false;
else
{
//FIXME: Maybe do this is as an accessor instead
if ( FOV == 0 )
{
m_hZoomOwner = NULL;
}
else
{
m_hZoomOwner = pRequester;
}
}
m_iFOV = FOV;
m_Local.m_flFOVRate = zoomRate;
return true;
}
ConVarRef
ConVarRef does not exist in the Episode 1-engine. You can either use the OB tier1-library (note: untested; might not work) or instead of ConVarRef( "sv_cheats" ), put extern ConVar sv_cheats; at the top of cbasecombatweapon_shared.cpp and use the member by pointer-operator (->) instead of the member-operator (.).
For the ConVars in the callbacks, simply use the ConVars directly or cast the IConVar to a ConVar.
调整武器子弹散步角度
打开你想让机瞄系统影响到的武器的.cpp文件,这里以SMG的.cpp作为示范。
weapon_smg1.cpp
搜索并找到 GetBulletSpread 函数,应该会看起来像这样:
virtual const Vector& GetBulletSpread( void )
{
static const Vector cone = VECTOR_CONE_5DEGREES;
return cone;
}
你可以像下面这个例子一样替换他(译者:DEGREES前面的数字“应该”可以随便改):
virtual const Vector& GetBulletSpread( void )
{
if ( m_bIsIronsighted )
{
static const Vector cone = VECTOR_CONE_1DEGREES;
return cone;
}
else
{
static const Vector cone = VECTOR_CONE_5DEGREES;
return cone;
}
}
这里有个图片实例,左边散布值为5度,右边散布值为1度
添加按键绑定
给 选项/按键目录 添加键值:
kb_act.lst
在 "#Valve_Combat_Title" 某处添加:
"toggle_ironsight" "#MOD_Toggle_Ironsight"
不要忘了添加 #MOD_Toggle_Ironsight 到你的 resource/MOD_english.txt 里 (以及其他语言).
机瞄提示音
添加
pPlayer->EmitSound( "WWIPlayer.IronSightIn" );
到 EnableIronsights 和
pPlayer->EmitSound( "WWIPlayer.IronSightOut" );
到 DisableIronsights.
把语句 WWIPlayer.IronSightin 和 WWIPlayer.IronSightOut 替换为你的声音文件名字并且预缓存他们
装填时避免保持在机瞄模式中
当装填时, 你仍然可以使用机瞄模式
甚至如果你把 DisableIronsights (); 放在 bool CBaseCombatWeapon:: DefaultReload里仍然会出现上述情况
当然, 他会自己关闭, 但如果你再按一次你设置的机瞄按键, 机瞄系统会重新工作但装填动画将会从"瞄准" 视角播放, 看起来不是太好。
为了防止这种事情,在:
找到函数: CBaseCombatWeapon::ToggleIronsights(void)
把他完全替换为:
void CBaseCombatWeapon::ToggleIronsights(void) //No possible use Iron Sight durin reloading
{
if (m_bInReload == true)
{
DisableIronsights();
}
else
{
if (m_bIsIronsighted)
DisableIronsights();
else
EnableIronsights();
}
}
简单的检查武器是否可用/隐藏准星
When you press ironsight toggle key with a weapon has no ironsight data in your hand.It will have no lag effect.And tt also looks wired.
The crosshair also seems to be unnecessary when you toggle the ironsight view.I am sure that you will also want to hide it
The author of the article says use bool to avoid using the weapon that has no Ironsight data in its weapon-script,But It can be achieved without setting a bool.
CC_ToggleIronSights()
In the CC_ToggleIronSights,Use FClassnameIs() to set the weapon that allowed to use.
The functions will not work unless player has the weapons that set in FClassnameIs() in hand
#ifdef CLIENT_DLL
void CC_ToggleIronSights(void)
{
CBasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
if (pPlayer == NULL)
return;
if (pPlayer->GetActiveWeapon() && (FClassnameIs(pPlayer->GetActiveWeapon(), "weapon_smg1") || FClassnameIs(pPlayer->GetActiveWeapon(), "weapon_pistol") || FClassnameIs(pPlayer->GetActiveWeapon(), "weapon_357") ||
FClassnameIs(pPlayer->GetActiveWeapon(), "weapon_shotgun")))
{
//If Player is using the weapon that allowed to use(smg/pistol/357/shotgun),Toggle IronSights function:
Msg("IronSight Mode Changed\n");//TestMsg
pPlayer->GetActiveWeapon()->ToggleIronsights();//ToggleIronSights
engine->ServerCmd("toggle_ironsight"); //forward to server
}
else//Otherwise
{
//Just Show a warning msg and do nothing special
Warning("Curret Weapon Doesn`t allow to Use IronSight function!\n");
}
}
static ConCommand toggle_ironsight("toggle_ironsight", CC_ToggleIronSights);
#endif
EnableIronsights()
使用函数获取控制准星显示和隐藏的变量并将值给予指针, 并且设置值为 "0" 来隐藏准星
void CBaseCombatWeapon::EnableIronsights(void)
{
/*#ifdef CLIENT_DLL
if (!prediction->IsFirstTimePredicted())
return;
#endif
*/
if (!HasIronsights() || m_bIsIronsighted)
return;
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
if (!pOwner)
return;
if (pOwner->SetFOV(this, pOwner->GetDefaultFOV() + GetIronsightFOVOffset(), 0.4f)) //modify the last value to adjust how fast the fov is applied
{
//Set ConVar "crosshair" to 0 to hide crosshair
ConVar *crosshair = cvar->FindVar("Crosshair");
crosshair->SetValue("0");
m_bIsIronsighted = true;
SetIronsightTime();
}
}
DisableIronsights()
跟上面一样,不过这次值设置为“1”
void CBaseCombatWeapon::DisableIronsights(void)
{
/*
#ifdef CLIENT_DLL
if( !prediction->IsFirstTimePredicted() )
return;
#endif
*/
// We are not using prediction in singleplayer
if (!HasIronsights() || !m_bIsIronsighted)
return;
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
if (!pOwner)
return;
if (pOwner->SetFOV(this, 0, 0.2f)) //modify the last value to adjust how fast the fov is applied
{
//Set ConVar "crosshair" to 1 to show crosshair
ConVar *crosshair = cvar->FindVar("Crosshair");
crosshair->SetValue("1");
m_bIsIronsighted = false;
SetIronsightTime();
}
}
--233wer 2023 年 4 月 20 日 16 点 52 分(GMT)