Memusnahkan objek di luar layar secara efisien pada peta 2D top-down

8

Saya tahu efisiensi adalah kunci dalam pemrograman game dan saya memiliki beberapa pengalaman dengan merender "peta" sebelumnya, tetapi mungkin tidak dengan cara terbaik.

Untuk gim TopDown 2D: (cukup render tekstur / ubin dunia, bukan yang lain)

Katakanlah, Anda memiliki peta 1000x1000 (ubin atau apa pun). Jika ubin tidak ada dalam tampilan kamera, seharusnya tidak boleh ditampilkan - sesederhana itu. Tidak perlu membuat ubin yang tidak akan terlihat. Tetapi karena Anda memiliki 1000x1000 objek di peta Anda, atau mungkin lebih sedikit, Anda mungkin tidak ingin mengulang semua 1000 * 1000 ubin hanya untuk melihat apakah mereka seharusnya dirender atau tidak.

Pertanyaan: Apa cara terbaik untuk menerapkan efisiensi ini? Sehingga "cepat / cepat" dapat menentukan ubin apa yang seharusnya diberikan?

Juga, saya tidak membangun permainan saya di sekitar ubin yang diberikan dengan SpriteBatch sehingga tidak ada persegi panjang, bentuknya bisa berbeda ukuran dan memiliki banyak titik, katakanlah benda melengkung 10 poin dan tekstur di dalam bentuk itu;

Pertanyaan: Bagaimana Anda menentukan apakah objek semacam ini "di dalam" Tampilan kamera?

Sangat mudah dengan persegi panjang 48x48, lihat saja apakah X + Lebar atau Y + Tinggi dalam tampilan kamera. Berbeda dengan banyak poin.

Sederhananya, bagaimana mengelola kode dan data secara efisien agar tidak harus dijalankan melalui / loop melalui jutaan objek pada saat yang sama.

Deukalion
sumber

Jawaban:

6

Adapun objek kompleks saya pikir cara terbaik adalah hanya dengan mengurangi mereka menjadi persegi panjang di sekitarnya, dan memeriksa apakah persegi panjang itu di dalam viewport. Bahkan jika Anda akan membuat tekstur yang tidak benar-benar terlihat (karena bentuknya) masih mungkin lebih cepat daripada melakukan algoritma deteksi yang lebih kompleks.

Sedangkan untuk menangani peta besar secara efisien, Anda harus membagi peta Anda pada skala yang lebih besar, katakanlah 10x10. Kemudian Anda memeriksa persimpangan viewport Anda. Dalam kasus terburuk ia mengenai 4 'wilayah' ini yang akan menghasilkan (100x100) * 4 = 40K objek. Ini adalah contoh yang disederhanakan. Untuk penggunaan nyata Anda harus mempertimbangkan struktur Quadtree yang sangat efisien untuk subdivisi dan deteksi tabrakan tersebut (pemeriksaan visibilitas viewport pada dasarnya adalah pemeriksaan tabrakan antara viewport dan sprite).

Petr Abdulin
sumber
Menggunakan Quadtree untuk petak peta sedikit berlebihan ... karena Anda hanya bisa menghitung indeks petak yang tepat yang perlu dirender. Dan selain itu, daerah lebih sederhana, dan saya akan merekomendasikan menggunakannya untuk putaran pertama optimasi. Ini akan membantu memahami dan menggunakan quadtrees nanti :) +1.
Liosan
@Liosan tidak jelas dari pertanyaan apakah 'ubin' ini memiliki ukuran yang sama, jika tidak solusi tentu akan sangat sepele.
Petr Abdulin
Anda benar, Deukalion bahkan menulis komentar untuk jawaban yang berbeda dengan mengatakan 'Dan ubin tidak selalu berukuran sama persis'.
Liosan
Apakah QuadTree bahkan berfungsi dengan ukuran wilayah yang tepat? Setiap wilayah tidak boleh berukuran persegi panjang karena objek di dalam persegi panjang tidak jadi itu tidak membuat persegi panjang. Jadi BUKAN sebuah grid pixel 1024x1024 yang dibuat, bentuknya mungkin sangat ortodoks.
Deukalion
Yah, saya kira tidak (saya sendiri belum pernah menggunakan quadtrees), tapi itu tidak masalah jika Anda bisa memasukkan semuanya ke dalam wilayah persegi panjang. Bagaimanapun, jika quadtree tidak sesuai untuk tugas Anda karena alasan tertentu, Anda, kemungkinan besar, perlu menggunakan pendekatan serupa.
Petr Abdulin
3

Ketika Anda memiliki banyak objek seluler, Anda harus menyimpannya dengan koordinatnya dalam struktur pohon multi-dimensi. Dengan begitu Anda dapat secara efisien mendapatkan daftar semua objek yang ada di dalam kotak yang diberikan. Anda bahkan dapat memerintahkannya dengan koordinat x atau y, yang penting untuk menggambar urutan ketika sprite objek tumpang tindih.

Ini juga akan sangat berguna untuk deteksi tabrakan.

Lihat artikel wikipedia tentang pohon kd untuk detailnya.

Ketika pohon 2d terlalu rumit untuk Anda, ada juga alternatif yang lebih mudah tetapi tidak kalah efektif: Simpan benda-benda itu sebagai anak-anak ubin. Saat Anda memindahkan objek, Anda menghapusnya dari daftar objek ubin lama, dan memasukkannya ke daftar objek yang baru. Saat Anda menggambar objek, Anda kembali mengulangi ubin di viewport dan mengambil objek mereka. Kemudian Anda mengurutkan semuanya berdasarkan koordinat y dan menggambarnya.

Philipp
sumber
1

Tidak tahu apakah ini cara terbaik, tapi beginilah cara saya belajar melakukannya:

Anda memiliki array dua dimensi "ubin"

public Tile tilemap[][];

dan Anda memutuskan posisi "kamera" dengan Vector2, Anda hanya akan membuat apa yang ada di dalam adegan, persegi panjang besar adalah apa yang dapat Anda lihat di layar, tidak berguna untuk menggambar sisa adegan.

Sekarang Anda perlu mendapatkan offset, dengan asumsi Anda ingin kamera Anda berada di tengah-tengah adegan:

offsetX = (graphics().width() / 2 - Math.round(cam.Position().X));
offsetX = Math.min(offsetX, 0);
offsetX = Math.max(offsetX, graphics().width() / 2 - mapWidth);
offsetY = (graphics().height()) / 2 - Math.round(cam.getPosition().Y);
offsetY = Math.min(offsetY, 0);
offsetY = Math.max((graphics().height() / 2 - mapHeight), offsetY);

sekarang, di bagian mana dari array ubin terlihat mulai dan selesai?

firstTileX = pixelsToTiles(-offsetX);

lastTileX = firstTileX + pixelsToTiles(graphics().width());

firstTileY = pixelsToTiles(-offsetY);

lastTileY = firstTileY + pixelsToTiles(graphics().height());

int pixelsToTiles(int pixels) {
    return (int) Math.floor((float) pixels / Tile.getHeight());
}

dan dalam metode menggambar Anda, Anda hanya mengulang-ulang bagian array yang terlihat:

   for (int x = firstTileX; x < lastTileX; x++) {
        for (int y = firstTileY; y < lastTileY; y++) {
              Vector2 position = new Vector2(tilesToPixelsX(x) + offsetX,
                        tilesToPixelsY(y) + offsetY);
              tilemap[x][y].Draw(surf, position);
        }
    }
riktothepast
sumber
1
Ya, tetapi ubin adalah contoh untuk menyederhanakan banyak hal. Saya sudah menulis bahwa saya memahami proses menentukan apakah suatu objek sudah dalam "tampilan" dengan bentuk persegi panjang / judul, bukan dengan bentuk yang lebih maju yang memiliki banyak titik. Juga, saya sedang mencari sesuatu yang membuat saya "tidak" melewati semua ubin selama setiap metode Pembaruan () di XNA. Jika saya memiliki peta "BESAR", dengan sekitar 10.000 objek (bentuk 3 poin dan lebih banyak) ini bukan cara untuk melakukannya. Setiap Pembaruan Saya harus menjalankan satu putaran pembaruan 10.000 dan hanya sebagai banyak perhitungan. Saya tidak menggunakan ubin dan ini tidak efisien; Aku pernah disana.
Deukalion
Dan ubin tidak selalu berukuran sama persis, jadi saya juga tidak bisa melakukannya. Saya tidak menggunakan persegi panjang.
Deukalion
Sederhananya: Saya hanya ingin loop melalui objek yang HARUS dirender, bukan loop melalui objek yang seharusnya tidak sama sekali.
Deukalion
@Deukalion Riktothepast's kode TIDAK hanya loop melalui ubin yang akan muncul di dalam kotak pembatas layar (meskipun ini tidak terlalu jelas). Teknik dasar yang sama dapat digunakan untuk loop melalui persegi panjang ubin dalam set koordinat yang diberikan.
DampeS8N
0

Anda dapat memiliki satu Bitmap yang merupakan keseluruhan adegan tetapi tidak ditampilkan. Dan kemudian ditampilkan layar kamera Bitmap berukuran lapisan yang hanya menarik dari seluruh adegan tetapi hanya bagian yang perlu ditampilkan.

mrall
sumber