Workaround for Physics.OverlapBox

Unity's Physics.OverlapBox intermittently crashes on Android devices. The crash is a seg fault from inside libunity.so and looks like it's from the PhysX integration.

I found this forum post discussing the crash and saw that another user, Bunzaga, posted a useful Box-Box collision test algorithm that could be used in a workaround. I came up with a performant workaround by using Physics.OverlapSphere to find nearby colliders, and then using Bunzaga's box-box collision test to filter the nearby colliders. The box-box collision test is based on this useful gamasutra article.

A sphere fully surrounding a cube. The sphere's radius is half the length of the vector (boxlength, boxwidth, boxdepth).

A sphere fully surrounding a cube. The sphere's radius is half the length of the vector (boxlength, boxwidth, boxdepth).

The algorithm only works correctly for BoxColliders. If you need accurate SphereCollider results, you can extend it by adding a section which checks for SphereCollider's and implementing the Box-Sphere collision test found on this gamasutra article.


The code is posted in full below, and exposes a CollisionCheck.OverlapBox which will not crash on Android.

CollisionCheck.cs

using UnityEngine;
// adapted by 00jknight from Bunzaga's post on https://forum.unity3d.com/threads/solved-unfixable-bug-random-crashes-within-overlapbox-on-android.410868/
public static class CollisionCheck
{
    private static Vector3 _AB = new Vector3(); // Direction A to B
    private static float[] _R = new float[9]; // 3x3 Rotation
    private static float[] _AbsR = new float[9]; // 3x3 Rotation
    private static Vector3 _AX = new Vector3(); // A Axis
    private static Vector3 _BX = new Vector3(); // B Axis
 
    private static Vector3 _v1 = new Vector3();
    private static Vector3 _v2 = new Vector3();
    private static Vector3 _v3 = new Vector3();
 
    private static float[] _aRot = new float[9];
    private static float[] _bRot = new float[9];
 
    private static float ar = 0.0f, br = 0.0f;
 
    private static float[] _identityMatrix = new float[9] { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };

    private static Collider[] sphere_cast_collider_storage = new Collider[32];
	
	public static int OverlapBox(Vector3 center, Vector3 halfExtents, Quaternion rotation, Collider[] colliderStorage, LayerMask killLayer) {
		var radius = 0.5f * new Vector3(halfExtents.x*2.0f, halfExtents.y*2.0f, halfExtents.z*2.0f).magnitude;
		int numOverlaps = Physics.OverlapSphereNonAlloc(center, radius, sphere_cast_collider_storage, killLayer.value);
		int numBoxOverlaps = 0;
		for (int i = 0; i < numOverlaps; i++) {
			var nearbyCollider = sphere_cast_collider_storage[i];
			//Debug.Log("Testing against " + nearbyCollider.gameObject.name);
			var nearbyBoxCollider = nearbyCollider.GetComponent();
			var nearbyColliderHalfExtents = nearbyBoxCollider != null ? HalfExtentsFromBoxCollider(nearbyBoxCollider) : nearbyCollider.transform.localScale * 0.5f;
			var nearbyColliderPosition = nearbyBoxCollider != null ? PositionFromCollider(nearbyBoxCollider) : nearbyCollider.transform.position;
			//Debug.Log("Position: " + nearbyCollider.transform.position);
			//Debug.Log("Rotation: " + nearbyCollider.transform.rotation);
			//Debug.Log("Half Extents: " + nearbyColliderHalfExtents);
			//Debug.Log("Has box: " + (nearbyBoxCollider != null).ToString());
			if (CollisionCheck.Box_Box(center, halfExtents, rotation, nearbyColliderPosition, nearbyColliderHalfExtents, nearbyCollider.transform.rotation)) {
				colliderStorage[numBoxOverlaps++] = sphere_cast_collider_storage[i];
			}
		}
		return numBoxOverlaps;
	}
	public static Vector3 HalfExtentsFromBoxCollider(BoxCollider boxCollider1) {
		return new Vector3(boxCollider1.size.x * boxCollider1.transform.localScale.x * 0.5f, boxCollider1.size.y * boxCollider1.transform.localScale.y * 0.5f, boxCollider1.size.z * boxCollider1.transform.localScale.z * 0.5f);
	}
	public static Vector3 PositionFromCollider(BoxCollider collider) {
		return collider.transform.position + collider.center;
	}
    
    
    // Adapted from: http://www.gamasutra.com/view/feature/131790/simple_intersection_tests_for_games.php?print=1
    public static bool Box_Box (Vector3 aPos, Vector3 aSize, Quaternion aQuat, Vector3 bPos, Vector3 bSize, Quaternion bQuat)
    {
        ar = 0.0f;
        br = 0.0f;
 
        QuaternionToFloatArray(aQuat, _aRot);
        QuaternionToFloatArray(bQuat, _bRot);
 
 
        for (int i = 0, i1 = 0; i < 3; i++, i1 += 3)
        {
            _AX.Set(_aRot[i1], _aRot[i1 + 1], _aRot[i1 + 2]);
            for (int j = 0, j1 = 0; j < 3; j++, j1 += 3)
            {
                _BX.Set(_bRot[j1], _bRot[j1 + 1], _bRot[j1 + 2]);
                _R[i1 + j] = Vector3.Dot(_AX, _BX);
            }
        }
 
        _AB = bPos - aPos;
 
        _v1.Set(_aRot[0], _aRot[1], _aRot[2]);
        _v2.Set(_aRot[3], _aRot[4], _aRot[5]);
        _v3.Set(_aRot[6], _aRot[7], _aRot[8]);
 
        _AB.Set(Vector3.Dot(_AB, _v1), Vector3.Dot(_AB, _v2), Vector3.Dot(_AB, _v3));
 
        for (int i = 0; i < 9; i++)
        {
            _AbsR[i] = Mathf.Abs(_R[i]) + 0.001f;
        }
        // Test axes L = A0, L = A1, L = A2
        for (int i = 0, i1 = 0; i < 3; i++, i1 += 3)
        {
            ar = aSize[i];
            br = (bSize[0] * _AbsR[i1]) + (bSize[1] * _AbsR[i1 + 1]) + (bSize[2] * _AbsR[i1 + 2]);
            if (Mathf.Abs(_AB[i]) > (ar + br)) { return false; }
        }
        // Test axes L = B0, L = B1, L = B2
        for (int i = 0; i < 3; i++)
        {
            ar = (aSize[0] * _AbsR[i]) + (aSize[1] * _AbsR[i + 3]) + (aSize[2] * _AbsR[i + 6]);
            br = bSize[i];
            if (Mathf.Abs((_AB[0] * _R[i]) + (_AB[1] * _R[i + 3]) + (_AB[2] * _R[i + 6])) > (ar + br)) { return false; }
        }
        // Test axis L = A0 x B0
        ar = (aSize[1] * _AbsR[6]) + (aSize[2] * _AbsR[3]);
        br = (bSize[1] * _AbsR[2]) + (bSize[2] * _AbsR[1]);
        if (Mathf.Abs(_AB[2] * _R[3] - _AB[1] * _R[6]) > (ar + br)) { return false; }
        // Test axis L = A0 x B1
        ar = (aSize[1] * _AbsR[7]) + (aSize[2] * _AbsR[4]);
        br = (bSize[0] * _AbsR[2]) + (bSize[2] * _AbsR[0]);
        if (Mathf.Abs((_AB[2] * _R[4]) - (_AB[1] * _R[7])) > (ar + br)) { return false; }
        // Test axis L = A0 x B2
        ar = aSize[1] * _AbsR[8] + aSize[2] * _AbsR[5];
        br = bSize[0] * _AbsR[1] + bSize[1] * _AbsR[0];
        if (Mathf.Abs((_AB[2] * _R[5]) - (_AB[1] * _R[8])) > (ar + br)) { return false; }
        // Test axis L = A1 x B0
        ar = (aSize[0] * _AbsR[6]) + (aSize[2] * _AbsR[0]);
        br = (bSize[1] * _AbsR[5]) + (bSize[2] * _AbsR[4]);
        if (Mathf.Abs((_AB[0] * _R[6]) - (_AB[2] * _R[0])) > (ar + br)) { return false; }
        // Test axis L = A1 x B1
        ar = (aSize[0] * _AbsR[7]) + (aSize[2] * _AbsR[1]);
        br = (bSize[0] * _AbsR[5]) + (bSize[2] * _AbsR[3]);
        if (Mathf.Abs((_AB[0] * _R[7]) - (_AB[2] * _R[1])) > (ar + br)) { return false; }
        // Test axis L = A1 x B2
        ar = (aSize[0] * _AbsR[8]) + (aSize[2] * _AbsR[2]);
        br = (bSize[0] * _AbsR[4]) + (bSize[1] * _AbsR[3]);
        if (Mathf.Abs((_AB[0] * _R[8]) - (_AB[2] * _R[2])) > (ar + br)) { return false; }
        // Test axis L = A2 x B0
        ar = (aSize[0] * _AbsR[3]) + (aSize[1] * _AbsR[0]);
        br = (bSize[1] * _AbsR[8]) + (bSize[2] * _AbsR[7]);
        if (Mathf.Abs((_AB[1] * _R[0]) - (_AB[0] * _R[3])) > (ar + br)) { return false; }
        // Test axis L = A2 x B1
        ar = (aSize[0] * _AbsR[4]) + (aSize[1] * _AbsR[1]);
        br = (bSize[0] * _AbsR[8]) + (bSize[2] * _AbsR[6]);
        if (Mathf.Abs((_AB[1] * _R[1]) - (_AB[0] * _R[4])) > (ar + br)) { return false; }
        // Test axis L = A2 x B2
        ar = (aSize[0] * _AbsR[4]) + (aSize[1] * _AbsR[2]);
        br = (bSize[0] * _AbsR[7]) + (bSize[1] * _AbsR[6]);
        if (Mathf.Abs((_AB[1] * _R[2]) - (_AB[0] * _R[5])) > (ar + br)) { return false; }
        return true;
    }
 
    // Adapted from: http://www.mrelusive.com/publications/papers/SIMD-From-Quaternion-to-Matrix-and-Back.pdf
    private static float[] QuaternionToFloatArray(Quaternion aQuat, float[] aFlatMatrix3x3 )
    {
        if(aFlatMatrix3x3 == null) {
            aFlatMatrix3x3 = new float[9];
        }
 
        aFlatMatrix3x3[0] = 1- (2.0f * (aQuat.y * aQuat.y)) - (2.0f * (aQuat.z * aQuat.z));
        aFlatMatrix3x3[1] = (2.0f * (aQuat.x * aQuat.y)) + (2.0f * (aQuat.w * aQuat.z));
        aFlatMatrix3x3[2] = (2.0f * (aQuat.x * aQuat.z)) - (2.0f * (aQuat.w * aQuat.y));
 
        aFlatMatrix3x3[3] = (2.0f * (aQuat.x * aQuat.y)) - (2.0f * (aQuat.w * aQuat.z));
        aFlatMatrix3x3[4] = 1.0f - (2.0f * (aQuat.x * aQuat.x)) - (2.0f * (aQuat.z * aQuat.z));
        aFlatMatrix3x3[5] = (2.0f * (aQuat.y * aQuat.z)) + (2.0f * (aQuat.w * aQuat.x));
 
        aFlatMatrix3x3[6] = (2.0f * (aQuat.x * aQuat.z)) + (2.0f * (aQuat.w * aQuat.y));
        aFlatMatrix3x3[7] = (2.0f * (aQuat.y * aQuat.z)) - (2.0f * (aQuat.w * aQuat.x));
        aFlatMatrix3x3[8] = 1.0f - (2.0f * (aQuat.x * aQuat.x)) - (2.0f * (aQuat.y * aQuat.y));
 
        return aFlatMatrix3x3;
    }
}
In