Peta dengan 20 juta ubin membuat game kehabisan memori, bagaimana cara menghindarinya?

11

Saat memuat peta ekstra besar, metode pemuatan membuang keluar dari memori di mana contoh baru ubin peta dibuat. Saya ingin seluruh peta diproses setidaknya pada aplikasi server (dan pada klien jika memungkinkan). Bagaimana saya mengatasi masalah ini?

UPD: pertanyaannya di sini adalah bagaimana membuat game berhenti mogok ketika masih ada memori bebas untuk digunakan. Adapun memecah peta dalam potongan, itu pendekatan yang baik, tetapi tidak apa yang saya inginkan dalam kasus saya.

UPD2: Pada awalnya saya pergi dan menetapkan tekstur untuk setiap instance baru dari kelas ubin dan itulah yang mengambil begitu banyak memori (juga memuat waktu). Sekarang dibutuhkan sekitar empat kali lebih sedikit ruang. Terima kasih untuk semua orang, sekarang saya dapat menjalankan peta besar tanpa berpikir untuk memecahnya menjadi potongan-potongan dulu.

UPD3: Setelah menulis ulang kode untuk melihat apakah array properti ubin bekerja lebih cepat atau mengkonsumsi lebih sedikit memori (dari properti yang disebutkan di ubin masing-masing sebagai properti objek), saya menemukan bahwa tidak hanya saya butuh banyak waktu untuk mencobanya, itu tidak membawa peningkatan kinerja, dan membuat permainan sangat sulit untuk di-debug.

pengguna1306322
sumber
8
Hanya memuat area di sekitar pemain.
MichaelHouse
4
20 juta ubin pada 4 byte per ubin hanya sekitar 80MB - jadi sepertinya ubin Anda tidak begitu kecil. Kami tidak dapat secara ajaib memberi Anda lebih banyak memori sehingga kami harus mencari cara untuk membuat data Anda lebih kecil. Jadi, tunjukkan pada kami apa yang ada di ubin ini.
Kylotan
Apa yang dilakukan metode pemuatan? Kode pos, apakah Anda menggunakan pipa Konten? Apakah ini memuat tekstur ke memori atau hanya metadata? Metadata apa? Jenis konten XNA umumnya merujuk hal-hal DirectX yang tidak dikelola sehingga objek yang dikelola sangat kecil, tetapi di sisi yang tidak dikelola mungkin ada banyak hal yang tidak Anda lihat kecuali Anda menjalankan profiler DirectX. stackoverflow.com/questions/3050188/…
Oskar Duveborn
2
Jika sistem mengeluarkan pengecualian memori, maka tidak ada cukup memori bebas untuk digunakan, terlepas dari apa yang mungkin Anda pikirkan. Tidak akan ada baris kode rahasia yang dapat kami berikan kepada Anda untuk mengaktifkan memori ekstra. Konten setiap ubin dan cara Anda mengalokasikannya penting.
Kylotan
3
Apa yang membuat Anda berpikir Anda memiliki memori bebas? Apakah Anda menjalankan proses 32-bit di dalam OS 64-bit?
Dalin Seivewright

Jawaban:

58

aplikasi mogok ketika mencapai 1.5Gb.

Ini sangat menyarankan bahwa Anda tidak mewakili ubin Anda dengan benar, karena ini berarti bahwa setiap ubin berukuran ~ 80 byte.

Yang perlu Anda pahami adalah bahwa perlu ada pemisahan antara konsep gameplay ubin, dan ubin visual yang dilihat pengguna. Dua konsep ini bukan hal yang sama .

Ambil Terraria sebagai contoh. Dunia Terraria terkecil membutuhkan 4200x1200 ubin, yaitu 5 juta ubin. Sekarang, berapa banyak ingatan yang diperlukan untuk mewakili dunia itu?

Nah, setiap ubin memiliki lapisan latar depan, lapisan latar belakang (dinding latar belakang), "lapisan kawat" di mana kabel pergi, dan "lapisan furnitur" di mana item furnitur pergi. Berapa banyak memori yang diambil setiap ubin? Sekali lagi, kita hanya berbicara secara konseptual, bukan secara visual.

Ubin latar depan dapat dengan mudah disimpan dalam waktu singkat yang tidak ditandatangani. Tidak ada lebih dari 65536 jenis ubin latar depan, jadi tidak ada gunanya menggunakan lebih banyak memori dari ini. Ubin latar belakang dapat dengan mudah berada dalam byte yang tidak ditandatangani, karena terdapat kurang dari 256 jenis ubin latar belakang yang berbeda. Lapisan kawat murni biner: salah satu ubin memiliki kawat di dalamnya atau tidak. Jadi itu satu bit per ubin. Dan lapisan furnitur lagi bisa menjadi byte yang tidak ditandatangani, tergantung pada berapa banyak kemungkinan potongan furnitur yang ada.

Total ukuran memori per ubin: 2 byte + 1 byte + 1 bit + 1 byte: 4 byte + 1 bit. Dengan demikian, ukuran total untuk peta Terraria kecil adalah 20790000 byte, atau ~ 20MB. (catatan: perhitungan ini didasarkan pada Terraria 1.1. Permainan telah berkembang banyak sejak saat itu, tetapi bahkan Terraria modern dapat masuk dalam 8 byte per lokasi ubin, atau ~ 40MB. Masih cukup dapat ditoleransi).

Anda seharusnya tidak pernah memiliki representasi ini disimpan sebagai array kelas C #. Mereka harus berupa array bilangan bulat atau yang serupa. AC # struct akan bekerja juga.

Sekarang, ketika tiba saatnya untuk menggambar bagian dari peta (perhatikan penekanannya), Terraria perlu mengubah ubin konseptual ini menjadi ubin yang sebenarnya . Setiap ubin harus benar-benar memilih gambar latar depan, gambar latar belakang, gambar furnitur opsional, dan memiliki gambar kawat. Di sinilah XNA hadir dengan berbagai sprite sheet dan semacamnya.

Yang perlu Anda lakukan adalah mengubah bagian yang terlihat dari peta konseptual Anda menjadi ubin sprite XNA yang sebenarnya. Anda seharusnya tidak mencoba mengubah semuanya sekaligus . Setiap ubin yang Anda simpan seharusnya hanya sebuah indeks yang mengatakan bahwa "Saya ubin tipe X," di mana X adalah bilangan bulat. Anda menggunakan indeks integer itu untuk mengambil sprite mana yang Anda gunakan untuk menampilkannya. Dan Anda menggunakan sprite sheet XNA untuk membuatnya lebih cepat dari sekadar menggambar paha depan individu.

Sekarang wilayah ubin yang terlihat perlu dipecah menjadi berbagai potongan, sehingga Anda tidak terus-menerus membuat lembaran sprite setiap kali kamera bergerak. Jadi Anda mungkin memiliki 64x64 bongkahan dunia sebagai sprite sheet. Potongan 64x64 mana pun di dunia terlihat dari posisi kamera pemain saat ini adalah bidak yang Anda gambar. Setiap potongan lain bahkan tidak memiliki sprite sheet; jika sepotong jatuh dari layar, Anda membuang lembar itu (catatan: Anda tidak benar-benar menghapusnya; Anda menyimpannya dan merespeknya untuk potongan baru yang mungkin akan terlihat kemudian).

Saya ingin seluruh peta diproses setidaknya pada aplikasi server (dan pada klien jika memungkinkan).

Anda Server tidak perlu tahu atau peduli tentang representasi visual dari ubin. Yang perlu diperhatikan hanyalah representasi konseptual. Pengguna menambahkan ubin di sini, sehingga mengubah indeks ubin itu.

Nicol Bolas
sumber
4
+1 Jawaban luar biasa. Perlu dipilih lebih dari saya.
MichaelHouse
22

Membagi medan menjadi daerah atau bongkahan. Kemudian hanya memuat potongan yang terlihat oleh para pemain dan membongkar yang tidak. Anda bisa menganggapnya seperti ban berjalan, di mana Anda memuat bongkahan di satu ujung dan menurunkannya di ujung lainnya saat pemain bergerak. Selalu berada di depan pemain.

Anda juga dapat menggunakan trik-trik seperti memasang. Di mana jika semua ubin dari satu jenis hanya memiliki satu contoh di memori dan diambil beberapa kali di semua lokasi di mana itu dibutuhkan.

Anda dapat menemukan lebih banyak ide seperti ini di sini:

Bagaimana mereka melakukannya: Jutaan ubin di Terraria

'Zonasi' area di peta ubin besar, serta ruang bawah tanah

MichaelHouse
sumber
Masalahnya adalah pendekatan seperti itu baik untuk aplikasi klien, tetapi tidak sebagus untuk server. Ketika beberapa ubin di dunia mati dan reaksi berantai dimulai (dan itu sering terjadi) seluruh peta harus ada dalam memori untuk itu dan itu adalah masalah ketika itu membuang memori.
user1306322
6
Apa isi setiap ubin? Berapa banyak memori yang digunakan per ubin? Bahkan pada setiap 10 byte Anda hanya memiliki 190 megabita. Server tidak boleh memuat tekstur atau informasi lain yang tidak diperlukan. Jika Anda memerlukan simulasi untuk 20 juta ubin, Anda harus menghapus sebanyak mungkin dari ubin itu untuk membentuk set simulasi yang hanya memiliki informasi yang diperlukan untuk reaksi berantai Anda.
MichaelHouse
5

Jawaban Byte56 bagus, dan saya mulai menulis ini sebagai komentar untuk itu tetapi terlalu lama dan mengandung beberapa tips yang mungkin lebih membantu dalam sebuah jawaban.

Pendekatan ini sama baiknya untuk server dan juga untuk klien. Bahkan itu mungkin lebih tepat, mengingat bahwa server akan diminta untuk peduli tentang area yang jauh lebih banyak daripada klien. Server memiliki ide yang berbeda tentang apa yang perlu dimuat dari yang akan dilakukan klien (ia peduli dengan semua wilayah yang dipedulikan semua klien yang terhubung), tetapi server ini sama-sama mampu mengelola serangkaian wilayah kerja.

Bahkan jika Anda bisa memuat peta raksasa (dan kemungkinan besar tidak bisa) dalam memori, Anda tidak mau . Sama sekali tidak ada titik dalam memiliki data yang dimuat yang tidak langsung harus Anda gunakan, itu tidak efisien dan lambat. Bahkan jika Anda bisa memasukkannya ke dalam memori, kemungkinan besar Anda tidak akan dapat memproses semuanya dalam jangka waktu yang masuk akal.

Saya curiga keberatan Anda untuk memiliki wilayah tertentu diturunkan karena pembaruan dunia Anda beralih ke semua ubin dan memprosesnya? Itu memang valid, tetapi tidak menghalangi hanya memuat bagian-bagian tertentu. Alih-alih, saat suatu kawasan dimuat, terapkan pembaruan apa pun yang tidak terjawab, untuk memperbarui. Itu jauh lebih efisien dari segi pemrosesan juga (memusatkan upaya besar pada area memori yang kecil). Jika ada efek pemrosesan yang mungkin melintasi batas wilayah (misalnya aliran lava atau aliran air yang akan terbawa ke wilayah lain), maka ikat kedua wilayah tersebut bersama-sama sehingga ketika satu dimuat, begitu juga yang lainnya. Idealnya situasi itu akan diminimalkan, jika tidak Anda akan menemukan diri Anda kembali dengan cepat di kasus 'semua wilayah harus dimuat setiap saat'.

MrCranky
sumber
3

Cara efisien yang saya gunakan dalam game XNA adalah:

  • gunakan spritesheets, bukan gambar untuk setiap ubin / objek, dan hanya memuat apa yang Anda perlukan di peta itu;
  • pisahkan peta menjadi bagian-bagian yang lebih kecil, misalkan peta potongan 500 x 200 petak jika peta Anda berukuran 4 kali dari ukuran ini, atau sesuatu yang dapat Anda hambat
  • muat potongan ini dalam memori dan cat hanya bagian peta yang terlihat (katakanlah, ubin yang terlihat ditambah 1 ubin untuk setiap arah pemain bergerak) dalam buffer layar;
  • bersihkan viewport dan salin piksel dari buffer over (ini disebut double buffer dan smooths the moviment);
  • ketika kamu bergerak, lacak bouds dari peta dan muat bongkahan berikutnya ketika sudah dekat dan tambahkan ke yang sekarang;
  • setelah pemain secara keseluruhan di peta baru ini, Anda dapat membongkar yang terakhir.

Anda juga bisa menggunakan peta besar tunggal dengan cara ini jika tidak terlalu besar dan menggunakan logika yang disajikan.

Tentu saja ukuran tekstur Anda harus seimbang dan resolusi membayar mahal untuk ini. Coba bekerja pada resolusi yang lebih rendah dan, jika perlu, buat paket res tinggi untuk memuat jika pengaturan konfigurasi diatur ke.

Anda harus mengulang hanya bagian yang relevan dari peta ini setiap kali dan hanya memberikan apa yang diperlukan. Dengan cara ini Anda akan banyak meningkatkan kinerja.

Sebuah server dapat memproses peta besar lebih cepat karena tidak harus membuat (salah satu operasi paling lambat) permainan, jadi logika untuk itu dapat berupa penggunaan potongan yang lebih besar atau bahkan seluruh peta, tetapi proses hanya logika sekitar pemain, seperti dalam 2 kali area tampilan pemain di sekitar pemain.

Saran di sini adalah saya sama sekali tidak ahli dalam dev permainan dan permainan saya dibuat untuk tugas akhir kelulusan saya (yang saya memiliki skor terbaik), jadi saya mungkin tidak begitu benar di setiap titik, tapi saya sudah meneliti web dan situs-situs seperti http://www.gamasutra.com dan situs pencipta XNA creators.xna.com (saat ini http://create.msdn.com ) untuk mengumpulkan pengetahuan dan keterampilan dan bekerja dengan baik untuk saya .

Ricardo Souza
sumber
2

Antara

  • beli lebih banyak memori
  • mewakili struktur data Anda dengan lebih kompak
  • jangan menyimpan semua data dalam memori sekaligus.
Thomas
sumber
Saya memiliki 8 Gb ram pada win7 64bit dan aplikasi macet ketika mencapai 1.5Gb. Jadi tidak ada gunanya membeli lebih banyak ram kecuali saya kehilangan sesuatu.
user1306322
1
@ user1306322: Jika Anda memiliki 20 juta ubin, dan membutuhkan sekitar ~ 1,5GB memori, itu berarti ubin Anda sekitar 80 byte per ubin . Apa sebenarnya yang Anda simpan dalam barang-barang ini?
Nicol Bolas
@NicolBolas seperti yang saya katakan, beberapa int, byte, dan tekstur2d. Juga mereka tidak mengambil 1.5GB, aplikasi crash ketika mencapai biaya memori ini.
user1306322
2
@ user1306322: Itu masih jauh lebih dari yang seharusnya. Seberapa besar texture2d? Apakah setiap ubin memiliki yang unik, atau apakah mereka berbagi tekstur? Juga, di mana Anda mengatakan ini? Anda tentu tidak memasukkannya ke dalam pertanyaan Anda, di mana informasi itu seharusnya berada. Tolong jelaskan keadaan spesifik Anda dengan lebih baik.
Nicol Bolas
@ user1306322: Terus terang, saya tidak mengerti mengapa jawaban saya diturunkan. Saya telah menyebutkan tiga solusi untuk situasi di luar ingatan - tentu saja, itu tidak berarti bahwa semuanya harus diterapkan. Tetapi saya masih berpikir mereka benar. Saya mungkin harus menulis "memperpanjang memori Anda" daripada "membeli" untuk memasukkan case mesin virtual. Apakah saya sedang downvoted karena jawaban saya singkat dan bukan verbose? Saya pikir poin mereka cukup mudah dipahami tanpa penjelasan lebih lanjut ?!
Thomas
1

pertanyaannya di sini adalah bagaimana membuat game berhenti mogok ketika masih ada memori bebas untuk digunakan. Adapun memecah peta dalam potongan, itu pendekatan yang baik, tetapi tidak apa yang saya inginkan dalam kasus saya.

Sebagai balasan untuk pembaruan Anda, Anda tidak dapat mengalokasikan array Int32.MaxValuedalam ukuran. Anda harus membaginya menjadi potongan-potongan. Anda bahkan bisa merangkumnya dalam kelas pembungkus yang memperlihatkan fasad seperti array:

Jimmy
sumber