球与砖块碰撞处理

发布于 2024-08-07 10:04:31 字数 1725 浏览 2 评论 0原文

我制作了游戏“Breakout”。一个有趣的小副项目。

现在,我通常不制作游戏,所以碰撞处理不是我通常考虑的事情。

我有一个桨、一个球和一些砖块。

现在,当发生碰撞时(我在提到的每个对象周围绘制矩形),我只需将球的 Y 值更改为 -Y 即可。

这很好用,除非球从侧面(东或西)击中砖块。副作用并不好,而且会破坏游戏玩法。

我想我可以放心地假设,当发生这种情况时,我需要将 X 值更改为 -X,而不是上述技术。

到目前为止,我有: if (ballRect.IntersectsWith(brickRect))

ballRectbrickRect 是每个对象周围的矩形。

现在,如果我围绕砖块的东边界、西边界等创建一个矩形会怎样?我猜宽度大约是一个像素。

如果与西部或东部矩形发生碰撞,则球的 X 值应为 -X。 反之亦然。

那么角落呢?我应该随机选择哪个矩形来控制 x 角吗?

或者也许我应该在每个角周围制作一个矩形?边长为 1*1 的矩形。 如果发生碰撞=>球的 -x 和 -y 值?

请分享您的想法。

到目前为止的流程如下:

    foreach (var brick in Bricks)
    {
        if (brick.IsAlive)
        {
            var brickRect = new Rectangle(brick.X, brick.Y, BrickWidth, BrickHeight);
            if (ballRect.IntersectsWith(brickRect)) //Ball has hit brick. lets find out which side of the brick
            {
                var brickRectNorth = new Rectangle(brick.X, brick.Y + BrickHeight, BrickWidth, 1);
                var brickRectSouth = new Rectangle(brick.X, brick.Y, BrickWidth, 1);

                var brickRectEast = new Rectangle(brick.X, brick.Y, 1, BrickHeight);
                var brickRectWest = new Rectangle(brick.X + BrickWidth, brick.Y, 1, BrickHeight);

                if (ballRect.IntersectsWith(brickRectNorth) || ballRect.IntersectsWith(brickRectSouth))
                {
                    //STUFF that makes ball.y = -ball.y
                }
                if (ballRect.IntersectsWith(brickRectWest) || ballRect.IntersectsWith(brickRectEast))
                {
                    //STUFF that makes ball.x = -ball.x
                }
            }
        }
    }

I have made the game, "Breakout". A small fun side-project.

Now, I usually do not make games, so collision-handling is not something I normally think about.

I have a paddle, a ball and some bricks.

For now, when there is a collision (I draw rectangles around each of the objects mentioned), I simply change the Y value of the ball to -Y.

This works fine, EXCEPT if the ball hits a brick from the side (either East or West). The side-effect is not pretty and ruins the gameplay.

I think I can safely assume that instead of the above technique, I need to change the X value to -X when this happens.

So far I have: if (ballRect.IntersectsWith(brickRect))

ballRect and brickRect being rectangles around each object.

Now, what if I created a rectangle around the eastern border of the brick, the western border, etc? I guess the width would be about a pixel.

If collision happens with western or eastern rectangle, then the balls X value should be -X.
And vice versa.

What about the corners though? Should I just randomly choose which rectangle to control of x corner?

Or perhaps should I make a rectangle around each corner? the rectangle being 1*1 in side.
If there is a collision => -x AND -y values of the ball?

Please share your thoughts.

Here is the process so far:

    foreach (var brick in Bricks)
    {
        if (brick.IsAlive)
        {
            var brickRect = new Rectangle(brick.X, brick.Y, BrickWidth, BrickHeight);
            if (ballRect.IntersectsWith(brickRect)) //Ball has hit brick. lets find out which side of the brick
            {
                var brickRectNorth = new Rectangle(brick.X, brick.Y + BrickHeight, BrickWidth, 1);
                var brickRectSouth = new Rectangle(brick.X, brick.Y, BrickWidth, 1);

                var brickRectEast = new Rectangle(brick.X, brick.Y, 1, BrickHeight);
                var brickRectWest = new Rectangle(brick.X + BrickWidth, brick.Y, 1, BrickHeight);

                if (ballRect.IntersectsWith(brickRectNorth) || ballRect.IntersectsWith(brickRectSouth))
                {
                    //STUFF that makes ball.y = -ball.y
                }
                if (ballRect.IntersectsWith(brickRectWest) || ballRect.IntersectsWith(brickRectEast))
                {
                    //STUFF that makes ball.x = -ball.x
                }
            }
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

淡墨 2024-08-14 10:04:31

我不会寻找矩形交点,而是与实际边缘相交。在拐角处,你的球同时接触两个边缘,因此它的运动矢量应该受到两个边缘的影响。

我会保留单个矩形进行碰撞检测,因为这减少了您需要在外循环中测试的矩形数量,但是一旦检测到与砖块的碰撞,就进入内循环来检测它是哪条边缘被击中。如果您只是测试每条边并相应地调整每条边的矢量,则拐角将免费出现(只要您在找到第一个相交边时不脱离循环)。

编辑:回应您更新的问题:

实际上,这就是我的做法(根据您的代码,这似乎是 C# 3.0,所以这就是我在下面的假设):

foreach(var brick in Bricks) {
    if(brick.IsAlive) {
        var brickRect = new Rectangle(brick.X, brick.Y, BrickWidth, BrickHeight);
        if(ballRect.IntersectsWith(brickRect)) {
            // Ball has hit brick.  Now let's adjust the ball's vector accordingly

            // Convenience variables.  Compiler will probably inline.
            var brickLeft = brick.X;
            var brickRight = brick.X + BrickWidth;
            var brickTop = brick.Y;
            var brickBottom = brick.Y + BrickHeight;

            var ballLeft = ball.X - ball.Radius;
            var ballRight = ball.X + ball.Radius;
            var ballTop = ball.Y - ball.Radius;
            var ballBottom = ball.Y + ball.Radius;

            // Test which vector(s) we need to flip
            bool flipX = (ballRight >= brickLeft || ballLeft <= brickRight);
            bool flipY = (ballTop >= brickBottom || ballBottom <= brickTop);

            // Flip the vectors (there are probably ways to optimize this,
            // too, but without seeing your code I can't tell).
            if(flipY) {
                // Stuff that makes ball.y = -ball.y
            }

            if(flipX) {
                // Stuff that makes ball.x = -ball.x
            }
        }
    }
}

基本上,要点是,由于您已经知道球实际上与砖块相交,因此您可以简化为简单的盒子测试,这要快得多。此外,无需为边缘创建额外的矩形 - 只需使用已有矩形的边缘即可。

Rather than looking for rectangle intersections, I'd intersect the actual edges. At the corner, your ball is touching two edges simultaneously, so its motion vector should be affected by both.

I would keep the single rectangle for collision detection, since that reduces the number of rectangles you need to test in your outer loop, but then once a collision with a brick has been detected, go into an inner loop to detect which edge it was that was hit. If you just test each edge and adjust the vector accordingly for each one, the corner will come for free (as long as you don't break out of the loop when you find the first intersecting edge).

Edit: In response to your updated question:

Actually, this is how I would do it (given your code, this appears to be C# 3.0, so that's what I've assumed below):

foreach(var brick in Bricks) {
    if(brick.IsAlive) {
        var brickRect = new Rectangle(brick.X, brick.Y, BrickWidth, BrickHeight);
        if(ballRect.IntersectsWith(brickRect)) {
            // Ball has hit brick.  Now let's adjust the ball's vector accordingly

            // Convenience variables.  Compiler will probably inline.
            var brickLeft = brick.X;
            var brickRight = brick.X + BrickWidth;
            var brickTop = brick.Y;
            var brickBottom = brick.Y + BrickHeight;

            var ballLeft = ball.X - ball.Radius;
            var ballRight = ball.X + ball.Radius;
            var ballTop = ball.Y - ball.Radius;
            var ballBottom = ball.Y + ball.Radius;

            // Test which vector(s) we need to flip
            bool flipX = (ballRight >= brickLeft || ballLeft <= brickRight);
            bool flipY = (ballTop >= brickBottom || ballBottom <= brickTop);

            // Flip the vectors (there are probably ways to optimize this,
            // too, but without seeing your code I can't tell).
            if(flipY) {
                // Stuff that makes ball.y = -ball.y
            }

            if(flipX) {
                // Stuff that makes ball.x = -ball.x
            }
        }
    }
}

Basically, the point is that since you already know the ball actually intersects the brick, you can simplify to a simple box test, which is much faster. Also, there's no need to create extra rectangles for the edges -- just use the edges of the rectangle you already have.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文