Battlefield Style Hitmarker: Difference between revisions
Thunder4ik (talk | contribs) |
|||
(7 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
{{ | {{Multiple issues| | ||
{{Dead end|date=January 2024}} | |||
{{Orphan|date=January 2024}} | |||
}} | |||
=The Goal= | == The Goal == | ||
We are going to re-create Battlefield-style hitmarkers for all weapons, complete with animation. This is going to be a very simple copy & paste tutorial, and there is room for contribution and tweaks. | |||
{{Note|This has been tested on the 2013 and 2007 SDKs.}} | |||
=The Code= | == The Code == | ||
Create the following files and place them inside your client (C:\MOD\src\game\client). Add them to the Client project in the game solution. | |||
=== hud_hitmarker.h === | |||
<source lang=cpp> | |||
== | //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// | ||
< | |||
// | // | ||
// Purpose: Hit Marker | // Purpose: Hit Marker | ||
Line 34: | Line 38: | ||
protected: | protected: | ||
virtual void ApplySchemeSettings(vgui::IScheme *scheme); | virtual void ApplySchemeSettings( vgui::IScheme *scheme ); | ||
virtual void Paint( void ); | virtual void Paint( void ); | ||
Line 41: | Line 45: | ||
CPanelAnimationVar( Color, m_HitmarkerColor, "HitMarkerColor", "255 255 255 255" ); | CPanelAnimationVar( Color, m_HitmarkerColor, "HitMarkerColor", "255 255 255 255" ); | ||
};</ | }; | ||
</source> | |||
== | === hud_hitmarker.cpp === | ||
< | <source lang=cpp> | ||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// | |||
// | // | ||
// Purpose: Hit Marker | // Purpose: Hit Marker | ||
Line 73: | Line 79: | ||
// Purpose: Constructor | // Purpose: Constructor | ||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
CHudHitmarker::CHudHitmarker( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "HudHitmarker" ) | |||
CHudHitmarker::CHudHitmarker( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, "HudHitmarker") | |||
{ | { | ||
vgui::Panel *pParent = g_pClientMode->GetViewport(); | vgui::Panel *pParent = g_pClientMode->GetViewport(); | ||
Line 83: | Line 88: | ||
int screenWide, screenTall; | int screenWide, screenTall; | ||
GetHudSize(screenWide, screenTall); | GetHudSize( screenWide, screenTall ); | ||
SetBounds(0, 0, screenWide, screenTall); | SetBounds( 0, 0, screenWide, screenTall ); | ||
} | } | ||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
// Purpose: | // Purpose: | ||
Line 106: | Line 111: | ||
m_bHitmarkerShow = false; | m_bHitmarkerShow = false; | ||
} | } | ||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
// Purpose: | // Purpose: | ||
Line 112: | Line 117: | ||
void CHudHitmarker::ApplySchemeSettings( vgui::IScheme *scheme ) | void CHudHitmarker::ApplySchemeSettings( vgui::IScheme *scheme ) | ||
{ | { | ||
BaseClass::ApplySchemeSettings(scheme); | BaseClass::ApplySchemeSettings( scheme ); | ||
SetPaintBackgroundEnabled(false); | SetPaintBackgroundEnabled( false ); | ||
SetPaintBorderEnabled(false); | SetPaintBorderEnabled( false ); | ||
} | } | ||
Line 125: | Line 130: | ||
return ( m_bHitmarkerShow && CHudElement::ShouldDraw() ); | return ( m_bHitmarkerShow && CHudElement::ShouldDraw() ); | ||
} | } | ||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
// Purpose: | // Purpose: | ||
Line 131: | Line 136: | ||
void CHudHitmarker::Paint( void ) | void CHudHitmarker::Paint( void ) | ||
{ | { | ||
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); | |||
if ( !pPlayer ) | |||
return; | |||
if ( m_bHitmarkerShow ) | |||
if (m_bHitmarkerShow) | |||
{ | { | ||
int | int x, y; | ||
// Find our screen position to start from | // Find our screen position to start from | ||
x = XRES(320); | x = XRES( 320 ); | ||
y = YRES(240); | y = YRES( 240 ); | ||
vgui::surface()->DrawSetColor( m_HitmarkerColor ); | vgui::surface()->DrawSetColor( m_HitmarkerColor ); | ||
Line 152: | Line 155: | ||
} | } | ||
} | } | ||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
// Purpose: | // Purpose: | ||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
void CHudHitmarker::MsgFunc_ShowHitmarker(bf_read &msg) | void CHudHitmarker::MsgFunc_ShowHitmarker( bf_read &msg ) | ||
{ | { | ||
m_bHitmarkerShow = msg.ReadByte(); | m_bHitmarkerShow = msg.ReadByte(); | ||
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("HitMarkerShow"); | g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HitMarkerShow" ); | ||
}</ | } | ||
</source> | |||
== | == Implementation == | ||
Once compiled, we now have the hud element created. The next step is to call it in the code, for this we will add a '''usermessage''' that gets called when a weapon fires and successfully hits an enemy. | |||
Depending on your mod base this will be hl2_usermessages.cpp or sdk_usermessages.cpp. | === <base>_usermessages.cpp === | ||
Depending on your mod base this will be '''hl2_usermessages.cpp''' or '''sdk_usermessages.cpp'''. | |||
Add the following line to bottom of the file | Add the following line to bottom of the file | ||
< | <source lang=cpp> | ||
usermessages->Register( "ShowHitmarker", 1 ); // Show hit marker | |||
</source> | |||
== | === weapon_<name>.cpp === | ||
For this example I will be using the '''weapon_pistol.cpp''', you will have to add this to all weapons used in your mod. The reasoning for this in my case is not all weapons use the <code>MAX_TRACE_LENGTH</code>, and I need to set this accordingly per weapon, else you could add this in the <code>Baseclass::PrimaryAttack</code>. | |||
For this example I will be using the weapon_pistol.cpp, you will have to add this to all weapons used in your mod. The reasoning for this in my case is not all weapons use the <code>MAX_TRACE_LENGTH</code>, and I need to set this accordingly per weapon, else you could add this in the Baseclass:PrimaryAttack. | |||
Declare the following underneath the rest of the function declarations near the top of the file. | Declare the following underneath the rest of the function declarations near the top of the file. | ||
< | <source lang=cpp> | ||
void DrawHitmarker( void ); | |||
</source> | |||
Implement the function you just declared as follows. | Implement the function you just declared as follows. | ||
< | <source lang=cpp> | ||
void CWeaponPistol::DrawHitmarker( void ) | |||
{ | { | ||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); | CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); | ||
if ( pPlayer == NULL ) | if ( pPlayer == NULL ) | ||
return; | |||
#ifndef CLIENT_DLL | |||
CSingleUserRecipientFilter filter(pPlayer); | CSingleUserRecipientFilter filter( pPlayer ); | ||
UserMessageBegin( filter, "ShowHitmarker" ); | |||
WRITE_BYTE( 1 ); | |||
MessageEnd(); | |||
#endif | |||
}</ | } | ||
</source> | |||
Inside your weapon file find the ::PrimaryAttack function and add under <code>CBasePlayer *pOwner = ToBasePlayer( GetOwner() );</code> add: | Inside your weapon file find the <code>::PrimaryAttack</code> function and add under <code>CBasePlayer *pOwner = ToBasePlayer( GetOwner() );</code> add: | ||
<source lang=cpp> | |||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); | |||
if ( !pPlayer ) | |||
return; | |||
</source> | |||
Finally, add this snippet at the bottom of the <code>::PrimaryAttack</code> function: | |||
<source lang=cpp> | |||
// Set up the vectors and traceline | |||
Finally, add this snippet at the bottom of the ::PrimaryAttack function | |||
< | |||
trace_t tr; | trace_t tr; | ||
Vector vecStart, vecStop, vecDir; | Vector vecStart, vecStop, vecDir; | ||
// | // Get the angles | ||
AngleVectors( pPlayer->EyeAngles( ), &vecDir ); | AngleVectors( pPlayer->EyeAngles(), &vecDir ); | ||
// | // Get the vectors | ||
vecStart = pPlayer->Weapon_ShootPosition(); | vecStart = pPlayer->Weapon_ShootPosition(); | ||
vecStop = vecStart + vecDir * MAX_TRACE_LENGTH; | vecStop = vecStart + vecDir * MAX_TRACE_LENGTH; | ||
// | // Do the TraceLine | ||
UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr ); | UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr ); | ||
// | // Check to see if we hit a player | ||
if ( tr.m_pEnt ) | if ( tr.m_pEnt ) | ||
{ | |||
if ( tr.m_pEnt->GetTeamNumber() != pPlayer->GetTeamNumber() ) | |||
{ | { | ||
if ( tr.m_pEnt->IsPlayer() ) | if ( tr.m_pEnt->IsPlayer() ) | ||
Line 229: | Line 239: | ||
} | } | ||
} | } | ||
}</ | } | ||
</source> | |||
==Animation== | == Animation == | ||
Open up your '''hudanimations.txt''' file and add the following to the bottom of the file. | |||
<pre> | |||
event HitMarkerShow | |||
{ | { | ||
Animate HudHitmarker Alpha 255 Linear 0.2 0.4 | Animate HudHitmarker Alpha 255 Linear 0.2 0.4 | ||
Line 245: | Line 256: | ||
{ | { | ||
Animate HudHitmarker Alpha 0 Linear 0.2 0.4 | Animate HudHitmarker Alpha 0 Linear 0.2 0.4 | ||
}</pre> | } | ||
</pre> | |||
You can edit the values here to achieve the look and feel you like, but I feel these settings emulate Battlefield's style nicely. | |||
For single player mods, you'll find you need to edit a line within the ::PrimaryAttack code we added, rather than detecting players we need to detect | == Finishing Up == | ||
=== Single Player Tweaks === | |||
For single player mods, you'll find you need to edit a line within the <code>::PrimaryAttack</code> code we added, rather than detecting players we need to detect NPCs. | |||
Simply change the following: | Simply change the following (or if you want both just add it): | ||
<source lang=cpp> | |||
< | // Check to see if we hit a player | ||
if ( tr.m_pEnt ) | if ( tr.m_pEnt ) | ||
{ | |||
if ( tr.m_pEnt->IsPlayer() ) | |||
{ | { | ||
DrawHitmarker(); | |||
} | |||
} | |||
</source> | |||
to this: | to this: | ||
<source lang=cpp> | |||
< | // Check to see if we hit an NPC | ||
if ( tr.m_pEnt ) | if ( tr.m_pEnt ) | ||
{ | |||
if ( tr.m_pEnt->IsNPC() ) | |||
{ | { | ||
if ( tr.m_pEnt-> | #ifndef CLIENT_DLL | ||
// Light Kill: Draw ONLY if we hit an enemy NPC | |||
if ( pPlayer->GetDefaultRelationshipDisposition( tr.m_pEnt->Classify() ) != D_HT ) | |||
{ | |||
//DevMsg( "Neutral NPC!\n" ); | |||
} | |||
else | |||
{ | { | ||
DrawHitmarker(); | DrawHitmarker(); | ||
} | } | ||
}</ | #endif | ||
} | |||
} | |||
</source> | |||
In single player the hitmarker will draw only if we hit an ENEMY NPC, it will ignore NPCs who change their behavior against the player. | |||
There is a lot of room for tweaking, for instance this will currently show when you hit any | '''Example:''' We have custom NPC who are friendly at the start, if you make him hate the player by the script the hitmarker will not show when you kill 'em. Works great on default HL2 NPCs. | ||
== Conclusion == | |||
So there we go, a very basic but fully functional Battlefield-style hitmaker. | |||
There is a lot of room for tweaking, for instance, this will currently show when you hit any players/NPCs regardless of their Team or AI Relationship. You could think about adding in functionality to differentiate between friendly and enemy players/NPCs and display different properties accordingly. | |||
Enjoy, | Enjoy, | ||
Ferrety | Ferrety | ||
ferrety6012@gmail.com | (ferrety6012@gmail.com) | ||
[[Category:Tutorials]] |
Latest revision as of 08:49, 21 January 2024




January 2024

You can help by

January 2024
The Goal
We are going to re-create Battlefield-style hitmarkers for all weapons, complete with animation. This is going to be a very simple copy & paste tutorial, and there is room for contribution and tweaks.

The Code
Create the following files and place them inside your client (C:\MOD\src\game\client). Add them to the Client project in the game solution.
hud_hitmarker.h
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Hit Marker
//
//=============================================================================//
#include "hudelement.h"
#include "hud_numericdisplay.h"
#include <vgui_controls/Panel.h>
class CHudHitmarker : public vgui::Panel, public CHudElement
{
DECLARE_CLASS_SIMPLE( CHudHitmarker, vgui::Panel );
public:
CHudHitmarker( const char *pElementName );
void Init();
void Reset();
bool ShouldDraw();
void MsgFunc_ShowHitmarker( bf_read &msg );
protected:
virtual void ApplySchemeSettings( vgui::IScheme *scheme );
virtual void Paint( void );
private:
bool m_bHitmarkerShow;
CPanelAnimationVar( Color, m_HitmarkerColor, "HitMarkerColor", "255 255 255 255" );
};
hud_hitmarker.cpp
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Hit Marker
//
//=============================================================================//
#include "cbase.h"
#include "hudelement.h"
#include "hud_macros.h"
#include "hud_hitmarker.h"
#include "iclientmode.h"
#include "c_baseplayer.h"
// VGUI panel includes
#include <vgui_controls/AnimationController.h>
#include <vgui/ISurface.h>
#include <vgui/ILocalize.h>
using namespace vgui;
// memdbgon must be the last include file in a .cpp file!
#include "tier0/memdbgon.h"
DECLARE_HUDELEMENT( CHudHitmarker );
DECLARE_HUD_MESSAGE( CHudHitmarker, ShowHitmarker );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudHitmarker::CHudHitmarker( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "HudHitmarker" )
{
vgui::Panel *pParent = g_pClientMode->GetViewport();
SetParent( pParent );
// Hitmarker will not show when the player is dead
SetHiddenBits( HIDEHUD_PLAYERDEAD );
int screenWide, screenTall;
GetHudSize( screenWide, screenTall );
SetBounds( 0, 0, screenWide, screenTall );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHitmarker::Init()
{
HOOK_HUD_MESSAGE( CHudHitmarker, ShowHitmarker );
SetAlpha( 0 );
m_bHitmarkerShow = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHitmarker::Reset()
{
SetAlpha( 0 );
m_bHitmarkerShow = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHitmarker::ApplySchemeSettings( vgui::IScheme *scheme )
{
BaseClass::ApplySchemeSettings( scheme );
SetPaintBackgroundEnabled( false );
SetPaintBorderEnabled( false );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CHudHitmarker::ShouldDraw( void )
{
return ( m_bHitmarkerShow && CHudElement::ShouldDraw() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHitmarker::Paint( void )
{
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
if ( !pPlayer )
return;
if ( m_bHitmarkerShow )
{
int x, y;
// Find our screen position to start from
x = XRES( 320 );
y = YRES( 240 );
vgui::surface()->DrawSetColor( m_HitmarkerColor );
vgui::surface()->DrawLine( x - 6, y - 5, x - 11, y - 10 );
vgui::surface()->DrawLine( x + 5, y - 5, x + 10, y - 10 );
vgui::surface()->DrawLine( x - 6, y + 5, x - 11, y + 10 );
vgui::surface()->DrawLine( x + 5, y + 5, x + 10, y + 10 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudHitmarker::MsgFunc_ShowHitmarker( bf_read &msg )
{
m_bHitmarkerShow = msg.ReadByte();
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HitMarkerShow" );
}
Implementation
Once compiled, we now have the hud element created. The next step is to call it in the code, for this we will add a usermessage that gets called when a weapon fires and successfully hits an enemy.
<base>_usermessages.cpp
Depending on your mod base this will be hl2_usermessages.cpp or sdk_usermessages.cpp.
Add the following line to bottom of the file
usermessages->Register( "ShowHitmarker", 1 ); // Show hit marker
weapon_<name>.cpp
For this example I will be using the weapon_pistol.cpp, you will have to add this to all weapons used in your mod. The reasoning for this in my case is not all weapons use the MAX_TRACE_LENGTH
, and I need to set this accordingly per weapon, else you could add this in the Baseclass::PrimaryAttack
.
Declare the following underneath the rest of the function declarations near the top of the file.
void DrawHitmarker( void );
Implement the function you just declared as follows.
void CWeaponPistol::DrawHitmarker( void )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer == NULL )
return;
#ifndef CLIENT_DLL
CSingleUserRecipientFilter filter( pPlayer );
UserMessageBegin( filter, "ShowHitmarker" );
WRITE_BYTE( 1 );
MessageEnd();
#endif
}
Inside your weapon file find the ::PrimaryAttack
function and add under CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
add:
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer )
return;
Finally, add this snippet at the bottom of the ::PrimaryAttack
function:
// Set up the vectors and traceline
trace_t tr;
Vector vecStart, vecStop, vecDir;
// Get the angles
AngleVectors( pPlayer->EyeAngles(), &vecDir );
// Get the vectors
vecStart = pPlayer->Weapon_ShootPosition();
vecStop = vecStart + vecDir * MAX_TRACE_LENGTH;
// Do the TraceLine
UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );
// Check to see if we hit a player
if ( tr.m_pEnt )
{
if ( tr.m_pEnt->GetTeamNumber() != pPlayer->GetTeamNumber() )
{
if ( tr.m_pEnt->IsPlayer() )
{
DrawHitmarker();
}
}
}
Animation
Open up your hudanimations.txt file and add the following to the bottom of the file.
event HitMarkerShow { Animate HudHitmarker Alpha 255 Linear 0.2 0.4 RunEvent HitMarkerHide 0.4 } event HitMarkerHide { Animate HudHitmarker Alpha 0 Linear 0.2 0.4 }
You can edit the values here to achieve the look and feel you like, but I feel these settings emulate Battlefield's style nicely.
Finishing Up
Single Player Tweaks
For single player mods, you'll find you need to edit a line within the ::PrimaryAttack
code we added, rather than detecting players we need to detect NPCs.
Simply change the following (or if you want both just add it):
// Check to see if we hit a player
if ( tr.m_pEnt )
{
if ( tr.m_pEnt->IsPlayer() )
{
DrawHitmarker();
}
}
to this:
// Check to see if we hit an NPC
if ( tr.m_pEnt )
{
if ( tr.m_pEnt->IsNPC() )
{
#ifndef CLIENT_DLL
// Light Kill: Draw ONLY if we hit an enemy NPC
if ( pPlayer->GetDefaultRelationshipDisposition( tr.m_pEnt->Classify() ) != D_HT )
{
//DevMsg( "Neutral NPC!\n" );
}
else
{
DrawHitmarker();
}
#endif
}
}
In single player the hitmarker will draw only if we hit an ENEMY NPC, it will ignore NPCs who change their behavior against the player.
Example: We have custom NPC who are friendly at the start, if you make him hate the player by the script the hitmarker will not show when you kill 'em. Works great on default HL2 NPCs.
Conclusion
So there we go, a very basic but fully functional Battlefield-style hitmaker.
There is a lot of room for tweaking, for instance, this will currently show when you hit any players/NPCs regardless of their Team or AI Relationship. You could think about adding in functionality to differentiate between friendly and enemy players/NPCs and display different properties accordingly.
Enjoy,
Ferrety (ferrety6012@gmail.com)