PDA

View Full Version : An improved method of locational damage.



legacy-Mr Evil
05-10-2003, 11:36 AM
Now that I am doing a weapon with locational damage, I have put some thought in to the problem of accurately determining where a shot hit, as GetClosestBone often seems to return a bone which doesn't correspond to where the shot appeared to hit.

To get a better idea of where it really hit, I am extending an imaginary line in the direction of the shot through the HitLocation and taking the point on that line which is closest to the Z axis of the victim, then using that point in GetClosestBone. This means that a shot to the chest will no longer be 'blocked' by a hand which may be in front as it will pass through right through.

The point closest to the Z axis of the victim can be determined by a little bit of simple trigonometry, as depicted by this diagram showing the collision cylinder and a hit on it:
http://www.mrevil.pwp.blueyonder.co.uk/temp/HitLocationDiagram.gif
where h and a are the distance from the HitLocation to the Z axis and the closest point to it respectively, and theta is the angle between those two lines.

Here is the code, extracted from my InstantFire class:

if(Other.IsA('Pawn'))
{
//Find a point on the victim's Z axis at the same height as the HitLocation.
ClosestLocation = Other.Location;
ClosestLocation.Z += (HitLocation - Other.Location).Z;

//Extend the shot along its direction to a point where it is closest to the victim's Z axis.
BoneTestLocation = X;
BoneTestLocation *= VSize(ClosestLocation - HitLocation);
BoneTestLocation *= normal(ClosestLocation - HitLocation) dot normal(HitLocation - Start);
BoneTestLocation += HitLocation;

//Find the closest bone.
HitBone = Other.GetClosestBone(BoneTestLocation, X, dist, 'head', HeadShotRadius);

if(HitBone != 'head')
HitBone = Other.GetClosestBone(BoneTestLocation, X, dist, 'spine', TorsoShotRadius);

//Caters for monsters, and anything else without any bones.
if(HitBone =='None')
{
HitVertical = BoneTestLocation.Z - Other.Location.Z;
BoneTestLocation.Z = Other.Location.Z;
HitHorizontal = VSize(BoneTestLocation - Other.Location);

if(HitHorizontal < Other.CollisionRadius * 0.5)
{
if(HitVertical > Other.CollisionHeight * 0.9)
HitBone = 'head';
else if(HitVertical > Other.CollisionHeight * -0.4)
HitBone = 'spine';
}
}

if(HitBone == 'head')
{
Damage *= HeadShotDamageMult;
DamageType = HeadshotDamageType;
}
else if(HitBone == 'spine')
DamageType = TorsoshotDamageType;
else
{
Damage *= LimbShotDamageMult;
DamageType = Default.DamageType;
}
}

This also contains a little bit to cope with monsters, who don't have skeletons, by simply measuring the height of the hit and how close to the centre of the monster it is. I've done extensive testing and it seems to work very nicely, but my geometry skills are a bit rusty. I think it may fail if the shot lands straight down on to the head, where it may pass right through and thus miss the head. This could be solved by measuring the closest bone at the HitLocation as normal, as well as with the method above, and taking the 'best' bone of the two found.

I have only accounted for two bones: head and spine; anything else being considered a limb of some sort, but it could be extended to take more locations in to account. I could also be applied to projectiles instead of an instant hit weapon.

legacy-Vir@s
05-11-2003, 03:46 AM
Very strange.

I never had any problems with GetClosestBone, works really nice here ;)

The problem with your function might be that you afterwards can't detect if you either hit the arm or the leg (if I got your idea right ;)).

Else it seems like a good idea.

legacy-Mr Evil
05-11-2003, 10:14 AM
I've been doing a lot of testing with a pawn placed in a map and a weapon that logs where it hit and there seem to be several situations where GetClosestBone doesn't work quite well enough. For instance, shooting the head from the side will usually result in a hit to the shoulder and shooting the chest from the front usually hits an arm or a hand.

legacy-Vir@s
05-11-2003, 03:05 PM
Well maybe collision of the UT2003 chars is a bit messed up.

But I tried location damage with another coder for the costum models over at RedOrchestra. And it worked fine (like I said, it was a costum model with costum karma etc.)
Maybe that's the problem ;)

But what about the detect arm / foot problem I mentioned above - maybe you should consider that in your code?

legacy-Mr Evil
05-11-2003, 03:12 PM
Originally posted by Vir@s
...But what about the detect arm / foot problem I mentioned above - maybe you should consider that in your code?
All that would be needed to cope with that is a few extra 'else if' statements to test for lthigh, rthigh etc. Head, torso and limb just happens to be all I needed for myself.

legacy-Vir@s
05-11-2003, 03:46 PM
I know - but since your are publishing that code I'm just giving feedback ;)

legacy-Ramm Jaeger
05-17-2003, 01:49 AM
Another thing you could do is just make the bullet actually go through the player and not stop. If you were doing realistic bullet physics you could do a check on thier momentum when they hit something and then slow them down for each item they hit. Then if they hit a hand they will still hit a chest. That's what I did for The Third Reich.

legacy-Vir@s
05-17-2003, 03:54 AM
Yeah the new UT2003 projectile class allows really good handling of such things, like reduced damage in distances, reducing speed with time, short: realistic bullet physics.

This allows you to script a completly physics controled projectile, really smart :D :D

legacy-fortress
05-25-2003, 11:05 AM
mr. evil i like what you have come up with
i my self have been thinking of a way to get better location damage.

i noticed something with the GetClosestBone function
it seems to return a bias name instead of the real bone name or least that how it is coded in the actor class
i havent had a chance to test this on a custom skeleton yet i hope to do so today

the question i have is does anybody know where GetClosestBone
gets the bone names it returns or are they hard coded

legacy-fortress
05-25-2003, 08:05 PM
well figured it out the GetClosestBone returns the bone alias name that is setup in the animation broswer in ued under sockets

so if you need more bone names you can just add more sockets to the skeleton and that should do it
:)

legacy-fortress
05-26-2003, 05:43 AM
i was wondering if there was any reason why you did that in the instant fire class and not the xpawn

legacy-Mr Evil
05-26-2003, 08:51 AM
Originally posted by fortress
i was wondering if there was any reason why you did that in the instant fire class and not the xpawn
Because I'm making weapons, so I don't want to replace the pawn. If I were doing something that required a new pawn anyway then it might make sense to do the damage calculations in the pawn, but either way has the same general effect.

legacy-Komrod
05-26-2003, 10:53 AM
http://www.komrod.com/pix/forum_closestbone.jpg

In the image, you see the explanation for those who dosnt understand yet. Even when we hit the player torso, the GetClosestBone function ( which is the only way to get locationnal damage ) return the arm bone instead of the torso bone.
Well, the hit system in unreal is also inaccurate as you can hit player when you shoot between his legs.

legacy-fortress
05-26-2003, 03:41 PM
ok just wondering if there was an advantage to doing that in the instant fire class

i supose it is 6 in one hand half a dozen in the other
for what i am working on requires a new pawn class.

legacy-Mr Evil
05-26-2003, 03:52 PM
Originally posted by fortress
ok just wondering if there was an advantage to doing that in the instant fire class

i supose it is 6 in one hand half a dozen in the other
for what i am working on requires a new pawn class.
Doing it in the pawn class means:
You write the code once and it will then apply equally to all weapons or any other form of damage.
You loose compatibility with other mods that replace the pawn.

Do it in the weapon and:
It only applies to that specific weapon.
Compatibility is better.

So which you choose depends on why you are doing it. If you want to create something where locational damage applies to everything, then use the pawn. If you want locational damage for a specific weapon, then code it in that weapon.

legacy-Cmd. Crunch
06-29-2003, 05:22 PM
I was just looking through this, and trying to use it, but what variable type is hitbone supposed to be?

And is the dist variable supposed to be intialized to anything? I looked in the Sniper Rifle code and it seems to be declared but never set to anything.

legacy-Mr Evil
06-29-2003, 05:50 PM
Originally posted by Cmd. Crunch
I was just looking through this, and trying to use it, but what variable type is hitbone supposed to be?

And is the dist variable supposed to be intialized to anything? I looked in the Sniper Rifle code and it seems to be declared but never set to anything.
HitBone is a name.
dist is set by GetClosestBone, I assume it is how close the bone is to the hit, but it is not necessary to use it for this, it's just for the function to work.

legacy-Cmd. Crunch
06-29-2003, 05:55 PM
I keep getting an error in this line:

HitBone = Other.GetClosestBone(BoneTestLocation, X, dist, 'head', HeadShotRadius);

HitBone is declared in the scope of the DoTrace like this:
local name HitBone;

And here's the error:
Error: Call to 'GetClosestBone': Bad Expression or missing ')'

legacy-Cmd. Crunch
06-29-2003, 06:41 PM
Nevermind, I fixed it. But can anyone tell me some good numbers for the HeadRadius and TorsoRadius? I would like to get the optimal numbers for this, as it's probably easier than going through a lot of experimentation to find them myself.

legacy-Mr Evil
06-30-2003, 02:31 PM
Originally posted by Cmd. Crunch
...But can anyone tell me some good numbers for the HeadRadius and TorsoRadius?...
I'm using values of 8.0 and 20.0 respectively, which seem to fit the standard models quite well.

legacy-SquirrelZero
07-09-2003, 07:23 PM
i've been experimenting with your method for the past day or so, but i noticed it still doesn't help with the problem of the "ghost headshot" -- i.e. shooting next to the head (but still inside the collision area) results in a headshot, even though it appears as if there's nothing but empty space. Curious if anyone has found a solution for this, as it seems no matter what i set the bone bias distance to in GetClosestBone() it will return a head result in this situation.

I've also set mine up for other body parts, so i included an example of how to do it for arms. I left out the leg stuff because it's basically the same, only different radius variables.


while (true)
{
HitBone = Other.GetClosestBone(BoneTestLocation, XVec, dist, 'head', HeadShotRadius);
if(HitBone != 'head')
HitBone = Other.GetClosestBone(BoneTestLocation, XVec, dist, 'spine', TorsoShotRadius);
else
break;
if(HitBone != 'spine')
HitBone = Other.GetClosestBone(BoneTestLocation, XVec, dist, 'lshoulder', ArmShotRadius);
else
break;
if(HitBone != 'lshoulder')
HitBone = Other.GetClosestBone(BoneTestLocation, XVec, dist, 'rshoulder', ArmShotRadius);
else
break;
if(HitBone != 'rshoulder')
HitBone = Other.GetClosestBone(BoneTestLocation, XVec, dist, 'rfarm', (ArmShotRadius * 0.75));
else
break;
if(HitBone != 'rfarm')
HitBone = Other.GetClosestBone(BoneTestLocation, XVec, dist, 'lfarm', (ArmShotRadius * 0.75));
else
break;
if (HitBone != 'lfarm')
HitBone = 'none';

break;
}

legacy-Mr Evil
07-09-2003, 07:40 PM
Have you tried testing the 'dist' variable after? If it does what I think it does, then it should tell you how far away from the bone the hit is. Failing that, have you tried putting a negative value for bias distance?

legacy-SquirrelZero
07-09-2003, 08:03 PM
Well, my experience with dist is that it does nothing. You can set it to anything before passing it to GetClosestBone(), and it will always output whatever value you input. Using a negative bias distance gives me the same result as a positive. Strange, it doesnt even seem to affect anything until i set the HeadShotRadius to about 15.0 or higher, then i notice headshots extend farther.

edit: on another note, your method for non-boned hits works great for avoiding the ghost headshot, but if you're planning on rotating or bending your character in any way it will become completely inaccurate, since the collision cylinder can't be rotated. That's why getclosestbone() was so useful... sigh, i'll work on this some more and get back to you.

legacy-Ciced
08-06-2003, 01:54 PM
Originally posted by Cmd. Crunch
I keep getting an error in this line:

HitBone = Other.GetClosestBone(BoneTestLocation, X, dist, 'head', HeadShotRadius);

HitBone is declared in the scope of the DoTrace like this:
local name HitBone;

And here's the error:
Error: Call to 'GetClosestBone': Bad Expression or missing ')'

Yes I'm getting the same error.

Any help?

legacy-SquirrelZero
08-06-2003, 04:51 PM
make sure all of those variables are actually declared first. BoneTestLocation, dist, and HeadShotRadius are all new to the instantfire class, so either declare them locally or globally.