Bagaimana saya bisa mengoptimalkan dunia voxel Minecraft-esque?


76

Saya menemukan dunia besar Minecraft yang luar biasa sangat lambat untuk dinavigasi, bahkan dengan quad core dan kartu grafis gemuk.

Saya menganggap kelambatan Minecraft berasal dari:

  • Java, karena partisi spasial dan manajemen memori lebih cepat dalam bahasa asli C ++.
  • Partisi dunia yang lemah.

Saya bisa salah pada kedua asumsi tersebut. Namun, ini membuat saya berpikir tentang cara terbaik untuk mengelola dunia voxel besar. Karena merupakan dunia 3D, di mana blok bisa eksis dalam setiap bagian dari dunia, itu pada dasarnya adalah array besar 3D [x][y][z], di mana setiap blok di dunia memiliki tipe (yaitu BlockType.Empty = 0, BlockType.Dirt = 1, dll)

Saya berasumsi bahwa untuk membuat dunia semacam ini berkinerja baik, Anda perlu:

  • Gunakan pohon dari beberapa varietas ( okt / kd / bsp ) untuk membagi semua kubus; sepertinya oct / kd akan menjadi pilihan yang lebih baik, karena Anda hanya dapat mempartisi pada tingkat per kubus bukan tingkat per segitiga.
  • Gunakan beberapa algoritme untuk mengetahui blok mana yang saat ini dapat dilihat, karena blok yang lebih dekat dengan pengguna dapat mengaburkan blok di belakang, sehingga tidak ada gunanya untuk merendernya.
  • Jaga agar objek blok tetap ringan, sehingga cepat untuk menambah dan menghapusnya dari pohon.

Saya kira tidak ada jawaban yang tepat untuk ini, tetapi saya akan tertarik untuk melihat pendapat orang tentang masalah ini. Bagaimana Anda meningkatkan kinerja di dunia berbasis voxel yang besar?



2
Jadi apa yang sebenarnya Anda tanyakan? Apakah Anda meminta pendekatan yang baik untuk mengelola dunia besar, atau umpan balik tentang pendekatan khusus Anda, atau pendapat tentang masalah mengelola dunia besar?
doppelgreener

1
Hal bagus sejauh ini, ini lebih tentang apa pendekatan umum yang paling umum untuk hal-hal semacam ini. Saya tidak secara khusus setelah umpan balik tentang pendekatan saya karena semua yang saya usulkan adalah apa yang secara logis saya harapkan akan terjadi. Saya hanya ingin informasi lebih lanjut tentang masalah ini dan tidak terlalu banyak yang muncul setelah beberapa pencarian. Saya kira pertanyaan saya bukan hanya tentang memberikan kinerja tetapi tentang bagaimana mengelola volume data yang begitu besar ... seperti
memotong

2
Jadi jelas dan tambahkan pertanyaan ke posting Anda sehingga kami tahu pertanyaan apa yang kami jawab. ;)
doppelgreener

3
Apa yang Anda maksud dengan "navigasi yang sangat lambat"? Jelas ada beberapa perlambatan saat gim ini menghasilkan medan baru, tetapi setelah itu, minecraft cenderung menangani medan dengan cukup baik.
thedaian

Jawaban:


106

Batuan Mesin Voxel

Rumput Mesin Voxel

Sehubungan dengan Java vs C ++, saya telah menulis mesin voxel di keduanya (versi C ++ ditunjukkan di atas). Saya juga sudah menulis mesin voxel sejak 2004 (ketika mereka tidak mode). :) Saya dapat mengatakan dengan sedikit keraguan bahwa kinerja C ++ jauh lebih unggul (tetapi juga lebih sulit untuk dikodekan). Ini kurang tentang kecepatan komputasi, dan lebih banyak tentang manajemen memori. Hands down, ketika Anda mengalokasikan / mendeallocating data sebanyak yang ada di dunia voxel, C (++) adalah bahasa yang harus dikalahkan. Namun, Anda harus memikirkan tujuan Anda. Jika kinerja adalah prioritas tertinggi Anda, lanjutkan dengan C ++. Jika Anda hanya ingin menulis permainan tanpa kinerja yang luar biasa, Java pasti dapat diterima (seperti yang dibuktikan oleh Minecraft). Ada banyak kasus sepele / tepi, tetapi secara umum Anda dapat mengharapkan Java berjalan sekitar 1,75-2,0 kali lebih lambat dari (ditulis dengan baik) C ++. Anda dapat melihat versi mesin saya yang kurang optimal dan dioptimalkan sedang berjalan di sini (EDIT: versi yang lebih baru di sini ). Sementara generasi chunk mungkin tampak lambat, perlu diingat itu menghasilkan diagram voronoi 3D secara volumetrik, menghitung permukaan normal, pencahayaan, AO, dan bayangan pada CPU dengan metode brute-force. Saya telah mencoba berbagai teknik dan saya bisa mendapatkan sekitar 100x generasi chunk lebih cepat menggunakan berbagai teknik caching dan instancing.

Untuk menjawab sisa pertanyaan Anda, ada banyak hal yang dapat Anda lakukan untuk meningkatkan kinerja.

  1. Caching. Di mana pun Anda bisa, Anda harus menghitung data sekali. Sebagai contoh, saya memanggang pencahayaan ke tempat kejadian. Itu bisa menggunakan pencahayaan dinamis (dalam ruang layar, sebagai pasca-proses), tetapi memanggang dalam pencahayaan berarti bahwa saya tidak harus melewati normals untuk segitiga, yang berarti ....
  2. Berikan data sesedikit mungkin ke kartu video. Satu hal yang cenderung dilupakan orang adalah semakin banyak data yang Anda berikan ke GPU, semakin banyak waktu yang diperlukan. Saya lulus dalam satu warna dan posisi simpul. Jika saya ingin melakukan siklus siang / malam, saya bisa melakukan gradasi warna, atau saya bisa menghitung ulang pemandangan saat matahari berangsur-angsur berubah.

  3. Karena mengirimkan data ke GPU sangat mahal, dimungkinkan untuk menulis mesin dalam perangkat lunak yang lebih cepat dalam beberapa hal. Keuntungan dari perangkat lunak adalah dapat melakukan semua jenis manipulasi data / akses memori yang tidak mungkin dilakukan pada GPU.

  4. Bermain dengan ukuran bets. Jika Anda menggunakan GPU, kinerja dapat bervariasi secara dramatis berdasarkan seberapa besar setiap larik simpul yang Anda lewati. Dengan demikian, bermain-main dengan ukuran potongan (jika Anda menggunakan potongan). Saya telah menemukan bahwa potongan 64x64x64 bekerja cukup baik. Apa pun yang terjadi, pertahankan potongan kubik Anda (tidak ada prisma persegi panjang). Ini akan membuat pengkodean dan berbagai operasi (seperti transformasi) lebih mudah, dan dalam beberapa kasus, lebih banyak performan. Jika Anda hanya menyimpan satu nilai untuk panjang setiap dimensi, ingatlah bahwa ada dua register yang lebih sedikit yang dipertukarkan saat menghitung.

  5. Pertimbangkan daftar tampilan (untuk OpenGL). Meskipun mereka adalah cara "lama", mereka bisa lebih cepat. Anda harus memanggang daftar tampilan menjadi variabel ... jika Anda memanggil operasi pembuatan daftar tampilan secara realtime, itu akan lambat sekali. Bagaimana daftar tampilan lebih cepat? Itu hanya memperbarui status, vs atribut per-simpul. Ini berarti saya bisa melewatkan hingga enam wajah, lalu satu warna (vs warna untuk setiap simpul voxel). Jika Anda menggunakan GL_QUADS dan voxels kubik, ini bisa menghemat hingga 20 byte (160 bit) per voxel! (15 byte tanpa alfa, meskipun biasanya Anda ingin menjaga hal-hal 4-byte selaras.)

  6. Saya menggunakan metode brute-force rendering "chunk", atau halaman data, yang merupakan teknik umum. Tidak seperti octrees, jauh lebih mudah / lebih cepat untuk membaca / memproses data, meskipun jauh lebih sedikit memori-friendly (namun, hari ini Anda bisa mendapatkan 64 gigabytes memori untuk $ 200- $ 300) ... bukan berarti bahwa rata-rata pengguna memiliki itu. Jelas, Anda tidak dapat mengalokasikan satu array besar untuk seluruh dunia (1024x1024x1024 set voxels adalah 4 gigabytes memori, dengan asumsi 32-bit int digunakan per voxel). Jadi, Anda mengalokasikan / membatalkan banyak array kecil, berdasarkan kedekatannya dengan pemirsa. Anda juga dapat mengalokasikan data, mendapatkan daftar tampilan yang diperlukan, lalu membuang data untuk menghemat memori. Saya pikir kombo yang ideal mungkin menggunakan pendekatan hibrida dari octrees dan array - menyimpan data dalam array ketika melakukan generasi prosedural dunia, pencahayaan, dll.

  7. Jadikan dekat ke jauh ... piksel yang terpotong menghemat waktu. GPU akan melempar piksel jika tidak lulus uji buffer kedalaman.

  8. Berikan hanya potongan / halaman di viewport (cukup jelas). Bahkan jika gpu tahu cara klip polgyons di luar viewport, melewati data ini masih membutuhkan waktu. Saya tidak tahu seperti apa struktur yang paling efisien untuk ini ("memalukan," Saya tidak pernah menulis pohon BSP), tetapi bahkan raycast sederhana pada basis per potong dapat meningkatkan kinerja, dan jelas menguji terhadap frustum penglihatan akan menghemat waktu.

  9. Info yang jelas, tetapi untuk pemula: singkirkan setiap poligon tunggal yang tidak ada di permukaan - yaitu jika voxel terdiri dari enam wajah, lepaskan wajah yang tidak pernah di-render (menyentuh voxel lain).

  10. Sebagai aturan umum semua yang Anda lakukan dalam pemrograman: CACHE LOCALITY! Jika Anda dapat menyimpan hal-hal cache-lokal (bahkan untuk sejumlah kecil waktu, itu akan membuat perbedaan besar. Ini berarti menjaga data Anda kongruen (di wilayah memori yang sama), dan tidak mengganti area memori untuk diproses terlalu sering. Jadi , idealnya, bekerja pada satu chunk per thread, dan jaga agar memori itu eksklusif untuk thread.Ini tidak hanya berlaku untuk cache CPU. Pikirkan hirarki cache seperti ini (paling lambat hingga tercepat): jaringan (cloud / database / dll) -> hard drive (dapatkan SSD jika Anda belum memilikinya), ram (dapatkan saluran tripple atau RAM lebih besar jika Anda belum memilikinya), CPU Cache (s), register. Coba simpan data Anda di akhir yang terakhir, dan tidak menukar lebih dari yang Anda harus.

  11. Threading. Lakukan. Dunia Voxel sangat cocok untuk threading, karena setiap bagian dapat dihitung (sebagian besar) secara independen dari yang lain ... Saya melihat peningkatan hampir-4x (pada inti 4, inti 8 i7) dalam generasi dunia prosedural ketika saya menulis rutinitas untuk threading.

  12. Jangan gunakan tipe data char / byte. Atau celana pendek. Rata-rata konsumen Anda akan memiliki prosesor AMD atau Intel modern (seperti halnya Anda, mungkin). Prosesor ini tidak memiliki register 8 bit. Mereka menghitung byte dengan menempatkannya ke dalam slot 32 bit, kemudian mengubahnya kembali (mungkin) dalam memori. Kompiler Anda dapat melakukan semua jenis voodoo, tetapi menggunakan nomor 32 atau 64 bit akan memberi Anda hasil yang paling dapat diprediksi (dan tercepat). Demikian juga, nilai "bool" tidak membutuhkan 1 bit; kompiler akan sering menggunakan 32 bit penuh untuk bool. Mungkin tergoda untuk melakukan jenis kompresi tertentu pada data Anda. Misalnya, Anda dapat menyimpan 8 voxel sebagai satu angka (2 ^ 8 = 256 kombinasi) jika semuanya jenis / warna yang sama. Namun, Anda harus berpikir tentang konsekuensi dari ini - mungkin menghemat banyak memori, tetapi itu juga dapat menghambat kinerja, bahkan dengan waktu dekompresi yang kecil, karena bahkan jumlah waktu ekstra yang kecil itu secara kubik dengan ukuran dunia Anda. Bayangkan menghitung raycast; untuk setiap langkah raycast, Anda harus menjalankan algoritma dekompresi (kecuali jika Anda menemukan cara cerdas untuk menggeneralisasi perhitungan untuk 8 voxels dalam satu langkah ray).

  13. Seperti yang dikatakan Jose Chavez, pola desain kelas terbang dapat bermanfaat. Sama seperti Anda akan menggunakan bitmap untuk mewakili ubin dalam game 2D, Anda dapat membangun dunia Anda dari beberapa jenis ubin (atau blok) 3D. Kelemahan dari ini adalah pengulangan tekstur, tetapi Anda dapat memperbaiki ini dengan menggunakan tekstur varians yang cocok bersama. Sebagai aturan praktis, Anda ingin memanfaatkan instancing di mana pun Anda bisa.

  14. Hindari pemrosesan simpul dan piksel dalam shader saat mengeluarkan geometri. Dalam mesin voxel Anda pasti akan memiliki banyak segitiga, sehingga bahkan shader piksel sederhana dapat sangat mengurangi waktu render Anda. Lebih baik me-render ke buffer, lalu membuat Anda pixel shader sebagai proses pasca. Jika Anda tidak bisa melakukan itu, coba lakukan perhitungan di vertex shader Anda. Kalkulasi lain harus dimasukkan ke dalam data titik jika memungkinkan. Akses tambahan menjadi sangat mahal jika Anda harus merender ulang semua geometri (seperti pemetaan bayangan atau pemetaan lingkungan). Terkadang lebih baik untuk melepaskan adegan dinamis demi detail yang lebih kaya. Jika gim Anda memiliki adegan yang dapat dimodifikasi (yaitu medan yang dapat dirusak), Anda selalu dapat menghitung ulang adegan saat semuanya dihancurkan. Rekompilasi tidak mahal dan harus di bawah satu detik.

  15. Lepaskan loop Anda dan jaga agar array tetap rata! Jangan lakukan ini:

    for (i = 0; i < chunkLength; i++) {
     for (j = 0; j < chunkLength; j++) {
      for (k = 0; k < chunkLength; k++) {
       MyData[i][j][k] = newVal;
      }
     }
    }
    //Instead, do this:
    for (i = 0; i < chunkLengthCubed; i++) {
     //figure out x, y, z index of chunk using modulus and div operators on i
     //myData should have chunkLengthCubed number of indices, obviously
     myData[i] = newVal;
    }

    EDIT: Melalui pengujian yang lebih luas, saya telah menemukan ini bisa salah. Gunakan kasing yang paling sesuai untuk skenario Anda. Secara umum, array harus rata, tetapi menggunakan multi-index loop seringkali bisa lebih cepat tergantung pada case

EDIT 2: saat menggunakan loop multi-indeks, yang terbaik untuk loop ke dalam urutan z, y, x daripada sebaliknya. Kompiler Anda mungkin mengoptimalkan ini, tetapi saya akan terkejut jika melakukannya. Ini memaksimalkan efisiensi dalam akses memori dan lokalitas.

for (k < 0; k < volumePitch; k++) {
    for (j = 0; j < volumePitch; j++) {
        for (i = 0; i < volumePitch; i++) {
            myIndex = k*volumePitch*volumePitch + j*volumePitch + i;
        }
    }
}
  1. Terkadang Anda harus membuat asumsi, generalisasi, dan pengorbanan. Yang terbaik yang dapat Anda lakukan adalah dengan berasumsi bahwa sebagian besar dunia Anda benar-benar statis, dan hanya berubah setiap beberapa ribu bingkai. Untuk bagian animasi di dunia, itu dapat dilakukan secara terpisah. Juga anggap sebagian besar duniamu benar-benar buram. Objek transparan dapat diberikan dalam pass yang terpisah. Asumsikan bahwa tekstur hanya bervariasi setiap x unit, atau bahwa objek hanya dapat ditempatkan dalam kenaikan x. Asumsikan ukuran dunia tetap ... sama menggodanya dengan dunia tanpa batas, ini dapat menyebabkan persyaratan sistem yang tidak dapat diprediksi. Sebagai contoh, untuk menyederhanakan pembentukan pola voronoi di bebatuan di atas, saya berasumsi bahwa setiap titik pusat voronoi terletak pada kisi yang seragam, dengan sedikit offset (dengan kata lain, hashing geometris tersirat). Asumsikan dunia yang tidak membungkus (memiliki tepi). Ini dapat menyederhanakan banyak kerumitan yang diperkenalkan oleh sistem koordinat pembungkus, dengan biaya minimal untuk pengalaman pengguna.

Anda dapat membaca lebih lanjut tentang implementasi saya di situs saya


9
+1. Sentuhan yang bagus termasuk gambar di bagian atas sebagai insentif untuk membaca esai. Sekarang saya sudah membaca esai saya bisa mengatakan mereka tidak diperlukan dan itu layak. ;)
George Duckett

Terima kasih - sebuah gambar bernilai ribuan kata, seperti kata mereka. :) Selain membuat dinding teks saya kurang mengintimidasi, saya ingin memberi pembaca gagasan tentang berapa banyak voxel yang dapat dihasilkan dengan kecepatan yang wajar menggunakan teknik yang dijelaskan.
Gavan Woolery

14
Saya masih berharap SE akan mengizinkan jawaban spesifik favorit.
joltmode

2
@ PatrickMoriarty # 15 adalah trik yang sangat umum. Dengan asumsi bahwa kompiler Anda tidak membuat optimasi ini (itu dapat membuka gulungan lingkaran Anda, tetapi mungkin tidak akan memadatkan array multidimensi). Anda ingin menyimpan semua data Anda di ruang memori yang berdekatan yang berdekatan, untuk caching. Array multidimensi dapat (berpotensi) dialokasikan di banyak ruang, karena merupakan array pointer. Sedangkan untuk membuka gulungan loop, pikirkan seperti apa kode yang dikompilasi terlihat. Agar swap register dan cache paling sedikit, Anda ingin menghasilkan vars / instruksi paling sedikit. Menurut Anda yang mana yang mengkompilasi menjadi lebih banyak?
Gavan Woolery

2
Sementara beberapa poin di sini bagus, terutama berkenaan dengan caching, threading, dan meminimalkan transfer GPU, beberapa di antaranya sangat tidak akurat. 5: SELALU menggunakan VBO / VAO alih-alih daftar tampilan. 6: Lebih banyak RAM hanya membutuhkan lebih banyak bandwidth. Dengan sadapan ke 12: Kebalikan EXACT berlaku untuk memori modern, yang setiap byte yang disimpan meningkatkan kemungkinan memasukkan lebih banyak data ke dalam cache. 14: Di Minecraft, ada lebih banyak titik daripada piksel (semua kubus yang jauh), jadi gerakkan perhitungan KE pixel shader, bukan DARI itu, lebih disukai dengan bayangan ditangguhkan.

7

Ada banyak hal yang bisa dilakukan Minecraft dengan lebih efisien. Misalnya, Minecraft memuat seluruh pilar vertikal sekitar 16x16 ubin dan menjadikannya. Saya merasa sangat tidak efisien untuk mengirim dan membuat banyak ubin tidak perlu. Tetapi saya tidak merasa bahwa pilihan bahasa adalah yang penting.

Java bisa sangat cepat tetapi untuk sesuatu yang berorientasi data ini, C ++ memang memiliki keuntungan besar dengan overhead yang jauh lebih sedikit untuk mengakses array dan bekerja dalam byte. Di sisi lain, jauh lebih mudah untuk melakukan threading di semua platform di Jawa. Kecuali Anda berencana untuk menggunakan OpenMP atau OpenCL, Anda tidak akan menemukan kenyamanan itu di C ++.

Sistem ideal saya akan menjadi hierarki yang sedikit lebih kompleks.

Tile adalah unit tunggal, kemungkinan sekitar 4 byte untuk menyimpan informasi seperti jenis material dan pencahayaan.

Segmen akan menjadi blok ubin berukuran 32x32x32.

  1. Bendera akan ditetapkan untuk masing-masing dari enam sisi, jika seluruh sisi itu adalah blok padat. Itu akan memungkinkan renderer menyumbat segmen di belakang segmen itu. Minecraft saat ini tampaknya tidak melakukan pengujian oklusi. Tapi ada yang menyebutkan bahwa penyumbatan perangkat keras tersedia yang bisa mahal tetapi lebih baik daripada memberikan sejumlah besar poligon pada kartu kelas bawah.
  2. Segmen hanya akan dimuat ke dalam memori selama aktivitas (pemain, NPC, fisika air, pertumbuhan pohon, dan sebagainya). Kalau tidak, mereka akan dikirim langsung masih dikompresi dari disk ke klien.

Sektor akan menjadi blok segmen 16x16x8.

  1. Sektor akan melacak segmen tertinggi untuk setiap kolom vertikal sehingga segmen yang lebih tinggi dari itu dapat dengan cepat ditentukan kosong.
  2. Itu juga akan melacak segmen bawah tersumbat, sehingga setiap segmen yang perlu dirender dari permukaan dapat dengan cepat direbut.
  3. Sektor-sektor juga akan melacak waktu berikutnya setiap segmen perlu diperbarui (fisika air, pertumbuhan pohon, dan sebagainya). Dengan cara ini memuat di setiap sektor akan cukup untuk menjaga dunia tetap hidup dan hanya memuat dalam segmen yang cukup lama untuk menyelesaikan tugas mereka.
  4. Semua posisi entitas akan dilacak relatif terhadap Sektor. Ini akan mencegah kesalahan titik apung yang ada di Minecraft saat bepergian sangat jauh dari pusat peta.

Dunia akan menjadi peta sektor yang tak terbatas.

  1. Dunia akan bertanggung jawab untuk mengelola sektor dan pembaruan mereka berikutnya.
  2. Dunia akan terlebih dahulu mengirim segmen ke pemain di sepanjang jalur potensial mereka. Minecraft secara reaktif mengirimkan segmen yang diminta klien, menghasut penundaan.

Saya umumnya menyukai ide ini, tetapi bagaimana Anda, secara internal, memetakan sektor-sektor Dunia ?
Clashsoft

Sementara array akan menjadi solusi terbaik untuk Ubin di Segmen dan Segmen di Sektor, Sektor di Dunia akan membutuhkan sesuatu yang berbeda untuk memungkinkan ukuran peta tanpa batas. Saran saya adalah menggunakan tabel hash (Kamus semu <Vector2i, Sektor>), menggunakan koordinat XY untuk hash. Kemudian Dunia dapat dengan mudah mencari sektor pada koordinat yang diberikan.
Josh Brown

6

Minecraft cukup cepat, bahkan pada 2-core saya. Java tampaknya tidak menjadi faktor pembatas, di sini, meskipun ada sedikit kelambatan server. Game lokal tampaknya lebih baik, jadi saya akan mengasumsikan beberapa inefisiensi, di sana.

Mengenai pertanyaan Anda, Notch (penulis Minecraft) telah membuat blog panjang lebar tentang teknologi. Secara khusus, dunia disimpan dalam "chunk" (Anda kadang-kadang melihat ini, terutama ketika seseorang hilang karena dunia belum terisi.), Jadi optimisasi pertama adalah memutuskan apakah suatu bongkahan dapat dilihat atau tidak .

Di dalam bongkahan, seperti yang sudah Anda duga, aplikasi harus memutuskan apakah suatu blok dapat dilihat atau tidak, berdasarkan apakah dikaburkan oleh blok lain atau tidak.

Perhatikan juga, bahwa ada FAKTA blok, yang dapat dianggap tidak terlihat, berdasarkan entah dikaburkan (yaitu, blok lain menutupi wajah) atau dengan arah mana kamera menunjuk (jika kamera menghadap Utara, Anda dapat dapat melihat wajah Utara blok APA PUN!)

Teknik umum juga termasuk tidak menyimpan objek blok yang terpisah tetapi, "sepotong" tipe blok, dengan satu blok prototipe untuk masing-masing, bersama dengan beberapa set data minimal untuk menggambarkan bagaimana blok ini dapat dikustomisasi. Misalnya, tidak ada blok granit khusus (yang saya tahu), tetapi air memiliki data untuk mengetahui seberapa dalam di sepanjang sisi-sisi, dari mana orang dapat menghitung arah alirannya.

Pertanyaan Anda tidak jelas jika Anda ingin mengoptimalkan kecepatan render, ukuran data, atau apa. Klarifikasi akan sangat membantu.


4
"rumpun" biasanya dinamai bongkahan.
Marco

Tangkapan yang bagus (+1); jawaban diperbarui. (Awalnya melakukan untuk memori, dan lupa kata yang tepat.)
Olie

Inefisiensi yang Anda rujuk juga dikenal sebagai "jaringan", yang tidak pernah bertindak dengan cara yang sama dua kali, bahkan dengan titik akhir yang sama dalam berkomunikasi.
Edwin Buck

4

Berikut ini hanya beberapa kata dari info dan saran umum, yang dapat saya berikan sebagai, um, modder Minecraft yang terlalu berpengalaman (yang mungkin setidaknya sebagian memberi Anda beberapa panduan.)

Alasan mengapa Minecraft lambat terkait dengan keputusan desain tingkat rendah yang dipertanyakan - misalnya, setiap kali sebuah blok direferensikan oleh pemosisian, permainan memvalidasi koordinat dengan sekitar 7 jika pernyataan untuk memastikan itu tidak di luar batas. . Selain itu, tidak ada cara untuk mengambil 'bongkahan' (unit blok 16x16x256 yang berfungsi dengan game) kemudian merujuk blok di dalamnya secara langsung, untuk mem-bypass pencarian cache dan, erm, masalah validasi konyol (iow, setiap referensi blok juga melibatkan pencarian bongkahan, antara lain.) Dalam mod saya, saya menciptakan cara untuk mengambil dan mengubah susunan blok secara langsung, yang mendorong generasi dungeon besar-besaran dari laggy yang tidak bisa dimainkan menjadi sangat cepat.

EDIT: Penghapusan klaim bahwa mendeklarasikan variabel pada ruang lingkup yang berbeda menghasilkan keuntungan kinerja, ini sebenarnya tidak tampak seperti masalahnya. Saya percaya pada saat itu saya menggabungkan hasil ini dengan sesuatu yang saya eksperimen (khususnya, menghilangkan gips antara ganda dan mengapung dalam kode terkait ledakan dengan menggabungkan ke ganda ... dapat dimengerti, ini memiliki dampak besar!)

Selain itu, meskipun ini bukan bidang tempat saya menghabiskan banyak waktu, sebagian besar gangguan kinerja di Minecraft adalah masalah rendering (sekitar 75% dari waktu permainan didedikasikan untuk itu di sistem saya). Jelas Anda tidak terlalu peduli jika kekhawatiran itu mendukung lebih banyak pemain dalam multipemain (server tidak membuat apa pun), tetapi itu penting sejauh mesin semua orang bisa bermain.

Jadi, apa pun bahasa yang Anda pilih, cobalah untuk menjadi sangat akrab dengan implementasi / detail level rendah, karena bahkan satu detail kecil dalam proyek seperti ini dapat membuat semua perbedaan (satu contoh bagi saya dalam C ++ adalah "Dapatkah kompiler menjalankan fungsi inline secara statis pointer? "Ya itu bisa! Membuat perbedaan yang luar biasa dalam salah satu proyek yang saya kerjakan, karena saya memiliki lebih sedikit kode dan keuntungan dari inlining.)

Saya benar-benar tidak menyukai jawaban itu karena itu membuat desain tingkat tinggi sulit, tetapi itu adalah kebenaran yang menyakitkan jika kinerja menjadi perhatian. Semoga Anda menemukan ini bermanfaat!

Juga, jawaban Gavin mencakup beberapa perincian yang saya tidak ingin ulangi (dan lebih banyak lagi! Dia jelas lebih banyak tahu tentang masalah ini daripada saya), dan saya setuju dengan dia untuk sebagian besar. Saya harus bereksperimen dengan komentarnya mengenai prosesor dan ukuran variabel yang lebih pendek, saya belum pernah mendengarnya - saya ingin membuktikan kepada diri sendiri bahwa itu benar!


2

Masalahnya adalah memikirkan bagaimana Anda pertama-tama memuat data. Jika Anda mengalirkan data peta ke memori saat diperlukan, ada batas wajar untuk apa yang dapat Anda render, ini sudah merupakan peningkatan kinerja rendering.

Apa yang Anda lakukan dengan data ini terserah Anda. Untuk kinerja GFX, Anda kemudian dapat menggunakan Kliping untuk klip objek yang tersembunyi, objek yang terlalu kecil untuk terlihat, dll.

Jika Anda hanya mencari teknik Kinerja Grafik, saya yakin Anda dapat menemukan banyak hal di internet.


1

Yang perlu dilihat adalah pola desain Flyweight . Saya percaya sebagian besar jawaban di sini merujuk pola desain ini dengan satu atau lain cara.

Meskipun saya tidak tahu metode pasti yang digunakan Minecraft untuk meminimalkan memori untuk setiap jenis blok, ini adalah cara yang mungkin untuk digunakan dalam game Anda. Idenya adalah memiliki hanya satu objek, seperti objek prototipe, yang menyimpan informasi tentang semua blok. Satu-satunya perbedaan adalah lokasi setiap blok.

Tetapi bahkan lokasi dapat diminimalkan: jika Anda tahu satu blok tanah adalah satu jenis, mengapa tidak menyimpan dimensi tanah itu sebagai satu blok raksasa, dengan satu set data lokasi?

Jelas satu-satunya cara untuk mengetahui adalah mulai menerapkan sendiri, dan melakukan beberapa tes memori untuk kinerja. Marilah kita tahu bagaimana kelanjutannya!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.