Пример Microsoft XNA Platformer, правильно ли реализовано обнаружение столкновений?

11

Пример, предоставленный Microsoft, выглядит так, как будто обнаружение столкновения (из того, что я вижу) будет иметь небольшую ошибку. Когда пользователь сталкивается с непроходимой плиткой, вычисляется глубина пересечения. Меньшее из значений глубины X и Y используется, чтобы зафиксировать положение пользователя, чтобы оно больше не сталкивалось с плиткой. Но если бы пользователь путешествовал по диагонали, может ли это привести к тому, что пользователь не окажется точно в той точке, где персонаж впервые столкнется с плиткой?

Я, наверное, ошибаюсь, но это именно так, как я это вижу.

   private void HandleCollisions()
        {
            // Get the player's bounding rectangle and find neighboring tiles.
            Rectangle bounds = BoundingRectangle;
            int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
            int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
            int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
            int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

            // Reset flag to search for ground collision.
            isOnGround = false;

            // For each potentially colliding tile,
            for (int y = topTile; y <= bottomTile; ++y)
            {
                for (int x = leftTile; x <= rightTile; ++x)
                {
                    // If this tile is collidable,
                    TileCollision collision = Level.GetCollision(x, y);
                    if (collision != TileCollision.Passable)
                    {
                        // Determine collision depth (with direction) and magnitude.
                        Rectangle tileBounds = Level.GetBounds(x, y);
                        Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                        if (depth != Vector2.Zero)
                        {
                            float absDepthX = Math.Abs(depth.X);
                            float absDepthY = Math.Abs(depth.Y);

                            // Resolve the collision along the shallow axis.
                            if (absDepthY < absDepthX || collision == TileCollision.Platform)
                            {
                                // If we crossed the top of a tile, we are on the ground.
                                if (previousBottom <= tileBounds.Top)
                                    isOnGround = true;

                                // Ignore platforms, unless we are on the ground.
                                if (collision == TileCollision.Impassable || IsOnGround)
                                {
                                    // Resolve the collision along the Y axis.
                                    Position = new Vector2(Position.X, Position.Y + depth.Y);

                                    // Perform further collisions with the new bounds.
                                    bounds = BoundingRectangle;
                                }
                            }
                            else if (collision == TileCollision.Impassable) // Ignore platforms.
                            {
                                // Resolve the collision along the X axis.
                                Position = new Vector2(Position.X + depth.X, Position.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                    }
                }
            }

            // Save the new bounds bottom.
            previousBottom = bounds.Bottom;
        }
PriestVallon
источник
3
Почему минус 1, госзакупки? Вопрос в том , действительно для меня. Но вот краткий ответ: платформер демо , который поставляется с XNA это всего лишь пример, во всяком случае. Не следует строго соблюдать в качестве модели для ваших игр. Это , чтобы показать вам , что игра может быть сделано. Вы не должны беспокоиться , если это реализация не лучший вообще.
Густаво Масиэл
Спасибо, я только что предположил на примере, что то, что они сделали, было лучшим способом, и что я что-то упустил. Спасибо за разъяснение для меня.
PriestVallon

Ответы:

12

Вы абсолютно правы . У меня была доля проблем с процедурами коллизий на примере платформера XNA. Но мне удалось начать с кода, представленного в образце, и немного его модифицировать, пока я не достиг согласованных результатов в каждом тестовом сценарии, который я мог бы использовать.

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

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

И если я правильно помню, действительная причина этого была примерно такая:

введите описание изображения здесь

Дэвид Гувея
источник
1
Дэвид всегда крутится на XNA!
Густаво Масиэль
1
@ Gustavo-Gtoknu Я вроде чувствовал, что все еще нужно нарисовать проблему: P
Дэвид Гувейя
1
Просто наткнулся на этот ответ - отличная работа! Спасибо Дэвид.
Остин Брунхорст
1

Если у вас есть несколько столкновений, если вы исправляете свои столкновения от ближайшего к дальнему от центра каждого задействованного прямоугольника, у вас не будет проблемы «зависания».

1) Найти все сталкивающиеся прямоугольники

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

3) Разрешайте коллизии по одному и проверяйте, все ли остальные по-прежнему допустимы.

В принятом ответе логика столкновения и ввода нечеткая; он имеет проверки для определения заголовка и т. д. Реализация описанного способа позволяет отделить логику коллизий от логики ввода за счет вычисления расстояния, когда это необходимо.

accordionfolder
источник