Bullet penetration logic

Hi,

I have been experimenting with bullet penetration. I am using [RaycastAll][1], which is working really fine. However I want a little more advance logic.

hits = Physics.RaycastAll (cam.position, cam.forward, 100.0);
for (var i = 0;i < hits.Length; i++) {
    //var hit = hits*;*

hits*.collider.SendMessage(“ApplyDamage”, (damage/(i+1)), SendMessageOptions.DontRequireReceiver);*
sparks.rotation = Quaternion.FromToRotation(Vector3.up, hits*.normal);*
Instantiate(sparks, hits*.point, sparks.rotation);*
if(hits*.rigidbody){*
hits_.rigidbody.AddForceAtPosition(cam.forward*(force/(i+1)), hits*.point);
}
}
This logic is quite simple. I add damage using a damage variable, which I divide by (i+1) which reduces the damage based upon how many colliders it has hit. So the first objects the raycast hits is struck by 100% damage, the second by 50% damage and so on.
But, I really want damage reduction based on the thickness of the object it is penetrating. So to give a example:
If the first collider is approx. 10CM thick the damage dealt to the object behind it is reduced by(let us say) 50%(just a random number).
Or if the object is approx. 1 meter thick no damage is done(since the “bullet” does not have enough power to penetrate such thick object(s)).
I really hope you get what I am saying.
Thank you very much.
[1]: http://unity3d.com/support/documentation/ScriptReference/Physics.RaycastAll.html*_

Hey guys, I hope this helps you out.

Foreword:

  • It may seem like a ton of work, but the simpler you keep your environment and your rules, the better off you’ll be.
  • A small amount of work (usually a handfull of lines and some data) can give you a lot more depth in gameplay

I’m not sure how realistic you wanna go here, and whether you want to consider a more realistic damage/force falloff or not, but here you go. I hope I didn’t miss anything.

This is a pretty quick overview of a basic breakdown of what an actual AAA console game would consider.

You can do one of few things, simulate a bullet (which is highly recommended) or do direct line of sight depending on what weapon you’re creating.

For the code below, I’ll be doing it based on a single ray cast (based on your implementation).

I went deep here for one reason only. I didn’t see any reference to a bullet simulation, your damage is being calculated over a single step, there is no consideration to distance and various other gameplay variables. Perhaps you’re doing this logic else where but I personally think this should be tied into this bit of logic. Specifically because if you’re making a shooter, you want to be efficient about all of this.

The idea is to fire off as few raycasts as possible, do as few tests as possible, and do as many direct lookups to objects as possible. This will become a balance that you’ll have to figure out in order to achieve the right performance.

Example:

  • Every object in the world (static objects, destructible, characters) should have colliders that have data so that the projectile understands how to interact with the collision.

  • Try to break down your objects based on materials: WOOD, THICK WOOD, CONCRETE, THICK CONCRETE, METAL, CLOTH, PLASTER, GLASS, FLESH, etc… Now, get your mind off the gutter. This will help you distinguish how the impact of your bullet will behave.

  • Usually this is done by doing a material lookup which can become quite complex. I’m going to give an example of a crate with wood panels and metal plating. What you would do here is create a simplified collision mesh that would consist of the different materials that the object consists of. The prop model would have 2 different collision meshes, or you can go deeper and do some sort of a material check on the specific point of the mesh normal. In the end you just want to check if the projectile hit the metal part or the wooden part. If it hit’s the wood, test against wood, if it hits metal, test against metal, if the prop object ends up taking damage, break it apart.

  • The purpose of all of this work is that you want to break things down to see if you want to even attempt to penetrate the material and then do further lookups to affect the projectile.

Consider the following…

If the metal hits, your glock shouldn’t even bother to try penetrating that. However, arm yourself with an AK47 and that could potentially happen. So your WEAPONS need to have MATERIAL VARIABLES that INFLUENCE how your WEAPON will BEHAVE against the ENVIRONMENT. Literally try dissecting how you describe your game. Look at the sentence’s objects, verbs, and how they translate to functionality and data. You can easily arrive at what you need to do by just listening to yourself.

You can add a lot of weapon data that relates to penetration strengths to materials. You can also hook these up to character bonuses such as perks, powerups, or skills that increase penetration chance or other gameplay mechanics. You can then also take other things into account such as how far you are from your target. Certain weapons work better at different distances. The more you consider, the more you’ll be able to start seeing that a glock will behave very differently in a variety of environments. Simulate the weapon behaviours and let your players explore their environment.

Here is an example of how you can easily setup this data:

{Glock
   [Wood_Thin -> CAN PENETRATE, 25% reduction to projectile force, 50% reduction to damage]
   [Wood_Reg -> CAN PENETRATE, 50% reduction to projectile force, 75% reduction to damage]
   [Wood_Thick -> CANNOT PENETRATE, 100% reduction to projectile force, 100% reduction to damage]
},
{Desert Eagle
   [Wood_Thin -> CAN PENETRATE, 10% reduction to projectile force, 20% reduction to damage]
   [Wood_Reg -> CAN PENETRATE, 20% reduction to projectile force, 20% reduction to damage]
   [Wood_Thick -> CAN PENETRATE, 30% reduction to projectile force, 20% reduction to damagee]
}
  • As you can see, you can create penalties to damage separately from distance fall off so that you can calculate penetration, ragdoll, and damage in a mutually exclusive way to actual damage produced. Again, use your judgement.

  • This model will produce a great, quite realistic level of gun behaviour relative to gun design and tuning. Each gun will feel quite different and you’ll be able to make weapon gunplay feel super satisfying and have every player loving you for it.

  • You might want to go deeper with projectile specific behaviours and how they relate to materials. Example, An AK might penetrate all of those 3 blocks of wood, certainly 2, but maybe only 1 thin metal plating. A minigun could clear through both light wood and light metal. An RPG explosion would probably bust the first piece of thin metal, but do nothing to the second. I’m not sure how it would react to the wood. Regarldess, you can create these systems (in the veins of fuzzy logic) and just let them play out.

  • I’m gonna show a bit of what some of this logic might look like in a quick, high level method that would fire off the basic messages that would tricke down into a series of much further implications. I’ll also implement a simple falloff for force/damage algorithm to take travel distance into consideration so that you can simulate a lot of what you’re doing over 1, quick iteration.

The idea is to simply register a vector of where the projectile starts and iterate over the for loop, affecting it’s potential damage and force. You then allow the material collision data and weapon damage variables to decide how it will influence the future of the damage and whatever the bullet is hitting. Again, the bullet doesn’t care about what it’s hitting, just that it’s hitting things. OOP.

    // Base class Fire Weapon()
    void function FireWeapon()
        // Get collisions in the raycast
        Collisions hitCollisions = Phys.PerformRaycast(Camera);
        // Get an instance of this weapon's projectile
        WeaponProjectile projectile = _WeaponProjectile.Create();

        // you can track distance if you want and reduce it over time to calculate fall off
        Vector3 projectileTrajectory = Camera.position;
        float distanceTravelled = 0.0f;
        Vector3 currentCollisionPosition = Vector3.zero;
        for( int i=0; i<hitCollisions.Count(); i++ ) {
            // keep track of distance travelled, update position of projectile travelled so far, reduce damage from falloff
            distanceTravelled = projectileTrajectory.Distance(hitCollisions.Count*.transform.position);*

projectileTrajectory = hitCollisions.Count*.transform.position;*
projectile.ReduceForceAndDamageFromFalloff(distanceTravelled);
// Get the collision data from the collider
CollisionMaterialData colMatData = (CollisionMaterialData)hitCollisions*.GetComponent(“CollisionMaterialData”);*
// Send the collider all of the projectile information
// Hit reactions, etc should occur on that game object, not on your hit function
// IE: Sparks in Metal vs. Concrete Debris. Is the object destructible?
// Have different, specific & optimal colliders for game objects that know how to behave differently
// IE: A simple collider will just spawn an effect.
// IE: A destructible object collider will probably cause damage to the object before it breaks,
// IE: Unity might be able to handle a lot of this stuff quite efficiently
* // Ideas: Are you making an adventure game? Imagine damage-based object->event triggers.*
* // Ideas: Imagine interesting gameplay behaviours that can twisted and applied to a variety of other games.*
hitCollisions*.SendMessage(“Hit Occurred”, projectile.ProjectileHitData);*
// We now take projectile and reduce it’s effectiveness based on the collider data
// Even if it’s a person, you could make that affect bullets going through because you could allow for multiple kills
// Make sure to pass in the counter (i) so that the projectile can figure out
//based on its own data whether it should just die based on the number of hits
projectile.ReduceForceAndDamageFromCollisionData(colMatData, i);
// do a check to see if the projectile is useless
if( projectile.IsDead() ) {
return;
}
}
- If your model was to create a projectile that loses efficiency over time, you would do a similar approach of calculating damage and force fall off over time. Guns should have this distance model built into them so that you can start creating gameplay based over different distances considering both short and long distances. GOOD GAMEPLAY for gunplay boils down to interesting simulations that can create depth.
- Consider these aspects as well when relating to your AI. If all of your gameplay is at long range, then it won’t feel like it has variety. If your enemies know that your gun can penetrate the material vs. assuming it, perhaps they can blind fire at you instead of actually moving around to get to your position. What happens if your enemies know that a shotgun has a short range and needs to move in closely and can go through wood? What if that enemy perching with rifle can keep his distance? Imagine the experience if enemy weapons are distributed in an interesting manner. Imagine if these are other players.
- This model should give you an insight into other things that you can do with your weapons and gameplay as well as how to correlate it to other gameplay such as enemies or meta gameplay aspects. This is an EXCELLENT start if you want to approach DEEP gunplay mechanics and experience because you can simulate things in a more interesting manner.
- If you wanna keep it simple do these things in a basic manner: distance + collisions → force + damage penalties. If you’re setting up simple colliders (like a box with one tag, vs. a set of small boxes tagged differently), at least setup the same data on the weapons so that they will understand these aspects. Add a bit of randomization to penetrate, or use a MIN_FORCE_TO_PENETRATE tunable for some added variety. You can get a lot out of very little by just keeping it super simple.
- Seriously, usually 2 lines of code will give you 50% improvement over 0% improvement. The problem is that you’ll want to go further and that aspect will feel tacked on because of it. But it’s still 50% improvement. Just make sure it doesn’t unbalance anything.
Also, I’m not sure what kinda game you’re making, but here is an idea to make some performance improvements. The idea here is to break gameplay side effects apart from presentation.
Example:
- Consider batching these tests and processes over a few frames. Everything from AI objectives, to taking cover, to whatever. Shooters do a ton of line of sight tests for AI and this stuff can amount to insane proportions.
- Only do what needs to get done to make the game be “on time” right away, distribute the remainder over time.
- Example: First apply damage and let that entity know about it. Now you know if it’s dead, and that object can start dealing with the hit effect on it’s own (blood, particles, ai, ragdoll, sounds). That way the object can have it’s AI shut down right away, control taken away from the player, thus removes gameplay micro lags and still allowing things to be efficient. (I.E. kickoff a HitReaction/HandleDamage coroutine).
- Because so many things happen in parallel, just make sure that whatever critical things need to happen, occur on the exact frame
I hope this helps you out!

Does it need to be dynamic? Could you not just tag thick walls as “thinkwall” and thin ones as “thinwall” and then just apply appropriate damage according to which one you are shooting through? That way you could skip the calculations. This is assuming that you are building your level with a set of prefabs.

Hmm… Maybe there is another way to do that, but I would try to send a raycast from the other side (raycast.all does ignore the other side of colliders, or am I wrong?) check for the same collider and calculate the distance between the points. You can use that number for the calculation of the damage!

EDIT: as anxo said, you could simply tag them, which would probably be enough for almost every situation…

Too old question but answering something just in case another guy stumble upon this, You know that when ever you are checking ray cast against any game object, that game object have to have a collider, and on each collider, it has a bound, bound has min and max attributes that is vector 3, if we ignore the y component, we can estimate a value by bound.max.x - bound.min.x, and same for z, there is also maybe an easy way to see what is the estimate orientation of the object that the ray is hitting we have the hit point’s normal, again if we remove the elevation from that, we get a vector that is horizontal, now we can see if it’s close to x axis or z and based on that we give a multiplier to the min calculation on each bound component, then we can add these two parts together and get a rough estimation of the distance that the object is moving inside any game object in horizontal manner, I’m not that good at math but I’m sure with a bit of trigonometry tricks and math we can add the third dimension to it also, then we can follow up the same rout that is already given on answers above in detail to add multiplier to this final distance based on the material and get the final dampening factor to damage.
But if you don’t want to go onto all these crazy math, a simpler way but a bit more cpu heavy one is when you are looping inside each hit point, you would cast another ray with a little offset from the hit point toward the end point, for example you move 1 millimeter in, but this time instead of ray cast all, you use simple ray cast and as soon as you hit something you know that it’s the other side of the object, now you can use Vector3.distance to get the distance between the original hit point you are evaluating and this new point, but that means for each hit point of your original you will have another ray cast, so you may want to track the total damage on the fly so that if it became zero you can do and early exit.
Hope what i said is understandable :smiley: