首页 > 解决方案 > 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>

标签: javascript2dphysicscollision

解决方案


推荐阅读