Weapon Accuracy and Vector Projectiles: Difference between revisions
Matthew334 (talk | contribs) |
Matthew334 (talk | contribs) |
||
Line 152: | Line 152: | ||
//Apply this to the view angles as well | //Apply this to the view angles as well | ||
vecScratch.x = -( KICK_MIN_X + ( maxVerticleKickAngle * kickPercy ) ) / ( 1 * dampEasy ); // is y axis or vertical. | vecScratch.x = -( KICK_MIN_X + ( maxVerticleKickAngle * kickPercy ) ) / ( 1 * dampEasy ); // is y axis or vertical. | ||
vecScratch.y = -( KICK_MIN_Y + ( maxVerticleKickAngle * kickPercy ) ) / ( 2 * dampEasy ); // is really X axis or horizontal. remove / | vecScratch.y = -( KICK_MIN_Y + ( maxVerticleKickAngle * kickPercy ) ) / ( 2 * dampEasy ); // is really X axis or horizontal. remove / followed by dampeasy multiplier to return to old value | ||
vecScratch.z = KICK_MIN_Z + ( maxVerticleKickAngle * kickPercy ) / 8; | vecScratch.z = KICK_MIN_Z + ( maxVerticleKickAngle * kickPercy ) / 8; | ||
Revision as of 16:52, 12 August 2019
Weapon Spread Customization
In this tutorial we will discuss some basics of how to effect the spread of projectiles from weapons such as the AR2, SMG1, Pistol, and Shotgun. This will include the effects of recoil parameters on simulating weapon spread as well as shot penalty timers and their effect on base combat weapons throughout your mod. This tutorial is a general overview as many different classes of weapons utilize camera and projectile vector generation differently. To start the tutorial, we need to discuss how the game determines shot angles.
Vector Cones & FOV
The bulk of the combat weapons in HL2 render the "spread" of projectiles based on a fixed "cone" or preset angle defined within a handful of places. These cones are based on the origin or center anchor point of the player's field of view or camera angle in first person. Below is an example from "weapon_smg1.cpp" which is typical for CHLMachineGun and CHLSelectFireMachineGun type weapons.
virtual const Vector& GetBulletSpread( void )
{
static const Vector cone = VECTOR_CONE_5DEGREES;
return cone;
}
One way most beginner mods will get the desired spread pattern or cone is to simply change the vector cone value to the closest desired degree value (Ex. "5DEGREES" is replaced with "1DEGREES" etc.) However this can have it's own issues, especially if we want to have several similar classes of weapons in the mod. To optimize certain weapons, we have to define vector cones specifically.

We can find the declaration for "VECTOR_CONE_5DEGREES" as an example in 'basecombatweapon_shared.h':
// NOTE: The way these are calculated is that each component == sin (degrees/2)
#define VECTOR_CONE_PRECALCULATED vec3_origin
#define VECTOR_CONE_1DEGREES Vector( 0.00873, 0.00873, 0.00873 )
#define VECTOR_CONE_LRIFLE Vector( 0.00980, 0.00980, 0.00980 )
#define VECTOR_CONE_SRIFLE Vector( 0.01261, 0.01261, 0.01261 )
#define VECTOR_CONE_LPISTOL Vector( 0.01474, 0.01474, 0.01474 )
#define VECTOR_CONE_SPISTOL Vector( 0.01648, 0.01658, 0.01658 )
#define VECTOR_CONE_2DEGREES Vector( 0.01745, 0.01745, 0.01745 )
#define VECTOR_CONE_SUBMACHG Vector( 0.02530, 0.02530, 0.02530 )
#define VECTOR_CONE_3DEGREES Vector( 0.02618, 0.02618, 0.02618 )
#define VECTOR_CONE_PDW Vector( 0.03228, 0.03228, 0.03228 )
#define VECTOR_CONE_4DEGREES Vector( 0.03490, 0.03490, 0.03490 )
#define VECTOR_CONE_5DEGREES Vector( 0.04362, 0.04362, 0.04362 )
#define VECTOR_CONE_6DEGREES Vector( 0.05234, 0.05234, 0.05234 )
#define VECTOR_CONE_7DEGREES Vector( 0.06105, 0.06105, 0.06105 )
#define VECTOR_CONE_8DEGREES Vector( 0.06976, 0.06976, 0.06976 )
#define VECTOR_CONE_9DEGREES Vector( 0.07846, 0.07846, 0.07846 )
#define VECTOR_CONE_10DEGREES Vector( 0.08716, 0.08716, 0.08716 )
#define VECTOR_CONE_11DEGREES Vector( 0.09589, 0.09589, 0.09589 )
#define VECTOR_CONE_15DEGREES Vector( 0.13053, 0.13053, 0.13053 )
#define VECTOR_CONE_20DEGREES Vector( 0.17365, 0.17365, 0.17365 )
In this example, the data table has been customized to allow for multiple rifles and submachine guns to be used, each with a slightly different base spread pattern.
Movement and Recoil Penalty
Vanilla Half-Life 2 features some advanced recoil effects in first person view which effect the rumble and pitch of the player camera angle. For this example we will discuss how this is accomplished and how to manipulate these effects in-game to produce unique weapon characteristics for a mod using weapon_smg1.cpp. Below is the local recoil definitions specific to just the SMG1
void CWeaponSMG1::AddViewKick( void )
{
#define EASY_DAMPEN 1.5f
#define MAX_VERTICAL_KICK 17.5f //Degrees
#define SLIDE_LIMIT 0.11f //Seconds
//Get the view kick
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer == NULL )
return;
DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT );
}
However the 'DoMachineGunKick' function is what controls the "rumble" effect specically. In this case 'SLIDE_LIMIT' refers to a time variable, expressed in seconds. To understand how the first person rumble effect is carried out, we need to look at 'basehlcombatweapon.cpp'
Basehlcombatweapon.cpp
The method that HL2 uses to build the player camera rumble or "view punch" effect for weapons like the SMG1, is defined in this file. Let's take a closer look at how this is done.
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHLMachineGun::DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime )
{
#define KICK_MIN_X 0.2f //Degrees
#define KICK_MIN_Y 0.2f //Degrees
#define KICK_MIN_Z 0.1f //Degrees
QAngle vecScratch;
//Find how far into our accuracy degradation we are
float duration = ( fireDurationTime > slideLimitTime ) ? slideLimitTime : fireDurationTime;
float kickPerc = duration / slideLimitTime;
// do this to get a hard discontinuity, clear out anything under 10 degrees punch
pPlayer->ViewPunchReset( 10 );
//Apply this to the view angles as well
vecScratch.x = -( KICK_MIN_X + ( maxVerticleKickAngle * kickPerc ) );
vecScratch.y = -( KICK_MIN_Y + ( maxVerticleKickAngle * kickPerc ) ) / 3;
vecScratch.z = KICK_MIN_Z + ( maxVerticleKickAngle * kickPerc ) / 8;
//Wibble left and right
if ( random->RandomInt( -1, 1 ) >= 0 )
vecScratch.y *= -1;
//Wobble up and down
if ( random->RandomInt( -1, 1 ) >= 0 )
vecScratch.z *= -1;
//If we're in easy, dampen the effect a bit
if ( g_pGameRules->IsSkillLevel( SKILL_EASY ) )
{
for ( int i = 0; i < 3; i++ )
{
vecScratch[i] *= dampEasy;
}
}
//Clip this to our desired min/max
UTIL_ClipPunchAngleOffset( vecScratch, pPlayer->m_Local.m_vecPunchAngle, QAngle( 24.0f, 3.0f, 1.0f ) );
//Add it to the view punch
// NOTE: 0.5 is just tuned to match the old effect before the punch became simulated
pPlayer->ViewPunch( vecScratch * 0.5 );
}
In this example the code used is unmodified. If we examine the expression for 'KickPerc' we notice that the fire duration time or time with the trigger pressed, is continually counted against the player during the rumble process. At a glance this is perfectly logical, however if the player does not release the trigger for several seconds, the gun will begin to bounce awkwardly in the air. To adjust this we can modify the code like this:
void CHLMachineGun::DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime )
{
#define KICK_MIN_X 0.2f //Degrees
#define KICK_MIN_Y 0.2f //Degrees
#define KICK_MIN_Z 0.1f //Degrees
QAngle vecScratch;
//Find how far into our accuracy degradation we are
float duration = ( fireDurationTime > slideLimitTime ) ? slideLimitTime : fireDurationTime;
float kickPerc = duration / slideLimitTime; //old implementation
float kickPercy = slideLimitTime; //new system. delete this line and switch kickpercy back to kickperc in vecscratch lines below for old system
// do this to get a hard discontinuity, clear out anything under 10 degrees punch
pPlayer->ViewPunchReset( 30 ); // formerly 10
//Apply this to the view angles as well
vecScratch.x = -( KICK_MIN_X + ( maxVerticleKickAngle * kickPercy ) ) / ( 1 * dampEasy ); // is y axis or vertical.
vecScratch.y = -( KICK_MIN_Y + ( maxVerticleKickAngle * kickPercy ) ) / ( 2 * dampEasy ); // is really X axis or horizontal. remove / followed by dampeasy multiplier to return to old value
vecScratch.z = KICK_MIN_Z + ( maxVerticleKickAngle * kickPercy ) / 8;
//Wibble left and right
if ( random->RandomInt( -1, 1 ) >= 0 )
vecScratch.y *= -1;
//Wobble up and down
if ( random->RandomInt( -1, 1 ) >= 0 )
vecScratch.z *= -1;
//If we're in easy, dampen the effect a bit
if ( fireDurationTime >= .3 ) // not going to worry about this being switched back. dampeasy was only for easy difficulty in default hl2
{
for ( int i = 0; i < 3; i++ )
{
vecScratch[i] *= dampEasy;
}
}
//Clip this to our desired min/max
UTIL_ClipPunchAngleOffset( vecScratch, pPlayer->m_Local.m_vecPunchAngle, QAngle( 24.0f, 3.0f, 1.0f ) );
//Add it to the view punch
// NOTE: 0.5 is just tuned to match the old effect before the punch became simulated
pPlayer->ViewPunch( vecScratch * 0.5 );
}