Обработка столкновений мячей и кирпичей - PullRequest
0 голосов
/ 13 октября 2009

Я сделал игру «Прорыв». Небольшой веселый сайд-проект.

Теперь я обычно не играю в игры, поэтому я не думаю о том, чтобы обрабатывать столкновения.

У меня есть весло, мяч и несколько кирпичей.

Пока что, когда происходит столкновение (я рисую прямоугольники вокруг каждого из упомянутых объектов), я просто изменяю значение Y шара на -Y.

Это прекрасно работает, ЗА ИСКЛЮЧЕНИЕМ, если мяч попадает в кирпич сбоку (на восток или запад). Побочный эффект не очень приятный и портит игровой процесс.

Я думаю, что могу смело предположить, что вместо вышеупомянутой техники мне нужно изменить значение X на -X, когда это произойдет.

Пока у меня есть: if (ballRect.IntersectsWith(brickRect))

ballRect и brickRect - прямоугольники вокруг каждого объекта.

Теперь, что если я создам прямоугольник вокруг восточной границы кирпича, западной границы и т. Д.? Я думаю, ширина будет около пикселя.

Если столкновение происходит с западным или восточным прямоугольником, то значение шаров X должно быть -X. И наоборот.

А как же углы? Должен ли я просто случайно выбрать, какой прямоугольник контролировать угол х?

Или, может быть, я должен сделать прямоугольник вокруг каждого угла? прямоугольник 1 * 1 в сторону. Если есть столкновение => -x И -у значений шара?

Пожалуйста, поделитесь своими мыслями.

Вот процесс на данный момент:

    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
                }
            }
        }
    }

1 Ответ

2 голосов
/ 13 октября 2009

Вместо того, чтобы искать прямоугольные пересечения, я бы пересек реальные края. В углу ваш мяч касается двух ребер одновременно, поэтому на вектор его движения должны влиять оба.

Я бы оставил один прямоугольник для обнаружения столкновений, так как это уменьшает количество прямоугольников, которые необходимо проверить во внешнем цикле, но затем, когда столкновение с кирпичом обнаружено, перейдите во внутренний цикл, чтобы определить, какое ребро это был удар. Если вы просто протестируете каждое ребро и соответственно отрегулируете вектор для каждого из них, угол придет бесплатно (если вы не вырвитесь из цикла, когда найдете первое пересекающееся ребро).

Редактировать : В ответ на ваш обновленный вопрос:

На самом деле, вот как я бы это сделал (учитывая ваш код, это похоже на 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
            }
        }
    }
}

По сути, суть в том, что, поскольку вы уже знаете, что мяч фактически пересекает кирпич, вы можете упростить простой бокс-тест, который намного быстрее. Кроме того, нет необходимости создавать дополнительные прямоугольники для краев - просто используйте края уже имеющегося у вас прямоугольника.

...