圆形/矩形碰撞检测,鼠标控制?

zujrkrfu  于 2021-09-23  发布在  Java
关注(0)|答案(0)|浏览(158)

目前,我有一个碰撞检测的圆反弹的墙壁,其他圆和矩形。我可以选择矩形由鼠标控制,但我在代码段中禁用了它,因为我希望其中一个圆由鼠标移动控制,我尝试通过将与bat类相同的值赋予player类来实现这一点,但值变为nan。我有什么办法可以做到这一点吗?

const canvas = document.querySelector('canvas')
const ctx = canvas.getContext("2d");
const mouse = { x: 0, y: 0, button: false }

function mouseEvents(e) {
    mouse.x = e.pageX;
    mouse.y = e.pageY;
    mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down", "up", "move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));

// short cut vars 
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center 
var ch = h / 2;
const gravity = 0;
var balls = []
var bats = []

// constants and helpers
const PI2 = Math.PI * 2;
const setStyle = (ctx, style) => { Object.keys(style).forEach(key => ctx[key] = style[key]) };

function random(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function distance(x1, y1, x2, y2) {
    const xDist = x2 - x1;
    const yDist = y2 - y1;
    return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2));
}

// the ball
class ball {
    constructor() {
        this.r = 25
        this.x = random(50, 1500)
        this.y = random(50, 1500)
        this.dx = 15
        this.dy = 15
        this.mass = 1
        this.maxSpeed = 15
        this.style = {
            lineWidth: 12,
            strokeStyle: "green"
        }
    }
    draw(ctx) {
        setStyle(ctx, this.style);
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r - this.style.lineWidth * 0.45, 0, PI2);
        ctx.stroke();
    }
    update() {
        this.dy += gravity;
        var speed = Math.sqrt(this.dx * this.dx + this.dy * this.dy);
        var x = this.x + this.dx;
        var y = this.y + this.dy;

        if (y > canvas.height - this.r) {
            y = (canvas.height - this.r) - (y - (canvas.height - this.r));
            this.dy = -this.dy;
        }
        if (y < this.r) {
            y = this.r - (y - this.r);
            this.dy = -this.dy;
        }
        if (x > canvas.width - this.r) {
            x = (canvas.width - this.r) - (x - (canvas.width - this.r));
            this.dx = -this.dx;
        }
        if (x < this.r) {
            x = this.r - (x - this.r);
            this.dx = -this.dx;
        }
        this.x = x;
        this.y = y;
        if (speed > this.maxSpeed) { // if over speed then slow the ball down gradualy
            var reduceSpeed = this.maxSpeed + (speed - this.maxSpeed) * 0.9; // reduce speed if over max speed
            this.dx = (this.dx / speed) * reduceSpeed;
            this.dy = (this.dy / speed) * reduceSpeed;
        }
        for (var i = 0; i < balls.length; i++) {
            if (this === balls[i]) continue
            if (distance(this.x, this.y, balls[i].x, balls[i].y) < this.r * 2) {
                resolveCollision(this, balls[i])
            }
        }

    }
}
class player {
    constructor() {
        this.r = 50
        this.x = random(50, 1500)
        this.y = random(50, 1500)
        this.dx = 0.2
        this.dy = 0.2
        this.mass = 1
        this.maxSpeed = 1000
        this.style = {
            lineWidth: 12,
            strokeStyle: "blue"
        }
    }
    draw(ctx) {
        setStyle(ctx, this.style);
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r - this.style.lineWidth * 0.45, 0, PI2);
        ctx.stroke();
    }
    update() {
        this.dx = mouse.x - this.x;
        this.dy = mouse.y - this.y;
        var x = this.x + this.dx;
        var y = this.y + this.dy;
        x < this.width / 2 && (x / 2);
        y < this.height / 2 && (y / 2);
        x > canvas.width / 2 && (x = canvas.width / 2);
        y > canvas.height / 2 && (y = canvas.height / 2);
        this.dx = x - this.x;
        this.dy = y - this.y;
        this.x = x;
        this.y = y;
        for (var i = 0; i < balls.length; i++) {
            if (this === balls[i]) continue
            if (distance(this.x, this.y, balls[i].x, balls[i].y) < this.r * 2) {
                resolveCollision(this, balls[i])
            }
        }

    }
}
const ballShadow = { 
        r: 50,
        x: 50,
        y: 50,
        dx: 0.2,
        dy: 0.2,
    }
//bat
class bat {
    constructor(x, y, w, h) {
        this.x = x
        this.y = y
        this.dx = 0
        this.dy = 0
        this.width = w
        this.height = h
        this.style = {
            lineWidth: 2,
            strokeStyle: "black",
        }
    }
    draw(ctx) {
        setStyle(ctx, this.style);
        ctx.strokeRect(this.x - this.width / 2, this.y - this.height / 2, this.width, this.height);
    }

    update() {
        this.dx = mouse.x - this.x;
        this.dy = mouse.y - this.y;
        var x = this.x + this.dx;
        var y = this.y + this.dy;
        x < this.width / 2 && (x = this.width / 2);
        y < this.height / 2 && (y = this.height / 2);
        x > canvas.width - this.width / 2 && (x = canvas.width - this.width / 2);
        y > canvas.height - this.height / 2 && (y = canvas.height - this.height / 2);
        this.dx = x - this.x;
        this.dy = y - this.y;
        this.x = x;
        this.y = y;

    }
}

function doBatBall(bat, ball) {
    var mirrorX = 1;
    var mirrorY = 1;

    const s = ballShadow; // alias
    s.x = ball.x;
    s.y = ball.y;
    s.dx = ball.dx;
    s.dy = ball.dy;
    s.x -= s.dx;
    s.y -= s.dy;

    // get the bat half width height
    const batW2 = bat.width / 2;
    const batH2 = bat.height / 2;

    // and bat size plus radius of ball
    var batH = batH2 + ball.r;
    var batW = batW2 + ball.r;

    // set ball position relative to bats last pos
    s.x -= bat.x;
    s.y -= bat.y;

    // set ball delta relative to bat
    s.dx -= bat.dx;
    s.dy -= bat.dy;

    // mirror x and or y if needed
    if (s.x < 0) {
        mirrorX = -1;
        s.x = -s.x;
        s.dx = -s.dx;
    }
    if (s.y < 0) {
        mirrorY = -1;
        s.y = -s.y;
        s.dy = -s.dy;
    }

    // bat now only has a bottom, right sides and bottom right corner
    var distY = (batH - s.y); // distance from bottom 
    var distX = (batW - s.x); // distance from right

    if (s.dx > 0 && s.dy > 0) { return } // ball moving away so no hit

    var ballSpeed = Math.sqrt(s.dx * s.dx + s.dy * s.dy) // get ball speed relative to bat

    // get x location of intercept for bottom of bat
    var bottomX = s.x + (s.dx / s.dy) * distY;

    // get y location of intercept for right of bat
    var rightY = s.y + (s.dy / s.dx) * distX;

    // get distance to bottom and right intercepts
    var distB = Math.hypot(bottomX - s.x, batH - s.y);
    var distR = Math.hypot(batW - s.x, rightY - s.y);
    var hit = false;

    if (s.dy < 0 && bottomX <= batW2 && distB <= ballSpeed && distB < distR) { // if hit is on bottom and bottom hit is closest
        hit = true;
        s.y = batH - s.dy * ((ballSpeed - distB) / ballSpeed);
        s.dy = -s.dy;
    }
    if (!hit && s.dx < 0 && rightY <= batH2 && distR <= ballSpeed && distR <= distB) { // if hit is on right and right hit is closest
        hit = true;
        s.x = batW - s.dx * ((ballSpeed - distR) / ballSpeed);;
        s.dx = -s.dx;
    }
    if (!hit) { // if no hit may have intercepted the corner. 
        // find the distance that the corner is from the line segment from the balls pos to the next pos
        const u = ((batW2 - s.x) * s.dx + (batH2 - s.y) * s.dy) / (ballSpeed * ballSpeed);

        // get the closest point on the line to the corner
        var cpx = s.x + s.dx * u;
        var cpy = s.y + s.dy * u;

        // get ball radius squared
        const radSqr = ball.r * ball.r;

        // get the distance of that point from the corner squared
        const dist = (cpx - batW2) * (cpx - batW2) + (cpy - batH2) * (cpy - batH2);

        // is that distance greater than ball radius
        if (dist > radSqr) { return } // no hit

        // solves the triangle from center to closest point on balls trajectory
        var d = Math.sqrt(radSqr - dist) / ballSpeed;

        // intercept point is closest to line start
        cpx -= s.dx * d;
        cpy -= s.dy * d;

        // get the distance from the ball current pos to the intercept point
        d = Math.hypot(cpx - s.x, cpy - s.y);

        // is the distance greater than the ball speed then its a miss
        if (d > ballSpeed) { return } // no hit return

        s.x = cpx; // position of contact
        s.y = cpy;

        // find the normalised tangent at intercept point 
        const ty = (cpx - batW2) / ball.r;
        const tx = -(cpy - batH2) / ball.r;

        // calculate the reflection vector
        const bsx = s.dx / ballSpeed; // normalise ball speed
        const bsy = s.dy / ballSpeed;
        const dot = (bsx * tx + bsy * ty) * 2;

        // get the distance the ball travels past the intercept
        d = ballSpeed - d;

        // the reflected vector is the balls new delta (this delta is normalised)
        s.dx = (tx * dot - bsx);
        s.dy = (ty * dot - bsy);

        // move the ball the remaining distance away from corner
        s.x += s.dx * d;
        s.y += s.dy * d;

        // set the ball delta to the balls speed
        s.dx *= ballSpeed
        s.dy *= ballSpeed
        hit = true;
    }

    // if the ball hit the bat restore absolute position
    if (hit) {
        // reverse mirror
        s.x *= mirrorX;
        s.dx *= mirrorX;
        s.y *= mirrorY;
        s.dy *= mirrorY;

        // remove bat relative position
        s.x += bat.x;
        s.y += bat.y;

        // remove bat relative delta
        s.dx += bat.dx;
        s.dy += bat.dy;

        // set the balls new position and delta
        ball.x = s.x;
        ball.y = s.y;
        ball.dx = s.dx;
        ball.dy = s.dy;
    }
}

function rotate(velocity, angle) {
    const rotatedVelocities = {
        x: velocity.x * Math.cos(angle) + velocity.y * Math.sin(angle),
        y: -velocity.x * Math.sin(angle) + velocity.y * Math.cos(angle)
    };

    return rotatedVelocities;
}

function resolveCollision(particle, otherParticle) {
    const xVelocityDiff = particle.dx - otherParticle.dx;
    const yVelocityDiff = particle.dy - otherParticle.dy;

    const xDist = otherParticle.x - particle.x;
    const yDist = otherParticle.y - particle.y;

    if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {

        const angle = Math.atan2(otherParticle.y - particle.y, otherParticle.x - particle.x);
        const m1 = particle.mass;
        const m2 = otherParticle.mass;

        const u1 = rotate({ x: particle.dx, y: particle.dy }, angle);
        const u2 = rotate({ x: otherParticle.dx, y: otherParticle.dy }, angle);

        const v1 = {
            x: u1.x * (m1 - m2) / (m1 + m2) + u2.x * 2 * m2 / (m1 + m2),
            y: u1.y
        };
        const v2 = {
            x: u2.x * (m1 - m2) / (m1 + m2) + u1.x * 2 * m2 / (m1 + m2),

            y: u2.y
        };

        const vFinal1 = rotate(v1, -angle);
        const vFinal2 = rotate(v2, -angle);

        particle.dx = vFinal1.x;
        particle.dy = vFinal1.y;

        otherParticle.dx = vFinal2.x;
        otherParticle.dy = vFinal2.y;
    }
}

for (let i = 0; i < 10; i++) {
    balls.push(new ball())
}
balls.push(new player())
    //x,y,w,h
bats.push(new bat((window.innerWidth / 2) - 150, window.innerHeight / 2, 10, 100))
bats.push(new bat((window.innerWidth / 2) + 150, window.innerHeight / 2, 10, 100))

// main update function
function update(timer) {

    if (w !== innerWidth || h !== innerHeight) {
        cw = (w = canvas.width = innerWidth) / 2;
        ch = (h = canvas.height = innerHeight) / 2;
    }

    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    ctx.clearRect(0, 0, w, h);

    // move bat and ball
    for (var i = 0; i < balls.length; i++) {
        for (var j = 0; j < bats.length; j++) {
            doBatBall(bats[j], balls[i])
        }
        balls[i].update()
        balls[i].draw(ctx)
    }
    bats.forEach(bat => {
        //bat.update();
        bat.draw(ctx);
    })

    // check for bal bat contact and change ball position and trajectory if needed

    // draw ball and bat

    requestAnimationFrame(update);

}
requestAnimationFrame(update);
<body>
    <canvas></canvas>
</body>

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题