Приклад платформи Microsoft XNA, чи точно реалізовано виявлення змови?


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

3
Чому мінус 1, проміле? Питання для мене справедливе. Але ось коротка відповідь: демонстратор платформи, який постачається разом із XNA, є лише прикладом. Не слід суворо дотримуватися як модель для ваших ігор. Це показати вам, що гру МОЖНО зробити. Ви не повинні турбуватися, якщо його реалізація зовсім не найкраща.
Густаво Масіель

Дякую, я просто припустив на прикладі, що те, що вони зробили, це найкращий спосіб зробити і що я щось пропускаю. Дякуємо, що очистили це за мене.
PriestVallon

Відповіді:


12

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

Зокрема, у мене виникала така проблема, коли я намагався ковзати по стіні, рухаючись по діагоналі проти неї. Через припущення, яке робиться зразком для вирішення зіткнень на основі найменшої осі переміщення, це призвело до того, що персонаж не міг рухатись, коли натискався на стіну в якомусь напрямку. Наприклад, використовуючи один знак, я застряг би, коли обіймав стелю і намагався рухатись проти неї зліва направо (не пам'ятаю конкретики). Перемикання знаку вирішило б цю ситуацію, але проблема з’явилася б у протилежному сценарії. Підсумок полягає в тому, що при наданій реалізації я не міг змусити його працювати правильно з усіх боків і з усіх напрямків - він завжди вийшов би з ладу принаймні в одному випадку.

Отже, ядро ​​змін, які я вніс, полягала в тому, щоб почати керувати рухом по осі X незалежно від руху по осі Y, на двох окремих кроках. Я писав про це раніше в цій відповіді, тому зверніться до деталей.

І якщо я правильно пам’ятаю, справжня причина для цього була приблизно такою:

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


1
Девід завжди кланяється на XNA!
Густаво Масіель

1
@ Gustavo-Gtoknu Я начебто відчув, що все ще потрібно вирішити проблему: P
David Gouveia

1
Щойно натрапив на цю відповідь - чудова робота! Дякую, Девід.
Остін Брункхорст

1

Якщо у вас є декілька зіткнень, якщо ви виправите зіткнення від найближчого до найдальшого від центру кожного прямокутника, що виникає, у вас не виникне питання "підвішування".

1) Знайдіть усі стикаються прямокутники

2) Якщо їх більше (залежно від випадку використання, це може бути частим або рідким), знайдіть найближчих.

3) Вирішіть зіткнення по черзі та перевірте, чи інші досі дійсні зіткнення

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.