Contoh Microsoft XNA Platformer, apakah Collsion Detection diimplementasikan secara akurat?

11

Contoh yang diberikan oleh Microsoft tampaknya seolah-olah deteksi tabrakan (dari apa yang bisa saya lihat) akan memiliki kesalahan kecil. Ketika pengguna bertabrakan dengan ubin yang tidak bisa dilewati, kedalaman persimpangan dihitung. Semakin kecil nilai kedalaman X dan Y digunakan untuk memperbaiki posisi pengguna sehingga tidak lagi bertabrakan dengan ubin. Tetapi jika pengguna bepergian secara diagonal apakah ini dapat menyebabkan pengguna tidak berakhir tepat pada titik di mana karakter pertama kali akan bertabrakan dengan ubin?

Saya mungkin salah, tapi itulah cara saya melihatnya.

   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
sumber
3
Mengapa minus 1, ppl? Pertanyaan itu valid untuk saya. Tapi inilah jawaban singkatnya: Demo platformer yang datang dengan XNA hanyalah sebuah contoh. Tidak diikuti secara ketat sebagai model untuk gim Anda. Ini untuk menunjukkan kepada Anda bahwa permainan BISA dilakukan. Anda tidak perlu repot jika implementasi bukan yang terbaik sama sekali.
Gustavo Maciel
Terima kasih, saya hanya berasumsi dengan contoh bahwa apa yang telah mereka lakukan adalah cara terbaik untuk dilakukan dan bahwa saya kehilangan sesuatu. Terima kasih telah menjelaskannya untuk saya.
PriestVallon

Jawaban:

12

Anda benar sekali . Saya mengalami masalah dengan rutinitas tabrakan pada sampel platformer XNA. Tapi saya sudah berhasil mulai dari kode seperti yang disediakan dalam sampel, dan memodifikasinya sedikit sampai saya mencapai hasil yang konsisten dalam setiap skenario pengujian yang bisa saya lakukan.

Secara khusus, jenis masalah yang saya alami adalah ketika mencoba untuk meluncur di sepanjang dinding dengan bergerak secara diagonal ke arahnya. Karena asumsi sampel dibuat untuk menyelesaikan tabrakan berdasarkan sumbu perpindahan terkecil, ini mengakibatkan karakter tidak bisa bergerak ketika mendorong dinding ke beberapa arah. Sebagai contoh, menggunakan satu tanda, saya akan terjebak ketika memeluk langit-langit dan mencoba bergerak melawannya dari kiri ke kanan (tidak dapat mengingat spesifiknya). Beralih tanda akan menyelesaikan situasi itu tetapi masalah akan muncul dalam skenario yang berlawanan. Intinya adalah bahwa dengan implementasi yang disediakan saya tidak bisa membuatnya bekerja dengan benar di semua sisi dan dari segala arah - selalu gagal pada setidaknya satu kasus.

Jadi inti dari perubahan yang saya lakukan adalah mulai menangani gerakan pada sumbu X secara independen dari gerakan pada sumbu Y, pada dua langkah terpisah. Saya telah menulis tentang hal ini sebelumnya dalam jawaban ini jadi pergilah ke sana untuk perinciannya.

Dan jika saya ingat dengan benar, alasan sebenarnya adalah sekitar seperti ini:

masukkan deskripsi gambar di sini

David Gouveia
sumber
1
David selalu menggunakan XNA!
Gustavo Maciel
1
@ Gustavo-Gtoknu Saya agak merasa masih membutuhkan gambar dari masalah: P
David Gouveia
1
Baru saja menemukan jawaban ini - kerja bagus! Terima kasih David.
Austin Brunkhorst
1

Ketika Anda memiliki banyak tabrakan jika Anda memperbaiki tabrakan Anda dari yang paling dekat ke yang paling jauh dari pusat setiap persegi panjang yang terlibat, Anda tidak akan memiliki masalah "menggantung."

1) Temukan semua persegi panjang bertabrakan

2) Jika ada lebih dari satu (tergantung pada kasus penggunaan Anda, ini bisa sering atau jarang), cari yang terdekat.

3) Selesaikan tabrakan satu per satu dan periksa apakah tabrakan lainnya masih valid

Dalam jawaban yang diterima tabrakan dan logika input berlumpur; ia memiliki pemeriksaan untuk menentukan pos, dll. Menerapkannya dengan cara yang dijelaskan menjaga logika tabrakan terpisah dari logika input dengan biaya menghitung jarak bila diperlukan.

folder akordeon
sumber