Menyingkirkan benda mati dalam permainan secara efektif?

10

Saya menggunakan for for atau foreach loop (tidak masalah) untuk mengulangi semua objek saya ketika mereka perlu diperbarui atau digambar. Namun, ketika sebuah objek terbunuh, saya ingin itu dihapus dari koleksi lagi.

Saya melakukan ini dengan menambahkan objek ke daftar objek mati, dan kemudian, ketika semuanya telah ditarik dan diperbarui, saya memeriksa semua yang ada di daftar ini, menghapus objek dalam daftar dari sumber aslinya juga.

Apakah ada cara yang lebih baik untuk melakukan ini?

Mathias Lykkegaard Lorenzen
sumber
2
Ini benar-benar tidak ada hubungannya dengan pengumpulan sampah, meskipun penggunaan kata "sampah" dalam judul dan jawaban sejauh yang telah disebutkan .NET Sampah Kolektor.
Andrew Russell
Saya mengambil kebebasan untuk benar-benar mengubah kata "sampah" menjadi "mati", untuk mencegah kebingungan dengan pengumpul sampah .NET.
Nevermind
Oh saya tahu itu. Artikel pengumpulan sampah masih relevan karena mungkin berperan dalam apa yang Anda lakukan dengan benda mati itu. Misalnya puing mati? Putuskan tautannya dari segalanya, setel ulang ke default, masukkan kembali ke keranjang puing.
doppelgreener

Jawaban:

11

Pertama-tama: terminologi. Jika Anda mengatakan "daftar sampah", orang akan berpikir Anda berbicara tentang pengumpul sampah. Sebut saja "daftar mati".

Seperti yang mungkin telah Anda temukan, maka pertanyaan Anda, Anda tidak dapat menghapus item dari koleksi saat Anda mengulanginya. Jika koleksi Anda adalah List<>maka cara paling sederhana untuk menghapus item dari itu adalah:

for(int i = list.Count-1; i >= 0; --i)
{
    if(list[i].IsDead)
        list.RemoveAt(i);
}

Perhatikan bahwa Anda mengulangi mundur . Anda dapat menghapus item saat iterasi ke depan, tetapi lebih berantakan dan membutuhkan goto. Anda tidak dapat melakukannya dengan foreach.

Anda dapat melakukan penghapusan secara terpisah dari loop pembaruan Anda. Atau Anda dapat menggabungkannya ke dalam loop pembaruan Anda. Selalu lakukan RemoveAtpada akhir loop - setelah titik itu dalam loop, list[i]tidak dapat dianggap valid (itu menjadi valid lagi pada iterasi berikutnya).

Perhatikan, juga, bahwa setiap objek memiliki IsDeadbenderanya sendiri (jika Anda menggunakan pola pembuangan, Anda dapat membuatnya IsDisposed) - Anda sebenarnya tidak perlu mempertahankan "daftar mati" sama sekali.

Menggunakan bendera pada setiap item lebih disukai untuk kinerja, karena Anda menghindari harus mencari melalui daftar untuk melakukan penghapusan. Dan itu juga lebih baik untuk desain - itu berarti bahwa setiap objek dapat dengan mudah memeriksa apakah sudah mati - sehingga Anda tidak sengaja memanggil metode apa pun di atasnya (jika objek ini menerapkan pola sekali pakai, mereka dapat membuangObjectDisposedException dalam kasus ini).

Jika Anda memiliki koleksi selain a List<>, maka menghapus item yang mati dari koleksi itu mungkin masih melibatkan pembuatan daftar mati (karena penghapusan selama iterasi mungkin tidak mungkin). Menurut pendapat saya, lebih baik, lebih bijaksana secara desain, untuk membuat daftar mati tepat sebelum digunakan, dengan mengulangi koleksi yang mencari IsDeadbendera, daripada mencoba mempertahankan daftar ketika benda terbunuh.

Setelah Anda selesai dengan daftar yang mati, Anda harus Clear()melakukannya, dan menyimpannya untuk menggunakannya kembali nanti.

Andrew Russell
sumber
Jadi pola pakai dianggap lebih 'efektif'?
Jonathan Connell
@ Jonathan: Saya tidak mengerti pertanyaan Anda. The IsDeadPola adalah fungsional identik dengan IsDisposed. Secara semantik menggunakan IsDeadmenyiratkan bahwa Anda mempertahankan daftar Draw / Update objek-objek ini yang akan menghapus item mati dari itu di lain waktu. Pola pembuangan tidak menyiratkan hal itu. Selain itu, pola pembuangan tidak menyiratkan bahwa Anda mungkin memiliki sumber daya yang tidak dikelola yang dipegang oleh objek tersebut (yang biasanya tidak sesuai untuk objek gameplay).
Andrew Russell
Anda dapat melakukannya dengan foreach dengan menggunakan terbalik.
shinzou
0

99,9% dari waktu, pengumpul sampah (GC) akan mengurus semuanya tanpa kerepotan. Biarkan saja fungsinya setelah Anda menghapus / membatalkan objek-objek itu. Ada latensi dalam pembersihan yang tergantung pada sejumlah faktor (berapa banyak blok memori yang telah ditetapkan misalnya) tetapi Anda tidak perlu biasanya tahu tentang itu, apalagi khawatir tentang hal itu. Ini dirancang hanya untuk bekerja. Yang merupakan salah satu alasan Anda menggunakan C # dan bukan C.

Re kinerja GC secara umum, jangan terlalu mengkhawatirkan diri sendiri kecuali jika Anda tahu (melalui profil) bahwa hal itu menciptakan kemacetan - ini hanya dalam permainan yang cukup menuntut, pada umumnya. Jika demikian, lihat GC.Collect (), dan berbagai perangkapnya, sehingga Anda tahu kapan waktu yang tepat untuk menggunakannya. Saya sarankan googling "force gc xna", ada banyak materi di luar sana.

Insinyur
sumber
Jadi, jika saya memiliki daftar yang saya gunakan dalam panggilan Pembaruan () saya setiap waktu, semua elemen di dalamnya di mana elemen "null" akan secara otomatis dibersihkan, DAN dihapus?
Mathias Lykkegaard Lorenzen
@Mathias No. Lihat komentar saya pada pertanyaan Anda. Pengumpul sampah tidak akan mengubah apa pun yang masih dapat Anda akses - termasuk koleksi Anda dan isinya.
Andrew Russell
1
Pertanyaannya tidak ada hubungannya dengan GC, kecuali pilihan kata "sampah" yang tidak menguntungkan.
Nevermind