Game Development Community

Thinking outside the hitbox

by Azaezel · in Torque 3D Professional · 01/31/2014 (2:11 am) · 2 replies

So we've got a fairly primitive critical damage system cooked up for our present game. (Simple leg, torso and head multipliers using the already existing getDamageLocation().) And are looking at lightweight notions for making that a bit more artist-friendly. (No, nuking the hitbox code from orbit doesn't fall under the category of lightwieght tweaking. Though I'm sure it'd be satisfying.)

Tossed the notion around in irc a bit, and still mulling it on over myself before I consider dragging a non-coder into the experiment, but: has anyone played around with the notion of mounting a weakpoint object to a player that would stick out just far enough to get hit first, then passing increased damage along to what it's mounted to?

I know from prior experience with our tsstatic mounts for deployable placement tips that bullets and other things using raycasts needed to be filtered out to keep you from shooting yourself and or becoming bot-invisible, so clearly collision happens.

The main points of interest are: If you've tried it, what particular object type would you recommend mounting for best performance in a multi-player environment, and are there any particular gotchas lurking to be mindful of?

Figured this one has come up in one form or fashion often enough recently (though usually revolving around the previously mentioned 'tear it out and replace it' solutions, rather than the actual desired results), that I might as well throw it out there for folks to mull over.

#1
01/31/2014 (6:13 am)
Didn't someone make a resource or post about animated hitboxes attached to the skeleton so that the body is a mass of sectioned off hitboxes? Bullet hits the boundsbox, raycast goes through to check which hitbox inside takes the damage. Search may or may not be your friend ...
#2
01/31/2014 (2:40 pm)
Well, went ahead and spent the time to go through

http://www.garagegames.com/community/resource/view/6538/1 with a few minor modifications. (I'll stick to short versions this time around, I promise!)

One obviously being the projectile callback:
IMPLEMENT_CALLBACK( ProjectileData, onCollision, void, ( Projectile* proj, SceneObject* col, F32 fade, Point3F pos, Point3F normal, S32 hitBoxNum ),
                   ( proj, col, fade, pos, normal, hitBoxNum ),
				   "@brief Called when a projectile collides with another object.\n\n"
                   "This function is only called on server objects."
				   "@param proj The projectile colliding with SceneObject col.\n"
				   "@param col The SceneObject hit by the projectile.\n"
				   "@param fade The current fadeValue of the projectile, affects its visibility.\n"
				   "@param pos The position of the collision.\n"
                   "@param normal The normal of the collision.\n"
				   "@see Projectile\n"
				  );

another for backwards compatibility:

bool ShapeBaseData::preload(bool server, String &errorStr)
{...
	  if (mUseHitboxes)
	  {
		  //find the HitBox mesh
		  //The hit box mesh is a convex mesh named HB$DDD where DDD is the detail level, the same used for the model and $ is a letter from a to z
		  bool HBfound=false;
		  for (i=0; i< Max_Hitboxes; i++)
		  {
			  char buff[8];
			  dSprintf(buff,sizeof(buff),"HB%d",i+1);
			  HBIndex[i] = mShape->findObject(buff);
			  if (HBIndex[i] != -1) HBfound = true;
		  }
		  if (!HBfound) Con::warnf("No hitboxes found!");
	  }
...}

void ShapeBaseData::initPersistFields()
{...
      addField( "UseHitboxes", TypeBool, Offset(mUseHitboxes, ShapeBaseData),
         "Do we use multiple collision boxes, or just stick to the bounds?" );
...}
+
if (!mDataBlock->mUseHitboxes) return true;
   //if we are here means that the ray has hit the bounding box, now check if an hit box was hit
   //if it hit a collision box the HitBoxNum
   //in the info struct is filled with the number of the box otherwise it remains -1
   if (mShapeInstance)
   {
	   RayInfo shortest;
	   shortest.t = 1e8;
	   if (isServerObject()) 
	   {
		   mShapeInstance->animate(0);	//Animate the model on the server
	   }
	   
	   for (U32 i = 0; i < ShapeBaseData::Max_Hitboxes; i++) 
	   {
		   if (mDataBlock->HBIndex[i] != -1) 
		   {
			   if (mShapeInstance->castRayEA(start, end, info,0,mDataBlock->HBIndex[i]))
			   {
				   info->object = this;
				   if (info->t < shortest.t) 
				   {
					   shortest = *info;
					   shortest.HitBoxNum = i+1;		//  +1 because the meshes HB## begin from 1
				   }
			   }
		   }
	   }
	   if (info->object == this) 
	   {
		   // Copy out the shortest time...
		   *info = shortest;
	   }
	   if (info->HitBoxNum == -1) return false;
	   info->point.interpolate(start,end,info->t);
   }
   return true;
}

There is one final issue I'm presently mulling on over and that's the question of: "So should these be renderable?"

If so, the system's fine as is, and if folks want to hide the convex, it's simple enough to map it's material to a clear one (though the optimizer in me cringes a bit there).

If not, might see about wrenching in something similar to the collisionDetails system (though that'll take a bit if I'm doin it this end, since I've got a conflicting setup there that's game-specific.)