Simulated Bullets: Difference between revisions
Jump to navigation
Jump to search
Note:This code is a work in progress. Feel free to make helpful changes. To test the code, you can bind a key to
Line 72: | Line 72: | ||
m_iLastBullet = -1; | m_iLastBullet = -1; | ||
} | } | ||
CON_COMMAND_F( | CON_COMMAND_F(createbullet_s,NULL,FCVAR_CHEAT) | ||
{ | { | ||
Assert(BulletManager()); | Assert(BulletManager()); | ||
Line 78: | Line 78: | ||
return; | return; | ||
CBasePlayer *pPlayer = UTIL_GetCommandClient(); | CBasePlayer *pPlayer = UTIL_GetCommandClient(); | ||
if(!pPlayer) | |||
return; | |||
Vector dir; | Vector dir; | ||
pPlayer->EyeVectors(&dir); | pPlayer->EyeVectors(&dir); | ||
FireBulletsInfo_t info(1,pPlayer->EyePosition(),dir,vec3_origin,0,0); | FireBulletsInfo_t info(1,pPlayer->EyePosition(),dir,vec3_origin,0,0); | ||
info.m_pAttacker = pPlayer; | info.m_pAttacker = pPlayer; | ||
BulletManager()->AddBullet(info); | float lag = gpGlobals->curtime - atof(engine->Cmd_Argv(1)); | ||
if(lag<0.0f) | |||
lag=0.0f; | |||
BulletManager()->AddBullet(info,lag); | |||
} | } | ||
void CBulletManager::Spawn(void) | void CBulletManager::Spawn(void) | ||
{ | { | ||
Think(); | |||
} | } | ||
void CBulletManager:: | void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/) | ||
{ | { | ||
#define CUR m_rgBullets[index] | |||
CUR.m_flBulletTime += time; | |||
Vector m_vOldOrigin = | Vector m_vOldOrigin = CUR.m_vOrigin; | ||
DevMsg("BulletSpeed %f LastDensity %f\n", | DevMsg("BulletSpeed %f LastDensity %f\n",CUR.m_flBulletSpeed, CUR.m_flLastDensity); | ||
CUR.m_vOrigin += CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time;//in/100th of a sec * 100th of a sec | |||
CTraceFilterSkipTwoEntities traceFilter( | CTraceFilterSkipTwoEntities traceFilter( CUR.m_Bullet.m_pAttacker, CUR.m_Bullet.m_pAdditionalIgnoreEnt, COLLISION_GROUP_NONE ); | ||
trace_t | trace_t trace; | ||
UTIL_TraceLine( m_vOldOrigin, | UTIL_TraceLine( m_vOldOrigin, CUR.m_vOrigin, MASK_SHOT, &traceFilter, &trace ); | ||
if( | if(trace.m_pEnt) | ||
{ | { | ||
if(!( | if(!(CUR.m_pHit).Find(trace.m_pEnt)) | ||
{ | { | ||
} | } | ||
} | } | ||
if( | if(CUR.m_flLastDensity!=0.0f) | ||
{ | { | ||
if(trace.allsolid) | |||
{ | |||
CUR.m_flBulletSpeed -= GetSpeedMod(index); | |||
} | |||
else if(trace.surface.name[0]!='*'&&!(trace.surface.flags & SURF_SKY)) | |||
{//make sure we didnt hit an invalid (nodraw) or sky material | |||
if(trace.startsolid) | |||
{ | |||
CUR.m_flBulletSpeed -= trace.fractionleftsolid * GetSpeedMod(index); | |||
CUR.m_flLastDensity = 0.0f; | |||
} | |||
if( trace.fraction != 1.0f) | |||
{ | |||
CUR.m_flLastDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density; | |||
} | |||
} | |||
} | } | ||
#ifndef _DEBUG | #ifndef _DEBUG | ||
Line 130: | Line 134: | ||
#endif | #endif | ||
{ | { | ||
NDebugOverlay::Line(m_vOldOrigin, | NDebugOverlay::Line(m_vOldOrigin,CUR.m_vOrigin, 255, 255, 255, true, 0.25f ); | ||
} | } | ||
if( | if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index)) | ||
RemoveBullet(x); | RemoveBullet(index); | ||
#undef CUR | |||
} | |||
void CBulletManager::Think(void) | |||
{ | |||
for(int x=0;x<=m_iLastBullet;x++) | |||
{ | |||
SimulateBullet(x); | |||
} | } | ||
SetNextThink( gpGlobals->curtime + 0.01f ); | SetNextThink( gpGlobals->curtime + 0.01f ); | ||
} | } | ||
void CBulletManager::AddBullet(FireBulletsInfo_t &bullet) | void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation) | ||
{ | { | ||
if(m_iLastBullet==MAX_BULLETS) | lagCompensation+=0.1f; | ||
{ | if(m_iLastBullet==MAX_BULLETS-1)//256 simultaneous bullets in the air is mayhem | ||
Assert(m_iLastBullet!=MAX_BULLETS); | {//let's do something reckless on bullet mayhem | ||
Warning("Bullet queue filled\n"); | Assert(m_iLastBullet!=MAX_BULLETS-1); | ||
Warning("Bullet queue filled (Removing bullet #0)\n"); | |||
RemoveBullet(0); | |||
} | } | ||
m_iLastBullet++; | else | ||
DevMsg("Bullet Created (%i)\n",m_iLastBullet); | m_iLastBullet++; | ||
m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet); | DevMsg("Bullet Created (%i) LagCompensation %f\n",m_iLastBullet,lagCompensation); | ||
m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet,lagCompensation); | |||
if(lagCompensation!=0.0f) | |||
SimulateBullet(m_iLastBullet, lagCompensation*100); | |||
} | } | ||
void CBulletManager::RemoveBullet(int index) | void CBulletManager::RemoveBullet(int index) | ||
{ | { | ||
DevMsg("Bullet Destroyed | if(m_iLastBullet!=index) | ||
{ | |||
m_rgBullets[index] = m_rgBullets[m_iLastBullet]; | |||
DevMsg("Bullet #%i Destroyed; Replacing with #%i\n",index, m_iLastBullet); | |||
} | |||
else | |||
DevMsg("Bullet #%i Destroyed\n",index); | |||
m_rgBullets[m_iLastBullet] = SimulatedBullet_t(); | m_rgBullets[m_iLastBullet] = SimulatedBullet_t(); | ||
m_iLastBullet--; | m_iLastBullet--; | ||
} | } | ||
bool CBulletManager::IsInWorld(int index) | inline bool CBulletManager::IsInWorld(int index) | ||
{ | { | ||
if (m_rgBullets[index].m_vOrigin.x >= MAX_COORD_INTEGER) return false; | if (m_rgBullets[index].m_vOrigin.x >= MAX_COORD_INTEGER) return false; | ||
Line 166: | Line 186: | ||
if (m_rgBullets[index].m_vOrigin.z <= MIN_COORD_INTEGER) return false; | if (m_rgBullets[index].m_vOrigin.z <= MIN_COORD_INTEGER) return false; | ||
return true; | return true; | ||
} | |||
inline float CBulletManager::GetSpeedMod(int index) | |||
{ | |||
return fabs(100.0f-m_rgBullets[index].m_flLastDensity) / 100.0f; | |||
}</pre> | }</pre> | ||
Revision as of 00:01, 21 November 2005
Preface
Basically, with simulated bullets, the aspects of physics are going to tried to be captured by simulating them in batch simulation code. So far the code is all server-side and is later expected to be client-side simulated with similar code.

createbullet
ingame and that will fire a bullet from your view with a parabola drawn along its path.Body
This code is WIP by ts2do
bullet_manager.h
#define MAX_BULLETS 256 #define BULLET_SPEED 30//inches per hundredths of a second class CBulletManager; extern CBulletManager *g_pBulletManager; inline CBulletManager *BulletManager() { return g_pBulletManager; } extern ConVar g_debug_bullets; struct SimulatedBullet_t { SimulatedBullet_t() { m_Bullet = FireBulletsInfo_t(); m_vOrigin = vec3_origin; m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; m_flCreationTime = gpGlobals->curtime; } SimulatedBullet_t( FireBulletsInfo_t &bullet ) { m_Bullet = bullet; m_vOrigin = bullet.m_vecSrc; m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; m_flCreationTime = gpGlobals->curtime; } FireBulletsInfo_t m_Bullet; Vector m_vOrigin; float m_flBulletSpeed; float m_flInitialBulletSpeed; float m_flCreationTime; CUtlVector<CBaseEntity *> m_pHit; float m_flLastDensity; }; class CBulletManager : public CBaseEntity { DECLARE_CLASS( CBulletManager, CBaseEntity ); public: SimulatedBullet_t m_rgBullets[MAX_BULLETS]; int m_iLastBullet; CBulletManager(); void Spawn(void); void Think(void); void AddBullet(FireBulletsInfo_t &bullet); void RemoveBullet(int index); bool IsInWorld(int index); };
bullet_manager.cpp
#include "cbase.h" #include "movevars_shared.h" #include "util_shared.h" #include "bullet_manager.h" ConVar g_debug_bullets( "g_debug_bullets", "0", FCVAR_CHEAT ); CBulletManager *g_pBulletManager; LINK_ENTITY_TO_CLASS( bullet_manager, CBulletManager ); CBulletManager::CBulletManager() { Q_memset( m_rgBullets, 0, MAX_BULLETS ); m_iLastBullet = -1; } CON_COMMAND_F(createbullet_s,NULL,FCVAR_CHEAT) { Assert(BulletManager()); if(!BulletManager()) return; CBasePlayer *pPlayer = UTIL_GetCommandClient(); if(!pPlayer) return; Vector dir; pPlayer->EyeVectors(&dir); FireBulletsInfo_t info(1,pPlayer->EyePosition(),dir,vec3_origin,0,0); info.m_pAttacker = pPlayer; float lag = gpGlobals->curtime - atof(engine->Cmd_Argv(1)); if(lag<0.0f) lag=0.0f; BulletManager()->AddBullet(info,lag); } void CBulletManager::Spawn(void) { Think(); } void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/) { #define CUR m_rgBullets[index] CUR.m_flBulletTime += time; Vector m_vOldOrigin = CUR.m_vOrigin; DevMsg("BulletSpeed %f LastDensity %f\n",CUR.m_flBulletSpeed, CUR.m_flLastDensity); CUR.m_vOrigin += CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time;//in/100th of a sec * 100th of a sec CTraceFilterSkipTwoEntities traceFilter( CUR.m_Bullet.m_pAttacker, CUR.m_Bullet.m_pAdditionalIgnoreEnt, COLLISION_GROUP_NONE ); trace_t trace; UTIL_TraceLine( m_vOldOrigin, CUR.m_vOrigin, MASK_SHOT, &traceFilter, &trace ); if(trace.m_pEnt) { if(!(CUR.m_pHit).Find(trace.m_pEnt)) { } } if(CUR.m_flLastDensity!=0.0f) { if(trace.allsolid) { CUR.m_flBulletSpeed -= GetSpeedMod(index); } else if(trace.surface.name[0]!='*'&&!(trace.surface.flags & SURF_SKY)) {//make sure we didnt hit an invalid (nodraw) or sky material if(trace.startsolid) { CUR.m_flBulletSpeed -= trace.fractionleftsolid * GetSpeedMod(index); CUR.m_flLastDensity = 0.0f; } if( trace.fraction != 1.0f) { CUR.m_flLastDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density; } } } #ifndef _DEBUG if(g_debug_bullets.GetBool()) #endif { NDebugOverlay::Line(m_vOldOrigin,CUR.m_vOrigin, 255, 255, 255, true, 0.25f ); } if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index)) RemoveBullet(index); #undef CUR } void CBulletManager::Think(void) { for(int x=0;x<=m_iLastBullet;x++) { SimulateBullet(x); } SetNextThink( gpGlobals->curtime + 0.01f ); } void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation) { lagCompensation+=0.1f; if(m_iLastBullet==MAX_BULLETS-1)//256 simultaneous bullets in the air is mayhem {//let's do something reckless on bullet mayhem Assert(m_iLastBullet!=MAX_BULLETS-1); Warning("Bullet queue filled (Removing bullet #0)\n"); RemoveBullet(0); } else m_iLastBullet++; DevMsg("Bullet Created (%i) LagCompensation %f\n",m_iLastBullet,lagCompensation); m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet,lagCompensation); if(lagCompensation!=0.0f) SimulateBullet(m_iLastBullet, lagCompensation*100); } void CBulletManager::RemoveBullet(int index) { if(m_iLastBullet!=index) { m_rgBullets[index] = m_rgBullets[m_iLastBullet]; DevMsg("Bullet #%i Destroyed; Replacing with #%i\n",index, m_iLastBullet); } else DevMsg("Bullet #%i Destroyed\n",index); m_rgBullets[m_iLastBullet] = SimulatedBullet_t(); m_iLastBullet--; } inline bool CBulletManager::IsInWorld(int index) { if (m_rgBullets[index].m_vOrigin.x >= MAX_COORD_INTEGER) return false; if (m_rgBullets[index].m_vOrigin.y >= MAX_COORD_INTEGER) return false; if (m_rgBullets[index].m_vOrigin.z >= MAX_COORD_INTEGER) return false; if (m_rgBullets[index].m_vOrigin.x <= MIN_COORD_INTEGER) return false; if (m_rgBullets[index].m_vOrigin.y <= MIN_COORD_INTEGER) return false; if (m_rgBullets[index].m_vOrigin.z <= MIN_COORD_INTEGER) return false; return true; } inline float CBulletManager::GetSpeedMod(int index) { return fabs(100.0f-m_rgBullets[index].m_flLastDensity) / 100.0f; }
Implementation
bullet_manager entity
gamerules.cpp
Add | #include "bullet_manager.h" |
After | #include "filesystem.h" |
Add | g_pBulletManager = (CBulletManager*)CBaseEntity::Create( "bullet_manager", vec3_origin, vec3_angle ); |
After | g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "player_manager", vec3_origin, vec3_angle ); |