Skip to content

Instantly share code, notes, and snippets.

@Refsa
Last active October 22, 2019 09:30
Show Gist options
  • Save Refsa/47f1e35771e16e305f71daf533c8ec1f to your computer and use it in GitHub Desktop.
Save Refsa/47f1e35771e16e305f71daf533c8ec1f to your computer and use it in GitHub Desktop.
Seperate Axis Theorem on two box bounds
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BetterBounds
{
public Vector3 center;
public Vector3 size;
public Quaternion rotation;
public Vector3[ ] GetAxes ( )
{
return new Vector3[ ] { rotation * Vector3.right, rotation * Vector3.up, rotation * Vector3.forward };
}
public Vector3[ ] GetVertices ( )
{
var halfsize = size / 2f;
return new Vector3[ ]
{
center + rotation * new Vector3 (-halfsize.x, halfsize.y, halfsize.z),
center + rotation * new Vector3 (halfsize.x, halfsize.y, halfsize.z),
center + rotation * new Vector3 (-halfsize.x, halfsize.y, -halfsize.z),
center + rotation * new Vector3 (halfsize.x, halfsize.y, -halfsize.z),
center + rotation * new Vector3 (-halfsize.x, -halfsize.y, halfsize.z),
center + rotation * new Vector3 (halfsize.x, -halfsize.y, halfsize.z),
center + rotation * new Vector3 (-halfsize.x, -halfsize.y, -halfsize.z),
center + rotation * new Vector3 (halfsize.x, -halfsize.y, -halfsize.z)
};
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EditorPhysics : MonoBehaviour
{
[SerializeField] bool autoRotate;
Quaternion rotationB = Quaternion.Euler (0f, 0f, 0f);
void OnDrawGizmos ( )
{
if (autoRotate)
{
rotationB = Quaternion.Slerp (rotationB, rotationB * Quaternion.Euler (1f, 1f, 0f), 1f);
}
else
rotationB = Quaternion.Euler (0f, 45f, 0f);
BetterBounds a = new BetterBounds
{
center = Vector3.right * 7f,
size = Vector3.one * 10f,
rotation = Quaternion.Euler (0f, 0f, 0f)
};
BetterBounds b = new BetterBounds
{
center = Vector3.left * 5f,
size = Vector3.one * 10f,
rotation = rotationB
};
if (SeparateAxisTheorem.CheckCollision (a, b))
{
Gizmos.color = Color.red;
}
else
{
Gizmos.color = Color.green;
}
Gizmos.DrawWireSphere (a.center, 0.25f);
Gizmos.DrawWireSphere (b.center, 0.25f);
var oldmatrix = Gizmos.matrix;
Gizmos.matrix = Matrix4x4.TRS (a.center, a.rotation, a.size);
Gizmos.DrawWireCube (Vector3.zero, Vector3.one);
Gizmos.matrix = Matrix4x4.TRS (b.center, b.rotation, b.size);
Gizmos.DrawWireCube (Vector3.zero, Vector3.one);
Gizmos.matrix = oldmatrix;
}
}
/// <summary>
/// Math from: https://unitylist.com/p/5uc/Unity-Separating-Axis-SAT
/// </summary>
public static class SeparateAxisTheorem
{
public static bool CheckCollision (BetterBounds a, BetterBounds b)
{
var aAxes = a.GetAxes ( );
var bAxes = b.GetAxes ( );
var AllAxes = new Vector3[ ]
{
aAxes[0],
aAxes[1],
aAxes[2],
bAxes[0],
bAxes[1],
bAxes[2],
Vector3.Cross (aAxes[0], bAxes[0]),
Vector3.Cross (aAxes[0], bAxes[1]),
Vector3.Cross (aAxes[0], bAxes[2]),
Vector3.Cross (aAxes[1], bAxes[0]),
Vector3.Cross (aAxes[1], bAxes[1]),
Vector3.Cross (aAxes[1], bAxes[2]),
Vector3.Cross (aAxes[2], bAxes[0]),
Vector3.Cross (aAxes[2], bAxes[1]),
Vector3.Cross (aAxes[2], bAxes[2])
};
int aAxesLength = aAxes.Length;
int bAxesLength = bAxes.Length;
var aVertices = a.GetVertices ( );
var bVertices = b.GetVertices ( );
int aVertsLength = aVertices.Length;
int bVertsLength = bVertices.Length;
bool hasOverlap = false;
if (ProjectionHasOverlap (AllAxes.Length, AllAxes, bVertsLength, bVertices, aVertsLength, aVertices))
{
hasOverlap = true;
}
else if (ProjectionHasOverlap (AllAxes.Length, AllAxes, aVertsLength, aVertices, bVertsLength, bVertices))
{
hasOverlap = true;
}
return hasOverlap;
}
/// Detects whether or not there is overlap on all separating axes.
private static bool ProjectionHasOverlap (int aAxesLength, Vector3[ ] aAxes, int bVertsLength, Vector3[ ] bVertices, int aVertsLength, Vector3[ ] aVertices)
{
var penetrationAxes = new List<Vector3> ( );
var penetrationAxesDistance = new List<float> ( );
float minOverlap = float.PositiveInfinity;
Vector3 minOverlapAxis = Vector3.zero;
for (int i = 0; i < aAxesLength; i++)
{
float bProjMin = float.MaxValue, aProjMin = float.MaxValue;
float bProjMax = float.MinValue, aProjMax = float.MinValue;
Vector3 axis = aAxes[i];
// Handles the cross product = {0,0,0} case
if (aAxes[i] == Vector3.zero) return true;
for (int j = 0; j < bVertsLength; j++)
{
float val = FindScalarProjection ((bVertices[j]), axis);
if (val < bProjMin)
{
bProjMin = val;
}
if (val > bProjMax)
{
bProjMax = val;
}
}
for (int j = 0; j < aVertsLength; j++)
{
float val = FindScalarProjection ((aVertices[j]), axis);
if (val < aProjMin)
{
aProjMin = val;
}
if (val > aProjMax)
{
aProjMax = val;
}
}
float overlap = FindOverlap (aProjMin, aProjMax, bProjMin, bProjMax);
if (overlap < minOverlap)
{
minOverlap = overlap;
minOverlapAxis = axis;
penetrationAxes.Add (axis);
penetrationAxesDistance.Add (overlap);
}
if (overlap <= 0)
{
// Separating Axis Found Early Out
return false;
}
}
return true; // A penetration has been found
}
/// Calculates the scalar projection of one vector onto another, assumes normalised axes
private static float FindScalarProjection (Vector3 point, Vector3 axis)
{
return Vector3.Dot (point, axis);
}
/// Calculates the amount of overlap of two intervals.
private static float FindOverlap (float astart, float aend, float bstart, float bend)
{
if (astart < bstart)
{
if (aend < bstart)
{
return 0f;
}
return aend - bstart;
}
if (bend < astart)
{
return 0f;
}
return bend - astart;
}
}
@Refsa
Copy link
Author

Refsa commented Oct 22, 2019

Short gif showing collision on separate-axis: https://i.imgur.com/LBVjItu.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment