Bagaimana cara menghapus entitas dari loop game saya ketika sudah mati?

16

Ok jadi saya punya daftar besar semua entitas saya yang saya lewati dan perbarui. Di AS3 saya bisa menyimpan ini sebagai Array (panjang dinamis, tidak diketik), Vektor (diketik) atau daftar tertaut (bukan asli). Saat ini saya menggunakan Array tetapi saya berencana untuk mengubah ke Vector atau daftar yang ditautkan jika lebih cepat.

Lagi pula, pertanyaan saya, ketika Entitas dihancurkan, bagaimana saya harus menghapusnya dari daftar? Saya dapat membatalkan posisinya, memisahkannya atau hanya memasang bendera di atasnya untuk mengatakan "lewati aku, aku sudah mati." Saya mengumpulkan entitas saya, jadi Entitas yang sudah mati kemungkinan besar akan hidup kembali di beberapa titik. Untuk setiap jenis koleksi, apa strategi terbaik saya, dan kombinasi jenis pengumpulan dan metode penghapusan mana yang paling berhasil?

Iain
sumber
Panjang vektor tidak tetap, tetapi diketik, dan itu membuatnya lebih unggul dari Array. Kekurangannya adalah tidak ada sintaks cepat untuk mendefinisikan daftar yang sudah diisi sebelumnya, tetapi Anda tidak perlu berpikir seperti itu.
Bart van Heukelom

Jawaban:

13

Saya akan menyimpan semua add / remove dalam daftar terpisah dan melakukan operasi tersebut setelah saya mengulangi pembaruan-loop.

Simon
sumber
10

Kerangka Flixel menggunakan bendera mati (sebenarnya beberapa bendera yang menentukan apakah itu harus ditarik, diperbarui, dan sebagainya). Saya akan mengatakan bahwa jika Anda akan menghidupkan kembali entitas, dan jika kinerja adalah masalah, Anda menggunakan bendera mati. Dalam pengalaman saya, instantiasi entitas baru adalah operasi paling mahal dalam use case yang Anda gambarkan, dan menyambungkan atau membatalkan elemen dapat menyebabkan memori mengembung mengingat pengumpulan sampah Flash yang kadang-kadang sarat dengan sampah.

Gregory Avery-Weir
sumber
1
+1 untuk flixel. Mendaur ulang yang deadsangat membantu dengan kinerja.
Snow Blind
3

Sementara beberapa teknik secara inheren lebih efisien daripada yang lain, itu hanya masalah jika Anda kehabisan siklus pada platform target Anda. Gunakan teknik apa pun yang memungkinkan Anda menyelesaikan permainan dengan lebih cepat. Cobalah untuk tidak bergantung pada implementasi spesifik dari struktur data kontainer Anda sementara itu dan itu akan membantu Anda untuk mengoptimalkan setelahnya jika Anda membutuhkannya.

Hanya untuk membahas beberapa teknik yang sudah dibahas oleh orang lain di sini. Jika urutan entitas penting, maka bendera mati dapat memungkinkan Anda untuk melakukan splice selama loop pembaruan Anda pada frame berikutnya. misalnya. pseudocode sangat sederhana:

void updateGame()
{
  // updateEntities()
  Entity* pSrcEntity = &mEntities[0];
  Entity* pDstEntity = &mEntities[0];
  newNumEntities = 0;
  for (int i = 0; i < numEntities; i++)
  {
    if (!pSrcEntity->isDead)
    {
       // could be inline but whatever.
       updateEntity(pDstEntity, pSrcEntity);
       // if entity just died, don't update the pDstEntity pointer, 
       // and just let the next entity updated overwrite it.
       if (!pDstEntity->isDead)
       {
          pDstEntity++;
          newNumEntities++;
       }
    }
    pSrcEntity++;
  }
}
numEntities = newNumEntities;

Ini adalah karakteristik dari skema ini:

  • kekompakan alami entitas (walaupun dengan kemungkinan 1 frame latensi sebelum slot entitas dapat direklamasi).
  • tidak ada masalah pemesanan ulang secara acak.
  • sementara daftar Linkedly ganda memiliki O (1) penyisipan / penghapusan, tetapi sangat sulit untuk membuat prefetch cache latency yang optimal. Menyimpannya dalam array yang ringkas memungkinkan teknik prefetching blok bekerja dengan baik.
  • Dalam hal penghancuran multi-objek, Anda tidak perlu melakukan shift-copy yang berlebihan untuk menjaga ketertiban dan kekompakan (semuanya dilakukan sekali selama pembaruan pass)
  • Anda memanfaatkan sentuhan data yang harus sudah ada dalam cache selama pembaruan.
  • Ini berfungsi dengan baik jika sumber dan entitas tujuan Anda akan memisahkan array. Anda kemudian dapat menggandakan buffer array entitas Anda untuk memanfaatkan multicore / eg. satu utas memperbarui / menulis entitas untuk bingkai N, sedangkan utas lain sedang merender entitas bingkai sebelumnya untuk bingkai N-1.
  • Kekompakan berarti lebih mudah untuk DMA keseluruhan untuk prosesor heterogen untuk bahkan lebih banyak CPU misalnya. SPU atau GPU.
jpaver
sumber
+1. Saya suka ini. Meskipun saya hampir tidak pernah membutuhkan pembaruan yang dipesan dalam suatu kumpulan, saya akan menambahkannya ke dalam kantong hal-hal yang perlu diingat jika saya mengalami situasi: o)
Kaj
2

Berbicara dalam hal pengalaman pemrograman umum saya, splicing biasanya merupakan operasi lambat, yang melibatkan pengalihan semua elemen yang ada ke atas. Saya pikir menetapkannya ke nol akan menjadi solusi terbaik di sini ; bendera mati akan berfungsi tetapi Anda harus berhati-hati untuk tidak membiarkannya membuat kode Anda berantakan.

Kami sebenarnya hanya berbicara tentang pengumpulan sumber daya di ruang obrolan, sebenarnya. Ini latihan yang sangat bagus, dan senang mendengar Anda melakukan itu. :)

Ricket
sumber
1
Jika urutan pembaruan tidak penting, splicing semudah memindahkan entitas terakhir ke indeks saat ini dan mengurangi jumlah pool dan indeks iterator Anda.
Kaj
Wow, poin bagus Kaj! :)
Ricket
2

Secara pribadi, saya akan menggunakan daftar tertaut. Iterasi daftar yang disukai cepat, serta menambah dan menghapus item. Menggunakan Array atau Vector akan menjadi pilihan yang baik jika Anda membutuhkan akses langsung ke item dalam struktur (mis. Akses ke indeks), tetapi itu tidak terdengar seperti Anda membutuhkannya.

Setiap kali Anda menghapus item dari daftar tertaut, Anda dapat menambahkannya ke kumpulan objek yang kemudian dapat didaur ulang untuk menghemat alokasi memori.

Saya telah menggunakan struktur data poligonal di beberapa proyek dan sangat senang dengan mereka.

Sunting: Maaf, saya pikir jawabannya tidak terlalu jelas dalam hal strategi penghapusan: Saya sarankan untuk menghapus item dari daftar, segera setelah itu mati dan menambahkannya langsung ke struktur penyatuan (daur ulang). Karena menghapus item dari daftar tertaut sangat performan, saya tidak melihat masalah dalam melakukannya.

bummzack
sumber
1
Saya berasumsi bahwa Anda menyarankan daftar tautan ganda di sini? (maju / mundur)? Juga: Apakah Anda menyarankan semacam kumpulan atas elemen tautan atau apakah Anda secara dinamis mengalokasikan masing-masing pemegang penunjuk dalam daftar tertaut?
Simon
Ya, itu haruslah daftar dobel-tautan yang paling cocok untuk tugas itu. Terima kasih telah menunjukkan itu! Mengenai penggunaan kembali barang: Saya sedang memikirkan kelas penyatuan khusus / struktur data, yang menciptakan objek baru berdasarkan permintaan atau menggunakan contoh yang ada jika ada beberapa di kelompok tersebut. Karenanya akan lebih baik untuk benar-benar menghapus item "mati" dari daftar dan menambahkannya ke kolam untuk penggunaan nanti.
bummzack
Daftar yang terhubung sendiri akan baik-baik saja. Daftar yang tertaut ganda hanya memberikan keuntungan dengan pengulangan di kedua arah. Untuk beralih melalui daftar yang terhubung sendiri dengan opsi untuk menghapus item saat ini, Anda harus melacak entri sebelumnya.
deft_code
@caspin ya persisnya. Jika Anda menggunakan daftar tunggal-tertaut, maka Anda perlu melacak node sebelumnya dan menautkan nextpointer mereka ke node setelah yang dihapus. Jika Anda tidak ingin repot melakukan ini sendiri, daftar double-linked akan menjadi Struktur Data pilihan.
bummzack
1

"Cukup tetapkan bendera di atasnya untuk mengatakan" lewati aku, aku sudah mati. "Aku mengumpulkan entitasku, jadi Entitas yang sudah mati kemungkinan besar akan hidup kembali di beberapa titik"

Saya pikir Anda menjawab pertanyaan Anda sendiri sehubungan dengan aplikasi spesifik ini. Saya akan menjauh dari array jika Anda berencana untuk mengerjakannya selain dari push dan pop. Daftar tertaut akan menjadi cara yang lebih cerdas untuk dilakukan jika Anda berencana melakukan operasi berat. Dengan semua yang dikatakan, jika Anda berencana untuk mengintegrasikan kembali entitas yang sama ke dalam game maka masuk akal untuk hanya menetapkan variabel boolean dan memeriksanya selama loop operasi game.

batang
sumber
0

Salah satu solusi umum dan bersih yang saya temukan pada lib yang saya gunakan adalah menggunakan peta yang dapat dikunci.

Anda memiliki 2 operasi lock()dan unlock(), sementara Anda mengulangi peta Anda akan lock(), sekarang dari titik ini setiap operasi yang mengubah peta tidak berpengaruh, itu hanya akan didorong ke dalam CommandQueueyang akan berjalan setelah Anda menelepon unlock().

Jadi menghapus entitas akan memiliki pseudo-code berikut:

void lockableMap::remove(std::string id) {
   if(isLocked) {
       commandQueue.add(new RemoveCommand(id));
   } else {
       //remove element from map
   }

dan ketika kamu unlock()

isLocked = false
commandQueue.execute(this);

Satu-satunya hal yang harus Anda pertimbangkan adalah bahwa Anda hanya akan menghapus entitas setelah loop.

EDIT: ini adalah solusi yang diusulkan oleh Simon.

GriffinHeart
sumber
0

Saya punya dua metode.

Saat Anda memanggil objek yang akan dihapus, itu benar-benar menetapkan dua flag:

1.Satu untuk memberitahu wadah bahwa suatu objek telah dihapus

2.Satu untuk memberitahu kontainer objek mana yang telah diminta untuk dihapus

void object::deleteObject()
{
    container->objectHasBeenDeleted = true;
    isToDelete = true;
}

Satu Menggunakan vektor objek

std::vector<object*> objects;

Kemudian dalam fungsi pembaruan, periksa untuk melihat apakah suatu objek telah dihapus dan jika demikian iterate melalui semua objek dan hapus yang memiliki tanda hapus

void container::update()
{
    if (objectHasBeenDeleted)
    {
        std::vector<object*>::iterator ListIterator;
        for(ListIterator=objects.begin(); ListIterator!=objects.end();)
        {
            if( (*ListIterator)->isToDelete )
            {
                ListIterator = objects.erase(ListIterator);
                delete *ListIterator;
            }
            else {
                ++ListIterator;
            }
        }
    objectHasBeenDeleted = false;
    }
}

Dua Menggunakan vektor (pointer ke a) objek.

std::vector<object*> *objects;

Dalam fungsi pembaruan, jika suatu objek akan dihapus, beralih melalui objek dan tambahkan objek yang tidak akan dihapus ke vektor baru. hapus vektor objek dan atur pointer ke vektor baru

void container::update()
{
    if (objectHasBeenDeleted)
    {
        std::vector<object*> *newVector;
        unsigned long i;
        for (i = 0; i < objects->size(); i++)
        {
            if (!objects->at(i)->isToDelete)
            {
                newVector->push_back(objects->at(i));
            }
        }
        delete objects;
        objects = newVector;
        objectHasBeenDeleted = false;
    }
}

sumber