Skip to content

Instantly share code, notes, and snippets.

@JanHolger
Last active December 26, 2021 23:52
Show Gist options
  • Save JanHolger/31a5beae19d6271e0bbae16ef21d78df to your computer and use it in GitHub Desktop.
Save JanHolger/31a5beae19d6271e0bbae16ef21d78df to your computer and use it in GitHub Desktop.
A javascript function that can do basic collision detection on n-dimensional boxes
/**
@param a The middlepoint coordinates and half extent sizes of the floating box (e.g. [x, y, z, w, h, d])
@param b The middlepoint coordinates and half extent sizes of the static box (e.g. [x, y, z, w, h, d])
@param v The "velocity" or "motion" vector that should be applied
@return The "velocity" or "motion" vector that can be applied without overlapping
*/
function boxCollide(a, b, v) {
// Find the amount of dimensions from the element count of a
const dimCount = a.length / 2
// Create a list of all axes
let axes = [...(new Array(dimCount))].map((_, i) => i)
// Initialize
const result = [...(new Array(dimCount))]
// Calculate the deltas[n] between the adjecent sides of a[n] and b[n]
const deltas = [...(new Array(dimCount))].map((_, i) => {
if(a[i] < b[i]) {
return (b[i] - (b[dimCount+i] / 2)) - (a[i] + (a[dimCount+i] / 2))
} else {
return (b[i] + (b[dimCount+i] / 2)) - (a[i] - (a[dimCount+i] / 2))
}
})
// Calculate the ratios between deltas[n] and v[n]
const deltaRatios = [...(new Array(dimCount))].map((_, i) => v[i] == 0 ? 1 : (deltas[i] / v[i]))
while(axes.length > 0) {
let ca = axes[0]
for(let a of axes) { // Find the closest hit axis
if(deltaRatios[a] < deltaRatios[ca]) {
ca = a
}
}
// Remove the axis from the remaining axes
axes = axes.filter(s => s != ca)
const d = deltas[ca] // Delta of the current axis
// Check whether delta and velocity point in the same direction
// because otherwise they would drift away from each other
// and collision would be impossible
let canCollide = (d == 0) || (Math.sign(d) == Math.sign(v[ca]))
for(let ta = 0; ta < dimCount; ta++) {
if(ta == ca)
continue // Only check the other axes
if(!canCollide)
break
// If already calculated use the actual hit point, otherwise use the current ratio of the velocity of the axis
const td = (!axes.includes(ta)) ? result[ta] : (deltaRatios[ca] * v[ta])
// Calculate the min's and max's of a and b on the axis
const aMin = a[ta] - (a[dimCount+ta] / 2) + td
const aMax = a[ta] + (a[dimCount+ta] / 2) + td
const bMin = b[ta] - (b[dimCount+ta] / 2)
const bMax = b[ta] + (b[dimCount+ta] / 2)
// Check whether a and b can hit on this axis
canCollide =
((aMin > bMin) && (aMin < bMax)) ||
((aMax > bMin) && (aMax < bMax)) ||
((aMin <= bMin) && (aMax >= bMax)) ||
((aMin >= bMin) && (aMax <= bMax))
;
}
// Calculate the possible motion for the current axis
result[ca] = canCollide ? (Math.min(Math.abs(v[ca]), Math.abs(d)) * Math.sign(v[ca])) : v[ca]
}
return result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment