什么是碰撞检测?
碰撞检测,是物体与物体之间的关系。用来检测物体与物体之间是否发生了碰撞,例如:射击游戏,就是检测子弹与敌人的碰撞。
碰撞检测,主要归纳为以下两种实现方式:
- 外接矩形判定法
- 外接圆判定法
外接矩形判定法
当我们需要被检测的物体为矩形,或者形态接近于矩形,我们就可以把这个物体抽象为矩形,然后判断两个矩形是否发生了碰撞。实现该方法,我们需要做到两步:1. 找出物体的外接矩形 2. 对外接矩形进行碰撞检测。
📢 有些物体虽然看起来没有发生碰撞,但是在外接矩形检测法的规则下,它们就会被认定为发生了碰撞
在我们实际开发中,如果想要判定两个不规则的图形是否生了碰撞,是非常困难的!但是,我们使用“外接矩形法”去判断,将会特别简单,但是,“外接矩形法”有个明显的缺点,就是误差较大。即使是这样,它简单的判定方式可以减少我们的开发难度,依然备受欢迎,毕竟有得必有失,没有什么完美的算法,只能取最合适的算法
init1() {
class Ball {
constructor(x, y, radius, color) {
this.x = x
this.y = y
this.radius = radius
this.color = color
this.vx = Math.random() * 3 - 1 // 添加随机速度
this.vy = Math.random() * 3 - 1
this.opacity = 1
this.lifeTime = 400 // 添加生命周期属性(毫秒)
this.birthTime = Date.now() // 记录小球创建的时间
}
fill(context) {
context.save()
context.globalAlpha = this.opacity
context.beginPath()
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
context.fillStyle = this.color
context.fill()
context.closePath()
context.restore()
}
}
// 获取鼠标移动位置
const mouseMove = element => {
let mouse = { x: 0, y: 0 }
element.addEventListener('mousemove', e => {
let x = e.pageX
let y = e.pageY
mouse.x = e.clientX - element.getBoundingClientRect().left // 使用clientX和getBoundingClientRect计算鼠标位置
mouse.y = e.clientY - element.getBoundingClientRect().top
})
return mouse
}
// 判断矩形是否发生了碰撞
const checkRect = (rectA, rectB) => {
return !(
rectA.x + rectA.width < rectB.x ||
rectB.x + rectB.width < rectA.x ||
rectA.y + rectA.height < rectB.y ||
rectB.y + rectB.height < rectA.y
)
}
// 获取小球的外接矩形
const getRect = ball => {
return {
x: ball.x - ball.radius,
y: ball.y - ball.radius,
width: ball.radius * 2,
height: ball.radius * 2
}
}
var cnv = document.getElementById('myCanvas1')
var ctx = cnv.getContext('2d')
const ballA = new Ball(cnv.width / 2, cnv.height / 2, 30)
const rectA = getRect(ballA)
const getMouse = mouseMove(cnv)
;(function frame() {
window.requestAnimationFrame(frame)
ctx.clearRect(0, 0, cnv.width, cnv.height)
ballA.fill(ctx)
ctx.strokeRect(rectA.x, rectA.y, rectA.width, rectA.height)
const ballB = new Ball(getMouse.x, getMouse.y, 30, 'red')
const rectB = getRect(ballB)
ballB.fill(ctx)
ctx.strokeRect(rectB.x, rectB.y, rectB.width, rectB.height)
// 碰撞检测
if (checkRect(rectA, rectB)) {
ctx.save()
ctx.fillStyle = 'red'
ctx.font = '24px 黑体'
ctx.fillText('撞上了', 0, 20, 50)
ctx.restore()
} else {
ctx.fillStyle = 'black'
ctx.font = '24px 黑体'
ctx.fillText('没撞上', 0, 20, 50)
}
})()
}
外接圆判定法
外接圆判定法指的是,当一个物体为圆,或者接近圆,我们可以把这个物体抽象成圆,然后判断这两个圆是否发生了碰撞。实现该方法,我们需要做到两步:1. 找出物体的外接圆 2. 对外接圆进行碰撞检测。
如下图所示,有些物体虽然看起来没有发生碰撞,但是在外接圆检测法的规则下,它们就会被认定为发生了碰撞。
<canvas id="myCanvas" width="300" height="300"></canvas>
init() {
class Ball {
constructor(x, y, radius, color) {
this.x = x
this.y = y
this.radius = radius
this.color = color
this.vx = Math.random() * 3 - 1 // 添加随机速度
this.vy = Math.random() * 3 - 1
this.opacity = 1
this.lifeTime = 400 // 添加生命周期属性(毫秒)
this.birthTime = Date.now() // 记录小球创建的时间
}
fill(context) {
context.save()
context.globalAlpha = this.opacity
context.beginPath()
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
context.fillStyle = this.color
context.fill()
context.closePath()
context.restore()
}
}
// 获取鼠标移动位置
const mouseMove = element => {
let mouse = { x: 0, y: 0 }
element.addEventListener('mousemove', e => {
let x = e.pageX
let y = e.pageY
mouse.x = e.clientX - element.getBoundingClientRect().left // 使用clientX和getBoundingClientRect计算鼠标位置
mouse.y = e.clientY - element.getBoundingClientRect().top
})
return mouse
}
// 定义一个函数checkCircle,用于检查两个圆是否相交
const checkCircle = (circleA, circleB) => {
// 计算两个圆心在x轴上的距离差
const dx = circleB.x - circleA.x
// 计算两个圆心在y轴上的距离差
const dy = circleB.y - circleA.y
// 计算两个圆心之间的距离
const distance = Math.sqrt(dx * dx + dy * dy)
// 判断两个圆心之间的距离是否小于两个圆的半径之和,如果是则说明两个圆相交
if (distance < circleA.radius + circleB.radius) {
return true // 返回true表示两个圆相交
}
return false // 否则返回false表示两个圆不相交
}
var cnv = document.getElementById('myCanvas')
var ctx = cnv.getContext('2d')
const ballA = new Ball(cnv.width / 2, cnv.height / 2, 30)
const getMouse = mouseMove(cnv)
;(function frame() {
window.requestAnimationFrame(frame)
ctx.clearRect(0, 0, cnv.width, cnv.height)
ballA.fill(ctx)
let ballB = new Ball(getMouse.x, getMouse.y, 30, 'red')
ballB.fill(ctx)
if (checkCircle(ballA, ballB)) {
ctx.save()
ctx.fillStyle = 'red'
ctx.font = '24px 黑体'
ctx.fillText('撞上了', 0, 20, 50)
ctx.restore()
} else {
ctx.fillStyle = 'black'
ctx.font = '24px 黑体'
ctx.fillText('没撞上', 0, 20, 50)
}
})()
}
到此canvas碰撞检测学习结束,道阻且长,行则将至。与诸君共勉。 ⭐️