画布碰撞

3

我是一个JavaScript新手,正在尝试找出如何制作一个球和挡板的碰撞效果,当游戏停止时会向玩家发出类似于“你输了”的提示。但我只希望红色的球能够击中挡板,蓝色的则不接触而通过。以下是我正在处理的代码。(如果您可以帮忙使两个球都碰撞,那也没关系)

var spawnRate = 100;

var spawnRateOfDescent = 2;

var lastSpawn = -10;

var objects = [];

var startTime = Date.now();

function spawnRandomObject() {


var t;

if (Math.random() < 0.50) {
    t = "red";
} else {
    t = "blue";
}


var object = {
    type: t,
    x: Math.random() * (canvas.width - 30) + 15,
    y: 0

}

objects.push(object);
}



function animate() {

var time = Date.now();

if (time > (lastSpawn + spawnRate)) {
    lastSpawn = time;
    spawnRandomObject();
}

for (var i = 0; i < objects.length; i++) {
    var object = objects[i];
    object.y += spawnRateOfDescent;
    ctx.beginPath();
    ctx.arc(object.x, object.y, 8, 0, Math.PI * 2);
    ctx.closePath();
    ctx.fillStyle = object.type;
    ctx.fill();

    }

    }



var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var paddleHeight = 10;
var paddleWidth = 60;
var paddleY = 480
var paddleX = (canvas.width-paddleWidth)/2;
var rightPressed = false;
var leftPressed = false;

document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

function keyDownHandler(e) {
if(e.keyCode == 39) {
    rightPressed = true;
}
else if(e.keyCode == 37) {
    leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.keyCode == 39) {
    rightPressed = false;
}
else if(e.keyCode == 37) {
    leftPressed = false;
}
}



function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}




function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaddle();    
animate();

if(rightPressed && paddleX < canvas.width-paddleWidth) {
    paddleX += 3;
}
else if(leftPressed && paddleX > 0) {
    paddleX -= 3;
}

}

setInterval(draw, 10);

谢谢!


1
每次绘制之前,您都需要检查盒子是否重叠(数学上来说,记住看到盒子不重叠比看到它们重叠容易)...此外,您可能希望缩进代码-这样更容易阅读和帮助。 - somethinghere
使用requestAnimationFrame而不是setInterval,可以获得更流畅的动画效果。 - pishpish
1
请不要篡改你的问题。 - Paul Roub
我无法删除它们。 - 22142870
你为什么要试图删除这个问题? - Braiam
1个回答

1
如果您有这样一个对象:
let ball = { type: 'red', x: 10, y: 10, width: 10, height: 10 };

您可能需要考虑添加一个方法来检查它是否与其他矩形重叠:

ball.overlapsBall = function( otherBall ){
    return !( 
        otherBall.x + otherBall.width < this.x 
        && otherBall.y + otherBall.height < this.y 
        && otherBall.y > this.y + this.height 
        && otherBall.x > this.x + this.height
    );
}

您可以通过检查它是否不重叠来实现此操作,这只有在一个框完全在另一个框之外时才为真(请阅读if语句并尝试形象化地理解它,它实际上非常简单)。

在您的绘制函数中,您现在可以添加一个循环来查看是否发生任何重叠:

var overlap = objects.filter(function( ball ) { return paddle.overlapsBall( ball ) });

你甚至可以放置一个 if 语句来检查它的类型!(filter 将取出整个球数组并检查重叠,删除任何不返回 true 的元素。现在你可以使用 overlaps.forEach(function( ball ){ /* ... */}); 对所有与挡板重叠的球进行操作。)
最后一件事,如果你计划对许多对象执行此操作,你可能需要考虑为每个挡板或球使用像这样的简单类:
class Object2D {

    constructor(x = 0, y = 0;, width = 1, height = 1){
        this.x = x;
        this.y = x;
        this.width = width;
        this.height = height;
    }

    overlaps( otherObject ){
        !( otherObject.x + otherObject.width < this.x && otherObject.y + otherObject.height < this.y && otherObject.y > this.y + this.height && otherObject.x > this.x + this.height );
    }

}

这使得你可以使用这个简单的表达式来创建一个新对象,该对象自动具有检查与相似对象重叠的方法:
var paddle = new Object2D(0,0,20,10);
var ball = new Object2D(5,5,10,10);
paddle.overlaps( ball ); // true!

此外,您可以确保任何Object2D包含所需的计算值。 您可以使用paddle instanceof Object2D(其为true)来检查此对象是否为正确类型。
请注意,在下面的评论中,@Janje不断指出,我们正在进行矩形重叠,这可能会为不是圆形的所有矩形部分创建一些“误报”。 这对于大多数情况已足够,但您可以通过快速搜索找到其他重叠和碰撞的数学公式。
更新:简单实现
请参见以下非常简单的示例,了解重叠如何运作:

var paddle = { x: 50,  y: 50,  width: 60,  height: 20 };
var box = { x: 5, y: 20, width: 20, height: 20 };

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');

document.body.appendChild( canvas );

canvas.width = 300;
canvas.height = 300;

function overlaps( a, b ){
  return !!( a.x + a.width > b.x &&  a.x < b.x + b.width
    && a.y + a.height > b.y &&  a.y < b.y + b.height );
}

function animate(){
  ctx.clearRect( 0, 0, canvas.width, canvas.height );
  
  ctx.fillStyle = overlaps( paddle, box ) ? "red" : "black";
  ctx.fillRect( paddle.x, paddle.y, paddle.width, paddle.height );
  ctx.fillRect( box.x, box.y, box.width, box.height );
 
  window.requestAnimationFrame( animate );
}

canvas.addEventListener('mousemove', function(event){
  paddle.x = event.clientX - paddle.width / 2;
  paddle.y = event.clientY - paddle.height / 2;
})

animate();


圆形和矩形之间的碰撞与矩形之间的碰撞是不同的。 - pishpish
大多数情况下,这已经足够好了,尤其是刚开始的时候。问题是关于更一般的碰撞技术。你可以用其他任何碰撞函数来替换它。(而且,矩形与矩形之间的碰撞对于圆形也同样适用) - somethinghere
1
是的,@janje,我知道。但实际上对于大多数情况来说,这已经“足够好了”。明显,OP在这里尝试着做一些新鲜事物,通过计算圆形-矩形碰撞可能会使事情变得过于复杂而不值得。就此而言,矩形-矩形就可以胜任了。 - somethinghere
1
@somethinghere. 你可能会有些反对,因为圆形-矩形碰撞是一个众所周知的问题。但是你是正确的,因为圆形是垂直落下的,而不是以非轴向角度落下,所以矩形-矩形碰撞测试在这里可以很好地工作。比矩形-矩形测试更简单的方法是测试球的底部是否已经到达了球拍的顶部:ball.y+ball.radius>paddle.y。如果没有,测试就结束了(没有碰撞)。如果是,继续测试球是否与球拍水平对齐。干杯! - markE
1
@markE 这是我更加欣赏的评论类型!扩展和改进想法会更有帮助。感谢您的补充! - somethinghere
显示剩余19条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接