Relative armour thickness and accurate armour penetration ballistics for 3D tank game

Hello I am seeking support in finding a way to implement an accurate armour penetration gun ballistics and relative armour thickness system for a 3D tank game in Unity.

First off I am seeking help in fabricating an accurate ballistics system to work in Unity that can replicate this system as displayed herein: World of Tanks - tanks.gg

Secondly I am wondering as to where is the ideal place to ask this question within the Unity forums?

Also I know the math formula for relative armour thickness:
Relative armour thickness = (base armour thickness Ć· vertical angle cosign) Ć· horizontal angle cosign
Here is a relative armour calculator: Relative Armor Calculator

Also I have a solution for accurate armour penetration values for the tank guns. Solution: use a damage curve system (as utilized with most AAA shooters) with armour penetration over distance instead of damage over distance; kind of like this (but with armour penetration not damage):

My current problem is that I do not know how to fabricate the accurate relative armour thickness and realistic armour penetration ballistics in Unity. I am an artist, designer and an engineer. I can make schematics and blueprints just fine but I am lacking in digital fabrication skills.

I know that this pursuit can be achieved within Unity for it already has been done years ago by this Unity based M.M.O. Ground War: Tanks (of which can be found herein: https://tanks.mail.ru/ ).

Again I am looking for help in finding a way to obtain an accurate ballistics system for a 3D tank game within Unity. A ballistics system that has the capability of relative armour thickness and accurate armour penetration characteristics.

If you have any ideas or wishes to help, please feel free to contact myself herein or via my Google mail address at: mattwow3@gmail.com

My immediate first impression would be to separate all of the tank colliders into individual colliders with different armor properties. I would advise against using the rigidbody physics system for the shells, however. You might have heard of the ā€œbullet through paperā€ problem. Physics is done in discrete time steps, so you lose accuracy at high velocities.

For reasonably high velocities a raycast should work much more effectively. The only problem is it means your projectile hits the target immediately, even if you’re 5km away, and you can’t simulate bullet drop as raycasts are perfectly straight lines. A combination of the two might work, by having a projectile that fires a ray out in front of it (about 10m, for example…).

This could all be done in a manner similar to this:

RaycastHit hit;
bool hitObject = false;
// Cast a ray from out position, forward (relative to projectile), a max distance of 10m.
hitObject = Physics.Raycast(transform.position, transform.forward, 10f, out hit);

if(hitObject)
{
    // With that we can get:
    GameObject section = hit.collider.gameObject;
}

Once you have the child object, you can get a script object from it using the GetComponent(); method. Once you have a reference to that script, you could access public variables like armorThickness and do your calculations from there. The RaycastHit object has a normal vector (hit.normal) which you can use with the velocity field on the RigidBody attached to the projectile to get the angle.

1 Like

This is spot on for exactly what I was looking for Scabbage.

I am wondering how to to test out this concept to see if it works. I am not great with making things in Unity I will say that outright. Although I wish to get to a point where I can have a template system so as to be able to add 3D tank models and appropriate parts into a template so that I can add a whole bunch of tanks into my tank game in a relatively short period of time. I will try to create a prototype although I am prepared to commission people who are far more talented than I in this field to bring about a robust ballistics system for the tank game that I am working on.

Has anyone tested such a ballistics system before Scabbage? It does not have to be pretty it just has to work well, I really hope to get this ballistics project rolling in the near future.

I’ve used a similar system for high speed ballistics in a side project, it works quite well.

In terms of modularisation the way I would do it is create the models in blender, separate out all of the different armor types into their own objects, then pull the file into Unity. Since I don’t know the extent of your understanding with the platform I’ll try to start from the basics and we can work up from there.

In Unity, there’s an object hierarchy that contains all objects in the scene. For one tank, it can have it’s own hierarchy of child objects. eg:

- Tank
    - Body
        - LeftTread
        - RightTread
    - Turret
        - Barrel

On this tank, it has a collider component on every child object (Body, LeftTread, RightTread, Turret, Barrel). Say we have a script called ā€œArmorā€ on every part that has its own armor properties.

The Armor script could look like this:

public class Armor : MonoBehaviour
{
    public float armorThickness;
}

Any time you create a component it should inherit from the MonoBehaviour class, which is Unity specific. Here it has a variable armorThickness that classes outside can use.

On a projectile, we’d drag a new mesh into the scene (or just create a capsule/sphere primitive from the menu up top, GameObject > 3D object > Capsule), and add a Rigidbody and Collider component. Collider can just be a sphere/capsule collider, because we’re doing the interactions with the environment ourselves with the raycast. Breakdown of what we’re doing:
Rigidbody: Lets an object move around using the physics system. With it we can apply forces like gravity, and give it an initial velocity when we fire the projectile.
Collider: Influences how rigidbodies behave. A Collider not only gives the physics system a shape to use for collisions, but it also changes the way mass is distributed in the rigidbody. For this we really only need a simple sphere.

On the projectile, we’d put our controller class for all of the physics, like this:

public class Projectile : MonoBehaviour
{
    bool hitObject;
    RaycastHit hit;
    Rigidbody rb;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }
 
    void FixedUpdate()
    {
        hitObject = Physics.Raycast(transform.position, transform.forward, 10f, out hit);
        if(hitObject)
        {
            // This is the gameobject that we hit (LeftTread, Turret, or even some non-tank collider)
            GameObject collisionObj = hit.collider.gameObject;
 
            // Get the Armor class from this object, if it has one.
            Armor armor = collisionObj.GetComponent<Armor>();

            // Check if the armor exists
            if(armor != null)
            {
                // I'm guessing giving the hit position, normal, velocity of the projectile and the mass would be enough
                // for a realistic-looking damage mechanic
                armor.DoExplosionyStuff(hit.position, hit.normal, rb.velocity, rb.mass);
            }
        }
    }
}

Here we constantly shoot a raycast in front of the projectile every frame. When it hits a collider, we check if that object has an Armor component on it (will be null otherwise), if it does, do all the explody stuff with the information from the projectile.

There are ways this could be more expandable and modular, but I’m trying to keep it minimal for now.

1 Like

Ahh this is great Scabbage! I am thinking of using hit points of the tanks and ammunition to simplify things for the time being. Perhaps a more realistic mode could be done but hit points would work fine for now.

Scabbage do you know if anyone would be interested in commission(s) for these type of requests? I don’t mind paying talented people for a good job done (way better than I could do right now, not really where my strengths are). I just want a way to get a ballistics template out the door and apply it to my tank game. I am currently using Physics Tank Maker ( https://www.assetstore.unity3d.com/…VxIuHLUBgSqqdz8ZsIaApH_8P8HAQ#!/content/50485 ) as a base for the tanks.

Physics Tank Maker works great for the automotive components of tanks although the ballistics side of it leaves something to be desired; thus I am here asking around.

This looks incredibly modular, and probably wouldn’t be too hard to get a better ballistics system going well in it. It does use a very large amount of individual colliders for the tracks though, which might make it a pain to make edits, depending on how it’s implemented.

I’d be happy to collaborate, I just don’t have very much time at the moment =( Depends on when you want to knock it out really, I’ll probably be free for most of next month. Otherwise you might be able to advertise here on the forums, or try sites like Freelancer (be wary of scammy devs though, they often lie about their qualifications to get hired).

Ah well I have a lot of time allocated for this project; in fact I have been at this tank project since basically the tail end of 2012 (although late 2015 is when I started tinkering around with Unity): http://withinamnesia.deviantart.com/art/Pz-Kpfw-IV-Schlachtross-410125369 ( ← this is one of the earliest footnotes in my tank game project history )

I have a lot of time opened up for this project. In fact all that I am looking for right now is help and people will to work with on the tank game project. I just got off of working for World of Tanks (tank historian content) and I feel that now is the time to really push forward with my own tank game instead of giving away my talent to others (plus I can make what ever I like which is a big plus :-P).

So when ever you feel inspired I will be waiting. Just send me a message at mattwow3@gmail.com or on that deviantART site or here or what ever you fancy Scabbage.

Hodamn, that’s an old project. I’ve been cooking up stuff on the side since late 2016 (destructible worlds without that horrendous blockiness of Minecraft), but haven’t been able to squeeze in very much progress over the past few months because of workload.

Accurate ballistics sounds interesting though, so I might try quickly prototyping now to see how it does. If I contact you later on it’ll probably be through my email, I don’t often come on the forums ;p

Okay luck chance meeting I suppose that we met, I too do not frequent these forums. If anyone else wants toget in contact with myself, I will welcome any help.

Okay, it was a luck chance meeting I suppose that we met. I too do not frequent these forums. If anyone else wants to get in contact with myself, I will welcome any help.

Prototyping done, super primitive damage modelling using the raycast + rigidbody system here.

Tank speed, projectile speed, projectile mass are all available in the editor. AI tanks use a seperate controller than the player controller inherits from, so all the movement calls could just as well be from an AI. WASD to move, space/left mouse to fire. Steers like a car because I am lazy =)

@Scabbage awesome work. :slight_smile: How would you extend it with penetration calculations and deflections if the angle and energy would allow the projectile to get deflected? Just some hints and I’ll try to do it.

I have written scripts - projectile motion and armor penetration taken directly from physics equations:

/*
https://en.wikipedia.org/wiki/Projectile_motion
https://amesweb.info/Physics/Projectile-Motion-Calculator.aspx
https://www.weber.edu/wsuimages/mollysmith/3500Presentations/Kinematics%20of%20Projectile%20Motion.pptx
https://en.wikipedia.org/wiki/Terminal_velocity
http://www.calctool.org/CALC/eng/aerospace/terminal
https://demonstrations.wolfram.com/ProjectileWithAirDrag/#more

To Do:
- Further optimization ? (reduce trigonometry ?);
- Trajectory of a projectile with Newton drag (also read about Reynolds numbers), it is more difficult to be solved analitycally,
  but may brings even more realistic behaviour.
*/

using System;
using UnityEngine;

public class ProjectileMotion : MonoBehaviour
{
    public bool AirResistance = true;
    public float Velocity = 300.0f;
    public float Mass = 0.5f;
    public float DragCoefficient = 0.3f;
    public float Duration = 5.0f;

    GameObject _Bullet;
    Vector3 _StartPosition, _PreviousPosition;   
    float _Area, _Distance, _HorizontalAngle, _StartTime, _TerminalVelocity, _VerticalAngle;
    MaterialPropertyBlock _PropertyBlock;   
    bool _UpdateProjectile = false;

    float GetVerticalAngle(Transform t)
    {
        return (-1.0f) * t.eulerAngles.x;
    }

    float GetHorizontalAngle(Transform t)
    {
        float angle = 360.0f - t.eulerAngles.y + 90.0f;
        if (angle > 360.0f) angle = angle - 360.0f;
        return angle;
    }

    // Projectile motion in a uniform gravitational 3D field without air resistance:
    // startPos = position of bullet, when bullet was shot;
    // startTime = time, when bullet was shot;
    // velocity = initial velocity (initial speed of bullet, when bullet was shot, for example 800 m/s);
    // theta = horizontal angle of initial velocity in radians, computed from GetHorizontalAngle(projectileToShoot.transform) * Mathf.Deg2Rad;
    // phi = vertical angle of initial velocity in radians, computed from GetVerticalAngle(projectileToShoot.transform) * Mathf.Deg2Rad;
    Vector3 ProjectileDisplacement (Vector3 startPos, float startTime, float velocity, float theta, float phi)
    {
        float time = Time.time - startTime;
        float x = velocity * time * Mathf.Cos(theta) * Mathf.Cos(phi);
        float y = velocity * time * Mathf.Sin(phi) - 0.5f * 9.8f * time * time;
        float z = velocity * time * Mathf.Sin(theta) * Mathf.Cos(phi);
        return startPos + new Vector3(x, y, z);
    }

    // Mass = kg
    // Drag coefficient (for bullets = 0.3)
    // The density of air at sea level = 1.5
    // Projected area of object (object with size 2 cm x 5 cm = 0.001 m2 etc.)
    float TerminalVelocity(float mass, float drag, float density, float area)
    {
        float gravity = 9.8066f;
        return (float)System.Math.Sqrt((2.0f * mass * gravity) / (density * area * drag));
    }

    // Projectile motion in a uniform gravitational 3D field with air resistance (Stoke's equation):
    // startPos = position of bullet, when bullet was shot;
    // startTime = time, when bullet was shot;
    // v0 = initial velocity (initial speed of bullet, when bullet was shot, for example 800 m/s);
    // vt = terminal velocity, computed from TerminalVelocity function;
    // theta = horizontal angle of initial velocity in radians, computed from GetHorizontalAngle(projectileToShoot.transform) * Mathf.Deg2Rad;
    // phi = vertical angle of initial velocity in radians, computed from GetVerticalAngle(projectileToShoot.transform) * Mathf.Deg2Rad;
    Vector3 ProjectileDisplacementWithAirDrag(Vector3 startPos, float startTime, float v0, float vt, float theta, float phi)
    {
        float t = Time.time - startTime;
        float g = 9.8066f;
        double e = Math.E;  // 2.7182818284590451
        float x = (float)((v0 * vt / g) * Math.Cos(theta) * Math.Cos(phi) * (1.0f - Math.Pow(e, -g * t / vt)));
        float y = (float)((vt / g) * (v0 * Math.Sin(phi) + vt) * (1.0f - Math.Pow(e, -g * t / vt)) - (vt * t));
        float z = (float)((v0 * vt / g) * Math.Sin(theta) * Math.Cos(phi) * (1.0f - Math.Pow(e, -g * t / vt)));
        return startPos + new Vector3(x, y, z);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && !_UpdateProjectile)  // initialize
        {
            _Bullet = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            _Bullet.transform.parent = Camera.main.transform;
            _Bullet.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
            _Bullet.transform.localPosition = new Vector3(0.0f, 0.0f, 2.0f);
            _Bullet.transform.localEulerAngles = Vector3.zero;
            float radius = _Bullet.GetComponent<SphereCollider>().radius * _Bullet.transform.localScale.x;
            _Area = Mathf.PI * radius * radius;
            _StartPosition = _Bullet.transform.position;
            _StartTime = Time.time;
            _TerminalVelocity = TerminalVelocity(Mass, DragCoefficient, 1.5f, _Area);
            _HorizontalAngle = GetHorizontalAngle(_Bullet.transform) * Mathf.Deg2Rad;
            _VerticalAngle = GetVerticalAngle(_Bullet.transform) * Mathf.Deg2Rad;
            _PropertyBlock = new MaterialPropertyBlock();
            _Bullet.GetComponent<Renderer>().GetPropertyBlock(_PropertyBlock);
            if (AirResistance)
                _PropertyBlock.SetColor("_Color", Color.red);
            else
                _PropertyBlock.SetColor("_Color", Color.blue);
            _Bullet.GetComponent<Renderer>().SetPropertyBlock(_PropertyBlock);
            _Bullet.transform.parent = null;
            _UpdateProjectile = true;
        }
       
        if (((Time.time - _StartTime) > Duration) && _UpdateProjectile)
        {
            _UpdateProjectile = false;
            Destroy(_Bullet);
        }
       
        if (_UpdateProjectile)
        {
            _PreviousPosition = _Bullet.transform.position;
            if (AirResistance)
            {
                _Bullet.transform.position = ProjectileDisplacementWithAirDrag(_StartPosition, _StartTime, Velocity, _TerminalVelocity, _HorizontalAngle, _VerticalAngle);
            }
            else
            {
                _Bullet.transform.position = ProjectileDisplacement (_StartPosition, _StartTime, Velocity, _HorizontalAngle, _VerticalAngle);
            }
            _Distance = Vector3.Distance(_PreviousPosition, _Bullet.transform.position);
        }
    }

    void FixedUpdate()
    {
        if (_UpdateProjectile)
        {
            RaycastHit hit;
            if (Physics.Raycast(_PreviousPosition, _Bullet.transform.position - _PreviousPosition, out hit, _Distance))
            {
                Debug.Log("Hit");
            }
        }
    }
}
using UnityEngine;

// Physics equations taken from: "Physics for Game Programmers" by Grant Palmer
public class ArmorPenetration : MonoBehaviour
{
    [Tooltip("Projectile mass [kg]")]
    public float Mass = 0.0082f;
    [Tooltip("Projectile (muzzle) velocity [m/s]")]
    public float Velocity = 440.0f;
    [Tooltip("Steel armor thickness [meters]")]
    public float Thickness = 0.01f;
    [Tooltip("Projectile diameter [meters]")]
    public float Diameter = 0.009f;
    [Tooltip("Angle of impact [degrees], angle is zero for projectile strike perpendicular to the steel plate")]
    public float Angle = 0.0f;

    // m - projectile mass [kg]
    // v - projectile (muzzle) velocity [m/s]
    // t - steel armor thickness [meters]
    // d - projectile diameter [meters]
    // phi - angle of impact [degrees], angle is zero for projectile strike perpendicular to the steel plate
    bool IsArmorCanBePenetrated (float m, float v, float t, float d, float phi)
    {
        float f = 1.8288f * (t / d - 0.45f) * (phi * phi + 2000f) + 12192f;  //armor resistance coefficient
        float k = 0.5f * m * v * v;  //kinetic energy of projectile
        float s = Mathf.Cos (phi * Mathf.Deg2Rad);
        float n = 8.025f * (t * d * d * f * f) / (s * s);  //minimum kinetic energy required to penetrate armor
        return (k > n);
    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(10, 10, 100, 50), "Calculate"))
        {
            bool result = IsArmorCanBePenetrated (Mass, Velocity, Thickness, Diameter, Angle);
            Debug.Log(result);
        }
    }
}

Hello… i am also in the same situation right now, i need a solution to make armor. right now no matter where the other player shoots the tank he will get damage, so i dont need the angeling and those stuff i just want a basic armor system, I also dont want to maunauly place colliders all over the tank as that isnt so efficient. Thanks

Please don’t necro-post. If you have a new question, make a new post. It’s FREE!!

How to report your problem productively in the Unity3D forums:

http://plbm.com/?p=220

This is the bare minimum of information to report:

  • what you want
  • what you tried
  • what you expected to happen
  • what actually happened, log output, variable values, and especially any errors you see
  • links to documentation you used to cross-check your work (CRITICAL!!!)

The purpose of YOU providing links is to make our job easier, while simultaneously showing us that you actually put effort into the process. If you haven’t put effort into finding the documentation, why should we bother putting effort into replying?

If you haven’t started yet, get started now with tutorials, lots of tutorials. Try this approach:

Imphenzia: How Did I Learn To Make Games:

https://www.youtube.com/watch?v=b3DOnigmLBY