我正在开发一个游戏,玩家是一个圆形,方块是瓷砖。用户用键盘移动角色(圆形),不应与方块(正方形)碰撞。
此外,如果圆形碰到方块的角落,我希望圆形能够沿着方块滑动,这样如果玩家继续按下相同方向的键移动,他们将沿着方块滑动而不是被卡住。
我在这里开发了一个完整的问题重现。
如果您运行代码段,您会发现圆形正确地围绕正方形移动,但之后它向下和向右移动。我不确定为什么会发生这种情况。它应该在此后完全向右移动而不偏离。
不幸的是,我不太擅长数学,所以我很难弄清楚为什么会出现这种情况。我了解到主要算法通过这个答案,但也参考了以下答案: 一 二 三 我还注意到另一个问题,如果您将
此外,如果圆形与瓷砖正面相撞,则理想情况下不应该有任何滑动。在这种情况下,它应该被卡在方块上。
此外,如果圆形碰到方块的角落,我希望圆形能够沿着方块滑动,这样如果玩家继续按下相同方向的键移动,他们将沿着方块滑动而不是被卡住。
我在这里开发了一个完整的问题重现。
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
class Vec2 {
constructor(x, y) {
this.x = x || 0;
this.y = y || 0;
}
distance(v) {
let x = v.x - this.x;
let y = v.y - this.y;
return Math.sqrt(x * x + y * y);
}
magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
dot(v) {
return this.x * v.x + this.y * v.y;
}
normalize() {
let magnitude = this.magnitude();
return new Vec2(this.x / magnitude, this.y / magnitude);
}
multiply(val) {
return typeof val === "number" ? new Vec2(this.x * val, this.y * val) : new Vec2(this.x * val.x, this.y * val.y);
}
subtract(val) {
return typeof val === "number" ? new Vec2(this.x - val, this.y - val) : new Vec2(this.x - val.x, this.y - val.y);
}
add(val) {
return typeof val === "number" ? new Vec2(this.x + val, this.y + val) : new Vec2(this.x + val.x, this.y + val.y);
}
}
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function drawCircle(xCenter, yCenter, radius) {
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI);
ctx.fill();
}
function drawSquare(x, y, w, h) {
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.stroke();
}
function circleRectangleCollision(cX, cY, cR, rX, rY, rW, rH) {
let x = clamp(cX, rX, rX + rW);
let y = clamp(cY, rY, rY + rH);
let cPos = new Vec2(cX, cY);
return cPos.distance(new Vec2(x, y)) < cR;
}
function getCircleRectangleDisplacement(rX, rY, rW, rH, cX, cY, cR, cVel) {
let circle = new Vec2(cX, cY);
let nearestX = Math.max(rX, Math.min(cX, rX + rW));
let nearestY = Math.max(rY, Math.min(cY, rY + rH));
let dist = new Vec2(cX - nearestX, cY - nearestY);
let tangentVel = dist.normalize().dot(cVel);
// The original answer had `cVel.subtract(tangentVel * 2);` here
// but that was giving me issues as well
return cVel.add(tangentVel);
}
let circlePos = new Vec2(150, 80);
let squarePos = new Vec2(240, 110);
let circleR = 50;
let squareW = 100;
let squareH = 100;
let circleVel = new Vec2(5, 0);
draw = () => {
ctx.fillStyle = "#b2c7ef";
ctx.fillRect(0, 0, 800, 800);
ctx.fillStyle = "#ffffff";
drawCircle(circlePos.x, circlePos.y, circleR);
drawSquare(squarePos.x, squarePos.y, squareW, squareH);
}
update = () => {
draw();
if (circleRectangleCollision(circlePos.x, circlePos.y, circleR, squarePos.x, squarePos.y, squareW, squareH)) {
circleVel = getCircleRectangleDisplacement(squarePos.x, squarePos.y, squareW, squareH, circlePos.x, circlePos.y, circleR, circleVel);
}
circlePos = circlePos.add(circleVel);
}
setInterval(update, 30);
canvas { display: flex; margin: 0 auto; }
<canvas width="800" height="800"></canvas>
不幸的是,我不太擅长数学,所以我很难弄清楚为什么会出现这种情况。我了解到主要算法通过这个答案,但也参考了以下答案: 一 二 三 我还注意到另一个问题,如果您将
circlePos
的y位置从80
更改为240
,则它仍然沿着正方形的顶部滑动,而不是沿着正方形的底部滑动。如果可能的话,我想修复这个问题。此外,如果圆形与瓷砖正面相撞,则理想情况下不应该有任何滑动。在这种情况下,它应该被卡在方块上。