如何实现盒子碰撞检测?
前言
当我们在游戏或者图形应用程序中渲染和操作各种物体时,往往需要判断物体之间是否发生了碰撞。盒子碰撞检测是一种简单而有效的碰撞检测方式,它使用矩形盒子来粗略地模拟物体形状,再通过比较盒子之间的位置和大小关系来判断物体是否相交。与其他高级的碰撞检测算法相比,盒子碰撞检测具有计算速度快、实现简单等优点,在2D游戏开发中得到广泛应用。在本篇文章中,我们将介绍盒子碰撞检测的基本原理和实现过程,并给出一些示例代码,帮助读者更好地理解和应用这种碰撞检测。
1.什么是盒子碰撞?
盒子碰撞检测是一种在2D游戏开发中常用的碰撞检测算法。它使用矩形的盒子来简单地模拟物体的形状,并通过判断盒子之间的位置和大小关系来判断物体是否重叠或者相交。
具体来说,盒子碰撞检测会为每个物体维护一个矩形的盒子,这个盒子的位置和大小与物体的形状密切相关。当需要检测两个物体是否相交时,只需要比较它们对应的盒子是否有重叠部分即可。如果盒子之间没有重叠,则可以判定两个物体没有发生碰撞;否则,需要进一步进行更加细致的检测或者处理。
盒子碰撞检测虽然比不上更加复杂的碰撞检测算法如SAT(分离轴测试)等,在处理更加复杂的物体形状时可能不是很准确。但由于它快速、简单,且能满足大多数情况下的需求,因此在许多2D游戏中得到广泛应用。
2.如何来检测盒子碰撞?
通过坐标比对来检测盒子碰撞,接下来分别介绍两种方案来检测盒子碰撞,第一种四个角的坐标来比对,第二种是通过中心坐标点来比对。
2.1 盒子四个点的坐标比对
通俗来讲,移动盒子的四个方向的偏移量,加上自身的宽度,如果小于目标盒子的四个方向的偏移量,这种情况就是没有出现碰撞。如果都不是上面这种情况,则出现了盒子碰撞。
// 方案1: 比较四个坐标点
if(
// 左侧
box1Info.x + box1Info.w < box2Info.x ||
// 上侧
box1Info.y + box1Info.h < box2Info.y ||
// 右侧
box1Info.x > box2Info.x + box2Info.w ||
// 下侧
box1Info.y > box2Info.y + box2Info.h
){
return false;
}else {
return true;
}
2.2 盒子中心坐标比对
首先获取盒子的中心坐标,盒子水平或垂直的偏移量,加上自身宽度或者高度的一半,然后获取两个盒子的中心坐标的差值,如果差值小于等于两个盒子的宽度之和除以2,这种情况就会出现盒子碰撞,不满足这个条件则没有发生碰撞。
// 方案2: 比较中心坐标点
const box1Center = {
x: box1Info.x + box1Info.w / 2,
y: box1Info.y + box1Info.h / 2
}
const box2Center = {
x: box2Info.x + box2Info.w / 2,
y: box2Info.y + box2Info.h / 2
}
const diff = {
x: Math.abs(box1Center.x - box2Center.x),
y: Math.abs(box1Center.y - box2Center.y)
}
if(diff.x <= (box1Info.w + box2Info.w)/2 && diff.y <= (box1Info.h+ box2Info.h)/2){
return true;
}else {
return false;
}
3.盒子碰撞的应用场景
- 游戏引擎中人物是否接触碰撞检测
- 低代码平台拖动元素与另一个元素是否接触检测
- 动画效果检测元素是否碰撞到其它物体来做对应的操作
4.盒子碰撞检测源代码
// 盒子容器
const wrapBox = document.querySelector(".wrap-box");
// box1 、box2
const allBox = wrapBox.querySelectorAll(".box");
/**
* @desc 盒子父容器添加鼠标事件
* 盒子事件通过时间代理来处理
*/
wrapBox.addEventListener('mousedown', (event)=>{
const { target } = event;
const className = target.className.split(" ");
if(className.includes('box')){
// 记录鼠标的开始位置
target._x = event.clientX - target.offsetLeft;
target._y = event.clientY - target.offsetTop;
window.addEventListener('mousemove', onMoveHandler, false);
wrapBox.addEventListener('mouseup', onUpHandler, false);
}
/**
* @desc 检测盒子是否碰撞
*/
function isCollision(boxes, checkType = 1){
const box1Info = getBoxInfo(boxes[0]);
const box2Info = getBoxInfo(boxes[1]);
// 方案1: 比较四个坐标点
if(checkType === 1){
if(
// 左侧
box1Info.x + box1Info.w < box2Info.x ||
// 上侧
box1Info.y + box1Info.h < box2Info.y ||
// 右侧
box1Info.x > box2Info.x + box2Info.w ||
// 下侧
box1Info.y > box2Info.y + box2Info.h
){
return false;
}else {
return true;
}
}else {
// 方案2: 比较中心坐标点
const box1Center = {
x: box1Info.x + box1Info.w / 2,
y: box1Info.y + box1Info.h / 2
}
const box2Center = {
x: box2Info.x + box2Info.w / 2,
y: box2Info.y + box2Info.h / 2
}
const diff = {
x: Math.abs(box1Center.x - box2Center.x),
y: Math.abs(box1Center.y - box2Center.y)
}
if(diff.x <= (box1Info.w + box2Info.w)/2 && diff.y <= (box1Info.h+ box2Info.h)/2){
return true;
}else {
return false;
}
}
}
/**
* @desc 鼠标移动处理事件
*/
function onMoveHandler(e){
const x = e.clientX - target._x;
const y = e.clientY - target._y;
// 移动盒子,设置偏移量
target.style.top = y + 'px';
target.style.left = x + 'px';
const isCollisionFlag = isCollision(allBox);
console.log('盒子是否碰撞: ', isCollisionFlag);
}
/**
* @desc 鼠标松开处理事件
*/
function onUpHandler(){
window.removeEventListener('mousemove', onMoveHandler, false);
wrapBox.removeEventListener('mouseup', onUpHandler, false);
}
/**
* @desc 获取盒子相关数据
*/
function getBoxInfo(box){
return {
x: box.offsetLeft,
y: box.offsetTop,
w: box.offsetWidth,
h: box.offsetHeight
}
}
})
转载自:https://juejin.cn/post/7236668766956142653