javascript - Vanilla Javascript中的圆/线碰撞问题
问题描述
我正在为一个简单的自上而下游戏开发一个伪物理引擎,我需要处理各种形状的碰撞,但我遇到了圆/线碰撞的问题。
对于与单条线碰撞的圆圈,它按预期工作,但是当圆圈同时与两条线(凹边)碰撞时,就会出现问题,如果我按住移动键,圆圈会与它们重叠(在这个例子中案例 D)。
我只需要知道它为什么会发生,也许如何解决。谢谢,如果你能帮忙!
WASD移动圈子:https ://jsfiddle.net/bh840rua/1/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Script</title>
</head>
<body>
<canvas id="canvas" width="1600px" height="900px"></canvas>
<script>
const canvas = document.getElementById("canvas")
const context = canvas.getContext("2d")
const keys = []
document.addEventListener("keydown", (e) => {
keys[e.keyCode] = true
})
document.addEventListener("keyup", (e) => {
keys[e.keyCode] = false
})
class Circle {
constructor(x, y, radius) {
this.center = new Vector(x, y)
this.radius = radius
this.velocity = new Vector(0, 0)
}
move() {
let collision = this.collide(line)
if (collision.collided) {
let collisionLine = new Line(collision.intersection.x, collision.intersection.y,
this.center.x, this.center.y)
let newForce = new Vector(collisionLine.vector.x, collisionLine.vector.y)
newForce.normalize()
this.center.x = collision.intersection.x + newForce.x * this.radius
this.center.y = collision.intersection.y + newForce.y * this.radius
}
collision = this.collide(line2)
if (collision.collided) {
let collisionLine = new Line(collision.intersection.x, collision.intersection.y,
this.center.x, this.center.y)
let newForce = new Vector(collisionLine.vector.x, collisionLine.vector.y)
newForce.normalize()
this.center.x = collision.intersection.x + newForce.x * this.radius
this.center.y = collision.intersection.y + newForce.y * this.radius
}
this.center.x += this.velocity.x
this.center.y += this.velocity.y
}
checkVelocity() {
let direction = [0, 0, 0, 0]
if (keys[65]) {
direction[0] = -1
} else direction[0] = 1
if (keys[68]) {
direction[1] = 1
} else direction[1] = -1
if (keys[87]) {
direction[2] = -1
} else direction[2] = 1
if (keys[83]) {
direction[3] = 1
} else direction[3] = -1
this.velocity.x = direction[0] + direction[1]
this.velocity.y = direction[2] + direction[3]
this.velocity.normalize()
}
collide(object) {
let dx31 = this.center.x - object.start.x
let dx21 = object.end.x - object.start.x
let dy31 = this.center.y - object.start.y
let dy21 = object.end.y - object.start.y
let d = ((dx21 * dx21) + (dy21 * dy21))
if (d != 0) d = ((dx31 * dx21) + (dy31 * dy21)) / d
if (d < 0.0) d = 0
if (d > 1.0) d = 1
let dx = this.center.x - (object.start.x + (dx21 * d))
let dy = this.center.y - (object.start.y + (dy21 * d))
let magnitude = Math.sqrt((dx * dx) + (dy * dy))
if (circle.radius >= magnitude) {
return {
collided: true,
intersection: {
x: this.center.x - dx,
y: this.center.y - dy
}
}
} else return {
collided: false
}
}
render() {
context.strokeStyle = "black"
context.lineWidth = "1"
context.beginPath()
context.arc(this.center.x, this.center.y, this.radius, 0, Math.PI * 2)
context.stroke()
context.closePath()
}
}
class Line {
constructor(startX, startY, endX, endY) {
this.start = new Vector(startX, startY)
this.end = new Vector(endX, endY)
}
get vector() {
return new Vector(this.end.x - this.start.x, this.end.y - this.start.y)
}
get angleRad() {
return Math.atan(this.vector.y / this.vector.x)
}
get angleDeg() {
return Math.atan(this.vector.y / this.vector.x) * 180 / Math.PI
}
get magnitude() {
return Math.sqrt(Math.pow(this.vector.x, 2) + Math.pow(this.vector.y, 2))
}
render() {
context.strokeStyle = "black"
context.lineWidth = "1"
context.beginPath()
context.moveTo(this.start.x, this.start.y)
context.lineTo(this.end.x, this.end.y)
context.stroke()
context.closePath()
}
}
class Vector {
constructor(x, y) {
this.x = x
this.y = y
}
get angleRad() {
return Math.atan(this.y / this.x)
}
get angleDeg() {
return Math.atan(this.y / this.x) * 180 / Math.PI
}
get magnitude() {
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2))
}
normalize() {
if (this.magnitude) {
let len = this.magnitude
this.x /= len
this.y /= len
}
}
}
const line = new Line(208, 200, 400, 200)
const line2 = new Line(208, 300, 400, 200)
const circle = new Circle(228, 150, 25);
let gameLoop = (timestamp) => {
context.clearRect(0, 0, canvas.width, canvas.height)
circle.checkVelocity()
circle.move()
line.render()
line2.render()
circle.render()
window.requestAnimationFrame(gameLoop)
}
window.requestAnimationFrame(gameLoop)
</script>
</body>
</html>
解决方案
推荐阅读
- prometheus - Prometheus 中合成序列的条件规则
- java - Formatting LocalDate in Java
- android - Gson 字符串中是否有要在 SharedPreferences 中存档的最大长度?
- java - Java 日期格式来自 yyyy-MM-dd'T'HH:mm:ss.SSS+ZZZZ
- email - 抄送和密送不工作,我收到错误“无效的电子邮件:[L ;@34c5bdeb”
- spring - 我正在使用 Spring、Hibernate 和 h2 构建电子商务网站。我收到此错误
- macos - 不透明的 NSWindow 和 WindowServer 性能不佳
- javascript - javascript中键的解构参数和对象
- java - 如何从 Java Swing/GUI 调用 JavaFXApplication?这甚至可能吗?
- intro.js - 如何以编程方式在 1 个 introjs 步骤中包含多个 div 元素