How can I make sure triggers or collisions are never skipped even for high speeds?

Firstoff: I once made a forum post that handelled a simular problem and I also added this to it, but I’m not getting any reactions so I’m just going to refer to it here. (it may be eassier to read it over there.
Forum post

I have 2 types of bullets and the problems are I think simular in solution but the effect is somewhat different.

My first bullet type is a fireball, it just moves in straight lines, when colliding with a player it is destroyed unless it’s the player who fired the bullet, if it collides with another bullet or object or ground it should be destroyed aswel.
Now for some reason 2 colliding bulelts arn’t always detected, 2/3 times they get destroyed, 1/3 they just continue.

GIF: Example of the error

I just noticed that if I try hard enough I can even pass through walls:
GIF: Another thing that goes wrong

This is my collision matrix:
​IMG: Collision Matrix

The code that sees if any trigger has been made:

using UnityEngine;
using System.Collections;
 
/// <summary>
/// Stef Geukens
///
/// This code will determine if the object it is attached to has triggered a overlap with a collider object.
/// If so, the object is destroyed. When the object hits another player then his own (set playernum to the correct player)
/// then a function is called (Hurt) in the ShotPlayer script so thaty script knows when the score in the scorescript has
/// to be updated and for what player.
/// </summary>
 
public class BulletTriggerDetection : MonoBehaviour {
    public static BulletTriggerDetection BTD;
    public int playerNum = 1;
    public bool destroyOnImpact = true;
    public bool destroyOnTimer = true;
    public bool destroyOnNumberOfCollides = false;
    public int maxNumberOfCollides = 6;
    private int numberOfCollides = 0;
 
    void Start()
    {
        BTD = gameObject.GetComponent<BulletTriggerDetection>(); ;
    }
 
    void OnTriggerEnter2D(Collider2D  col)
    {
      Debug.Log(gameObject.name + " has collided with " + col.gameObject.name);
      Debug.Log("My layer: " + gameObject.layer);
      Debug.Log("Number of Impacts: " + numberOfCollides);
 
        if (playerNum == 1)
        {
       
                if (col.gameObject.tag == ("Player2") || col.gameObject.tag == ("Player3") || col.gameObject.tag == ("Player4"))
                {
                    col.gameObject.GetComponent<ShotPlayer>().Hurt(col.gameObject.tag, playerNum);
                    // Debug.Log("Player" + playerNum + " just shot " + col.gameObject.tag);
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player1") && destroyOnImpact)
                {
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player1"))
                {
                    numberOfCollides += 1;
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
 
 
        }
        else if (playerNum == 2)
        {
 
                if (col.gameObject.tag == ("Player1") || col.gameObject.tag == ("Player3") || col.gameObject.tag == ("Player4"))
                {
                    Debug.Log("State1");
                    col.gameObject.GetComponent<ShotPlayer>().Hurt(col.gameObject.tag, playerNum);
                    // Debug.Log("Player" + playerNum + " just shot " + col.gameObject.tag);
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player2") && destroyOnImpact)
                {
                    Debug.Log("State2");
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player2"))
                {
                    Debug.Log("State3");
                    numberOfCollides += 1;
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
 
        }
        else if (playerNum == 3)
        {
 
 
                if (col.gameObject.tag == ("Player1") || col.gameObject.tag == ("Player2") || col.gameObject.tag == ("Player4"))
                {
                    col.gameObject.GetComponent<ShotPlayer>().Hurt(col.gameObject.tag, playerNum);
                    // Debug.Log("Player" + playerNum + " just shot " + col.gameObject.tag);
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player3") && destroyOnImpact)
                {
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player3"))
                {
                    numberOfCollides += 1;
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
 
        }
        else if (playerNum == 4)
        {
 
 
                if (col.gameObject.tag == ("Player1") || col.gameObject.tag == ("Player2") || col.gameObject.tag == ("Player3"))
                {
                    col.gameObject.GetComponent<ShotPlayer>().Hurt(col.gameObject.tag, playerNum);
                    // Debug.Log("Player" + playerNum + " just shot " + col.gameObject.tag);
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player4") && destroyOnImpact)
                {
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
                else if (col.gameObject.tag != ("Player4"))
                {
                    numberOfCollides += 1;
                    destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
                }
 
      }
    }
 
 
    public static void destroyBullet(GameObject objectInNeedOfDestruction, float destroyTimer, bool hit, bool exDestroyOnTimer, bool exDestroyOnCollide)
    {
        Vector3 position = objectInNeedOfDestruction.transform.position;
        Quaternion rotation = objectInNeedOfDestruction.transform.rotation;
        if (BTD.destroyOnTimer && exDestroyOnTimer)
        {
            Destroy(objectInNeedOfDestruction, destroyTimer);
            BTD.showBulletAnimation(position, rotation, hit);
        }
        else if (BTD.destroyOnNumberOfCollides && BTD.numberOfCollides >= BTD.maxNumberOfCollides)
        {
            Destroy(objectInNeedOfDestruction, 0);
            BTD.showBulletAnimation(position, rotation, hit);
        }
    }
 
 
    public Transform hitPrefab;
    void showBulletAnimation(Vector3 position, Quaternion rotation, bool hit)
    {
        if (hit)
        {
            Transform hitParticleClone = Instantiate(hitPrefab, position, rotation) as Transform;
            Destroy(hitParticleClone.gameObject, 2f);
        }
 
    }
}

gameobject settings (rigidbody is set to continues):
​IMG: Setting Fireball


The second type of bullet is a rubber ball, effected by gravity and it should bounce of objects and ground, and get destroyed by other bullets or hitting and killing a player.

​IMG: Settings Rubberball

As you can see this parent has been set as a trigger as to be destroyed by a player or another bullet, the child has been set as collider:
​IMG: More information about the Rubberball

​GIF: Showing the problem with the rubber ball

With very low speeds the rubber ball seems to work a bit beter but thats not what I want.
GIF: Showing it works on low speed

Hello,

The quick thing you can try, is to play with your Physics2d settings where the collision matrix is.

You can find what each setting does here Unity - Manual: Physics 2D

For starters, you may want to increase Velocity Iterations and Position Iterations but the other settings may help you as well.

Now for the off-topic part.

I looked at your code and there is quite a lot of duplication just for checking

if (playerNum == 1) else if (playerNum == 2) etc ...

This does not scale well with more players. Imagine if at some point, you want to add 2 more players. You would have to change a lot of code.

My suggestion is to tag all players with the tag “Player”. Just “Player”, no number after it.
Now your OnTriggerEnter2D becomes

void OnTriggerEnter2D(Collider2D  col)
{
	Debug.Log(gameObject.name + " has collided with " + col.gameObject.name);
	Debug.Log("My layer: " + gameObject.layer);
	Debug.Log("Number of Impacts: " + numberOfCollides);
	
	if(col.gameObject.tag == ("Player"))
	{
		BulletTriggerDetection otherBTD = col.GetComponent<BulletTriggerDetection>();
		if(playerNum != otherBTD.playerNum)
		{
			col.gameObject.GetComponent<ShotPlayer>().Hurt(col.gameObject.tag, playerNum);
			// Debug.Log("Player" + playerNum + " just shot " + col.gameObject.tag);
			destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
			numberOfCollides += 1;
		}
	}
	else if(destroyOnImpact)
	{
		destroyBullet(gameObject, 0, true, destroyOnTimer, destroyOnNumberOfCollides);
	}
}

if your bullet actually go as fast as a real bullet (higher than the speed of sounds),
Use raycasting. This way you are sure to miss nothing.
only down side with raycasting, it does not work well for large object.