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
m (→dlls) |
|||
Line 33: | Line 33: | ||
m_vOrigin = vec3_origin; | m_vOrigin = vec3_origin; | ||
m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; | m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; | ||
m_flEntryDensity = m_flLagCompensation = 0.0f; | |||
} | } | ||
Line 42: | Line 41: | ||
m_vOrigin = bullet.m_vecSrc; | m_vOrigin = bullet.m_vecSrc; | ||
m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; | m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; | ||
m_flEntryDensity = 0.0f; | |||
m_flLagCompensation = lagCompensation; | m_flLagCompensation = lagCompensation; | ||
m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE); | m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE); | ||
Line 60: | Line 59: | ||
float m_flBulletSpeed; | float m_flBulletSpeed; | ||
float m_flInitialBulletSpeed; | float m_flInitialBulletSpeed; | ||
float m_flLagCompensation; | float m_flLagCompensation; | ||
float m_flEntryDensity; | |||
CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist | CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist | ||
CTraceFilterSimpleList *m_pIgnoreList;//already hit | CTraceFilterSimpleList *m_pIgnoreList;//already hit | ||
CTraceFilterSkipTwoEntities *m_pTwoEnts; | CTraceFilterSkipTwoEntities *m_pTwoEnts; | ||
}; | }; | ||
class CBulletManager : public CBaseEntity | class CBulletManager : public CBaseEntity | ||
Line 81: | Line 79: | ||
void RemoveBullet(int index); | void RemoveBullet(int index); | ||
inline bool IsInWorld(int index); | inline bool IsInWorld(int index); | ||
};</pre> | };</pre> | ||
Line 121: | Line 118: | ||
{ | { | ||
#define CUR m_rgBullets[index] | #define CUR m_rgBullets[index] | ||
trace_t trace, trace_back; | trace_t trace, trace_back; | ||
Vector m_vOldOrigin(CUR.m_vOrigin); | Vector m_vOldOrigin(CUR.m_vOrigin); | ||
Vector m_vTraceStart(CUR.m_vOrigin); | Vector m_vTraceStart(CUR.m_vOrigin); | ||
DevMsg("BulletSpeed | DevMsg("BulletSpeed %f\n",CUR.m_flBulletSpeed); | ||
Vector vecNewRay = CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time; | |||
CUR.m_vOrigin += vecNewRay;//in/100th of a sec * 100th of a sec | |||
float | Vector vecEntryPosition; | ||
Vector vecExitPosition; | |||
float flPenetrationDistance = 0.0f; | |||
do | do | ||
{ | { | ||
UTIL_TraceLine( m_vTraceStart, CUR.m_vOrigin, MASK_SHOT, CUR.m_pTwoEnts, &trace ); | UTIL_TraceLine( m_vTraceStart, CUR.m_vOrigin, MASK_SHOT, CUR.m_pTwoEnts, &trace ); | ||
if(trace.allsolid) | if(trace.allsolid)//in solid | ||
{ | { | ||
trace.endpos = CUR.m_vOrigin; | trace.endpos = CUR.m_vOrigin; | ||
trace.fraction = 1.0f; | trace.fraction = 1.0f; | ||
CUR.m_flBulletSpeed -= CUR.m_flBulletSpeed * CUR.m_flEntryDensity / 1000.0f; | |||
break; | break; | ||
} | } | ||
else | else | ||
{ | { | ||
if(trace. | if(trace.startsolid)//exit solid | ||
{ | { | ||
// | //TODO: penetration surface impact stuff | ||
vecExitPosition = trace.fractionleftsolid * vecNewRay + m_vTraceStart; | |||
CUR.m_flBulletSpeed -= | flPenetrationDistance = vecEntryPosition.DistTo(vecExitPosition); | ||
CUR.m_flBulletSpeed -= flPenetrationDistance * CUR.m_flEntryDensity / 1000.0f; | |||
} | } | ||
else if(trace.fraction!=1.0f)//hit solid | |||
{ | { | ||
CUR.m_pIgnoreList->AddEntityToIgnore(trace.m_pEnt); | //TODO: surface impact stuff | ||
CUR.m_flEntryDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density; | |||
vecEntryPosition = trace.endpos; | |||
if(trace.DidHitNonWorldEntity()) | |||
{ | |||
if(CUR.m_pIgnoreList->ShouldHitEntity(trace.m_pEnt,MASK_SHOT)) | |||
{ | |||
CUR.m_pIgnoreList->AddEntityToIgnore(trace.m_pEnt); | |||
//TODO: entity impact stuff | |||
} | |||
} | |||
} | } | ||
} | } | ||
m_vTraceStart = trace.endpos + CUR.m_Bullet.m_vecDirShooting; | m_vTraceStart = trace.endpos + CUR.m_Bullet.m_vecDirShooting; | ||
}while(trace.endpos!=CUR.m_vOrigin); | }while(trace.endpos!=CUR.m_vOrigin&&CUR.m_flBulletSpeed>0.0f); | ||
#ifndef _DEBUG | #ifndef _DEBUG | ||
Line 226: | Line 225: | ||
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; | ||
}</pre> | }</pre> | ||
Revision as of 13:45, 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 line drawn along its path.Body
This code is WIP by ts2do
cl_dll
c_bullet_manager.cpp
#include "cbase.h" CON_COMMAND_F(createbullet,NULL,FCVAR_CHEAT) { engine->ServerCmd(VarArgs("createbullet_s %f\n",gpGlobals->curtime)); }
dlls
bullet_manager.h
#define MAX_BULLETS 256 #define BULLET_SPEED 240//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_flEntryDensity = m_flLagCompensation = 0.0f; } SimulatedBullet_t( FireBulletsInfo_t &bullet, float lagCompensation ) { m_Bullet = bullet; m_vOrigin = bullet.m_vecSrc; m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; m_flEntryDensity = 0.0f; m_flLagCompensation = lagCompensation; m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE); m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAttacker); m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAdditionalIgnoreEnt); m_pTwoEnts = new CTraceFilterSkipTwoEntities(m_Bullet.m_pAttacker,m_Bullet.m_pAdditionalIgnoreEnt,COLLISION_GROUP_NONE); } void DeletePointers(void) { //leave the compensation considerations because they're entities delete m_pIgnoreList; delete m_pTwoEnts; } FireBulletsInfo_t m_Bullet; Vector m_vOrigin; float m_flBulletSpeed; float m_flInitialBulletSpeed; float m_flLagCompensation; float m_flEntryDensity; CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist CTraceFilterSimpleList *m_pIgnoreList;//already hit CTraceFilterSkipTwoEntities *m_pTwoEnts; }; 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 SimulateBullet(int index, float time=1.0f); void AddBullet(FireBulletsInfo_t &bullet, float lagCompensation); void RemoveBullet(int index); inline 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==gpGlobals->curtime) 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] trace_t trace, trace_back; Vector m_vOldOrigin(CUR.m_vOrigin); Vector m_vTraceStart(CUR.m_vOrigin); DevMsg("BulletSpeed %f\n",CUR.m_flBulletSpeed); Vector vecNewRay = CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time; CUR.m_vOrigin += vecNewRay;//in/100th of a sec * 100th of a sec Vector vecEntryPosition; Vector vecExitPosition; float flPenetrationDistance = 0.0f; do { UTIL_TraceLine( m_vTraceStart, CUR.m_vOrigin, MASK_SHOT, CUR.m_pTwoEnts, &trace ); if(trace.allsolid)//in solid { trace.endpos = CUR.m_vOrigin; trace.fraction = 1.0f; CUR.m_flBulletSpeed -= CUR.m_flBulletSpeed * CUR.m_flEntryDensity / 1000.0f; break; } else { if(trace.startsolid)//exit solid { //TODO: penetration surface impact stuff vecExitPosition = trace.fractionleftsolid * vecNewRay + m_vTraceStart; flPenetrationDistance = vecEntryPosition.DistTo(vecExitPosition); CUR.m_flBulletSpeed -= flPenetrationDistance * CUR.m_flEntryDensity / 1000.0f; } else if(trace.fraction!=1.0f)//hit solid { //TODO: surface impact stuff CUR.m_flEntryDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density; vecEntryPosition = trace.endpos; if(trace.DidHitNonWorldEntity()) { if(CUR.m_pIgnoreList->ShouldHitEntity(trace.m_pEnt,MASK_SHOT)) { CUR.m_pIgnoreList->AddEntityToIgnore(trace.m_pEnt); //TODO: entity impact stuff } } } } m_vTraceStart = trace.endpos + CUR.m_Bullet.m_vecDirShooting; }while(trace.endpos!=CUR.m_vOrigin&&CUR.m_flBulletSpeed>0.0f); #ifndef _DEBUG if(g_debug_bullets.GetBool()) #endif { NDebugOverlay::Line(m_vOldOrigin,CUR.m_vOrigin, 255, 255, 255, true, 10.0f ); NDebugOverlay::Cross3D(CUR.m_vOrigin, 16, 255, 0, 0, true, 10.0f); } 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) { 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].DeletePointers(); m_rgBullets[index] = m_rgBullets[m_iLastBullet]; DevMsg("Bullet #%i Destroyed; Replacing with #%i\n",index, m_iLastBullet); } else { m_rgBullets[index].DeletePointers(); 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; }
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 ); |