Bagaimana cara mengoptimalkan mesin tumbukan di mana urutannya signifikan dan tumbukan bersyarat berdasarkan grup objek?

14

Jika ini adalah pertama kalinya Anda pada pertanyaan ini, saya sarankan membaca bagian pra-pembaruan di bawah ini terlebih dahulu, lalu bagian ini. Berikut adalah sintesis masalah:

Pada dasarnya, saya memiliki mesin pendeteksi tabrakan dan resolusi dengan sistem partisi spasial grid di mana urutan-tabrakan dan kelompok tabrakan penting. Satu tubuh pada suatu waktu harus bergerak, kemudian mendeteksi tabrakan, lalu menyelesaikan tabrakan. Jika saya memindahkan semua benda sekaligus, maka menghasilkan kemungkinan pasangan tabrakan, itu jelas lebih cepat, tetapi resolusi pecah karena urutan tabrakan tidak dihormati. Jika saya menggerakkan satu tubuh sekali waktu, saya dipaksa untuk mendapatkan tubuh untuk memeriksa tabrakan, dan itu menjadi masalah. Letakkan kelompok dalam campuran, dan Anda dapat membayangkan mengapa itu menjadi sangat lambat sangat cepat dengan banyak tubuh.


Pembaruan: Saya telah bekerja sangat keras dalam hal ini, tetapi tidak dapat mengelola untuk mengoptimalkan apa pun.

Saya berhasil menerapkan "lukisan" yang dideskripsikan oleh Will dan mengubah grup menjadi bitset, tetapi itu adalah speedup yang sangat kecil.

Saya juga menemukan masalah besar : mesin saya tergantung pesanan.

Saya mencoba implementasi generasi tabrakan yang unik , yang pasti mempercepat semuanya, tetapi mematahkan urutan tabrakan .

Biarkan saya jelaskan:

  • dalam desain asli saya (tidak menghasilkan pasangan), ini terjadi:

    1. satu tubuh bergerak
    2. setelah bergerak, ia menyegarkan sel-selnya dan membuat tubuh yang bertabrakan dengannya
    3. jika tumpang tindih dengan benda yang harus diatasi, selesaikan tabrakan

    ini berarti bahwa jika suatu benda bergerak, dan mengenai dinding (atau benda lain mana pun), hanya benda yang telah bergerak yang akan menyelesaikan tabrakannya dan benda lainnya tidak akan terpengaruh.

    Ini adalah perilaku yang saya inginkan .

    Saya mengerti itu tidak umum untuk mesin fisika, tetapi memiliki banyak keuntungan untuk game bergaya retro .

  • dalam desain kisi biasa (menghasilkan pasangan unik), ini terjadi:

    1. semua tubuh bergerak
    2. setelah semua tubuh bergerak, segarkan semua sel
    3. menghasilkan pasangan tabrakan yang unik
    4. untuk setiap pasangan, menangani deteksi dan resolusi tabrakan

    dalam hal ini gerakan simultan bisa mengakibatkan dua tubuh tumpang tindih, dan mereka akan menyelesaikan pada saat yang sama - ini secara efektif membuat tubuh "saling mendorong", dan merusak stabilitas tabrakan dengan banyak benda

    Perilaku ini umum untuk mesin fisika, tetapi tidak dapat diterima dalam kasus saya .

Saya juga menemukan masalah lain, yang utama (bahkan jika itu tidak mungkin terjadi dalam situasi dunia nyata):

  • pertimbangkan badan kelompok A, B dan W
  • A bertabrakan dan memutuskan melawan W dan A
  • B bertabrakan dan memutuskan melawan W dan B
  • A tidak melakukan apa pun terhadap B
  • B tidak melakukan apa pun terhadap A

mungkin ada situasi di mana banyak tubuh A dan tubuh B menempati sel yang sama - dalam hal ini, ada banyak iterasi yang tidak perlu antara tubuh yang tidak boleh bereaksi satu sama lain (atau hanya mendeteksi tabrakan tetapi tidak menyelesaikannya) .

Untuk 100 mayat yang menempati sel yang sama, ini 100 ^ 100 iterasi! Ini terjadi karena pasangan unik tidak dihasilkan - tetapi saya tidak dapat menghasilkan pasangan unik , jika tidak saya akan mendapatkan perilaku yang tidak saya inginkan.

Apakah ada cara untuk mengoptimalkan jenis mesin tabrakan ini?

Ini adalah pedoman yang harus dihormati:

  • Urutan tabrakan sangat penting!

    • Tubuh harus bergerak satu per satu , kemudian memeriksa tabrakan satu per satu , dan menyelesaikan setelah gerakan satu per satu .
  • Badan harus memiliki 3 bit grup

    • Grup : grup yang dimiliki tubuh
    • GroupsToCheck : grup yang harus dideteksi tubuh terhadap tabrakan
    • GroupsNoResolve : mengelompokkan tubuh yang tidak boleh menyelesaikan tabrakan
    • Mungkin ada situasi di mana saya hanya ingin tabrakan terdeteksi tetapi tidak diselesaikan



Pra-perbarui:

Kata Pengantar : Saya sadar bahwa mengoptimalkan hambatan ini bukanlah suatu keharusan - mesin sudah sangat cepat. Saya, bagaimanapun, untuk tujuan yang menyenangkan dan mendidik, akan senang menemukan cara untuk membuat mesin lebih cepat.


Saya membuat mesin pendeteksi / respons tabrakan C ++ 2D untuk keperluan umum, dengan penekanan pada fleksibilitas dan kecepatan.

Berikut diagram arsitekturnya yang sangat mendasar:

Arsitektur mesin dasar

Pada dasarnya, kelas utamanya adalah World, yang memiliki (mengelola memori) dari a ResolverBase*, a SpatialBase*dan a vector<Body*>.

SpatialBase adalah kelas virtual murni yang berurusan dengan deteksi tabrakan fase luas.

ResolverBase adalah kelas virtual murni yang berurusan dengan resolusi tabrakan.

Tubuh berkomunikasi World::SpatialBase*dengan SpatialInfobenda - benda, yang dimiliki oleh tubuh itu sendiri.


Saat ini ada satu kelas spasial:, yang merupakan Grid : SpatialBasegrid 2D tetap dasar. Ini memiliki kelas info sendiri GridInfo : SpatialInfo,.

Begini tampilannya arsitekturnya:

Arsitektur mesin dengan grid spasial

The Gridkelas memiliki array 2D Cell*. The Cellkelas berisi kumpulan (tidak dimiliki) Body*: a vector<Body*>yang berisi semua mayat yang ada di sel.

GridInfo benda juga mengandung petunjuk yang tidak memiliki sel-sel di mana tubuh berada.


Seperti yang saya katakan sebelumnya, mesin didasarkan pada kelompok.

  • Body::getGroups()mengembalikan a std::bitsetdari semua grup yang menjadi bagian tubuh.
  • Body::getGroupsToCheck()mengembalikan a std::bitsetdari semua grup yang harus diperiksa oleh tubuh terhadap benturan

Tubuh dapat menempati lebih dari satu sel. GridInfo selalu menyimpan pointer yang tidak memiliki ke sel yang ditempati.


Setelah satu tubuh bergerak, deteksi tabrakan terjadi. Saya berasumsi bahwa semua benda adalah kotak pembatas sumbu-sejajar.

Cara kerja deteksi tabrakan fase lebar:

Bagian 1: pembaruan info spasial

Untuk masing-masing Body body:

    • Sel yang ditempati paling kiri atas dan sel yang ditempati paling kanan bawah dihitung.
    • Jika mereka berbeda dari sel-sel sebelumnya, body.gridInfo.cellsdibersihkan, dan diisi dengan semua sel yang ditempati tubuh (2D untuk loop dari sel paling kiri atas ke sel paling kanan bawah).
  1. body sekarang dijamin untuk mengetahui sel apa yang ditempatinya.

Bagian 2: cek tabrakan yang sebenarnya

Untuk masing-masing Body body:

  • body.gridInfo.handleCollisions disebut:

void GridInfo::handleCollisions(float mFrameTime)
{
    static int paint{-1};
    ++paint;

    for(const auto& c : cells)
        for(const auto& b : c->getBodies())
        {
            if(b->paint == paint) continue;
            base.handleCollision(mFrameTime, b);
            b->paint = paint;
        }
}

void Body::handleCollision(float mFrameTime, Body* mBody)
    {
        if(mBody == this || !mustCheck(*mBody) || !shape.isOverlapping(mBody->getShape())) return;

        auto intersection(getMinIntersection(shape, mBody->getShape()));

        onDetection({*mBody, mFrameTime, mBody->getUserData(), intersection});
        mBody->onDetection({*this, mFrameTime, userData, -intersection});

        if(!resolve || mustIgnoreResolution(*mBody)) return;
        bodiesToResolve.push_back(mBody);
    }

  • Tabrakan kemudian diselesaikan untuk setiap tubuh di bodiesToResolve.

  • Itu dia.


Jadi, saya sudah mencoba untuk mengoptimalkan deteksi tabrakan fase luas ini cukup lama sekarang. Setiap kali saya mencoba sesuatu yang lain dari arsitektur / setup saat ini, sesuatu tidak berjalan sesuai rencana atau saya membuat asumsi tentang simulasi yang kemudian terbukti salah.

Pertanyaan saya adalah: bagaimana saya bisa mengoptimalkan fase luas dari mesin tabrakan saya ?

Apakah ada beberapa optimasi C ++ ajaib yang dapat diterapkan di sini?

Dapatkah arsitektur dirancang ulang untuk memungkinkan kinerja yang lebih baik?


Output callgrind untuk versi terbaru: http://txtup.co/rLJgz

Vittorio Romeo
sumber
Profil dan identifikasi kemacetan. Biarkan kami tahu di mana mereka berada, maka kami memiliki sesuatu untuk dikerjakan.
Maik Semder
@MaikSemder: Saya melakukan itu, dan menulisnya di pos. Ini satu-satunya cuplikan kode yang menjadi hambatannya. Maaf jika ini panjang dan terperinci, tapi itu bagian dari pertanyaan karena saya yakin bahwa kemacetan ini hanya bisa diselesaikan dengan mengubah sesuatu dalam desain mesin.
Vittorio Romeo
Maaf, sulit ditemukan. Bisakah Anda memberi kami beberapa nomor? Fungsi waktu dan jumlah objek yang diproses dalam fungsi itu?
Maik Semder
@MaikSemder: diuji dengan Callgrind, pada biner yang dikompilasi dengan Dentang 3.4 SVN -O3: 10000 badan dinamis - fungsinya getBodiesToCheck()dipanggil 5462334 kali, dan mengambil 35,1% dari seluruh waktu pembuatan profil (Waktu akses baca instruksi)
Vittorio Romeo
2
@ Quonux: jangan tersinggung. Saya suka "menciptakan kembali roda". Saya bisa mengambil Bullet atau Box2D dan membuat permainan dengan perpustakaan itu, tapi itu bukan tujuan saya. Saya merasa jauh lebih puas dan belajar lebih banyak dengan menciptakan sesuatu dari awal dan mencoba mengatasi hambatan yang muncul - bahkan jika itu berarti frustrasi dan meminta bantuan. Selain keyakinan saya bahwa pengkodean dari awal sangat berharga untuk tujuan belajar, saya juga merasa sangat menyenangkan, dan senang menghabiskan waktu luang saya.
Vittorio Romeo

Jawaban:

14

getBodiesToCheck()

Mungkin ada dua masalah dengan getBodiesToCheck()fungsi tersebut; pertama:

if(!contains(bodiesToCheck, b)) bodiesToCheck.push_back(b);

Bagian ini O (n 2 ) bukan?

Daripada memeriksa untuk melihat apakah tubuh sudah ada dalam daftar, gunakan lukisan sebagai gantinya.

loop_count++;
if(!loop_count) { // if loop_count can wrap,
    // you just need to iterate all bodies to reset it here
}
bodiesToCheck.clear();
for(const auto& q : queries)
    for(const auto& b : *q)
        if(b->paint != loop_count) {
            bodiesToCheck.push_back(b);
            b->paint = loop_count;
        }
return bodiesToCheck;

Anda mendereferensi pointer pada fase kumpulkan, tetapi Anda tetap akan melakukan redereferensi pada fase uji jadi jika Anda memiliki cukup L1 itu bukan masalah besar. Anda dapat meningkatkan kinerja dengan menambahkan petunjuk pra-pengambilan ke kompiler juga misalnya __builtin_prefetch, meskipun itu lebih mudah dengan for(int i=q->length; i-->0; )loop klasik dan semacamnya.

Itu tweak sederhana, tetapi pemikiran kedua saya adalah bahwa mungkin ada cara yang lebih cepat untuk mengatur ini:

Namun, Anda dapat beralih menggunakan bitmap , dan menghindari seluruh bodiesToCheckvektor. Inilah pendekatannya:

Anda sudah menggunakan kunci integer untuk tubuh, tetapi kemudian mencari mereka di peta dan hal-hal dan menyimpan sekitar daftar mereka. Anda dapat pindah ke pengalokasi slot, yang pada dasarnya hanya sebuah array atau vektor. Misalnya:

class TBodyImpl {
   public:
       virtual ~TBodyImpl() {}
       virtual void onHit(int other) {}
       virtual ....
       const int slot;
   protected:
      TBodyImpl(int slot): slot(slot_) {}
};

struct TBodyBase {
    enum ... type;
    ...
    rect_t rect;
    TQuadTreeNode *quadTreeNode; // see below
    TBodyImpl* imp; // often null
};

std::vector<TBodyBase> bodies; // not pointers to them

Apa artinya ini adalah bahwa semua hal yang diperlukan untuk melakukan tabrakan sebenarnya adalah dalam memori yang ramah-cache linear, dan Anda hanya pergi ke bit implementasi khusus dan melampirkannya ke salah satu slot ini jika ada beberapa yang perlu.

Untuk melacak alokasi dalam vektor benda ini, Anda dapat menggunakan array bilangan bulat sebagai bitmap dan menggunakan bit twiddling atau lainnya __builtin_ffs. Ini sangat efisien untuk berpindah ke slot yang saat ini ditempati, atau menemukan slot kosong di dalam array. Anda bahkan dapat memadatkan array kadang-kadang jika tumbuh terlalu besar dan kemudian banyak ditandai dihapus, dengan menggerakkan mereka di ujung untuk mengisi celah.

hanya periksa setiap tabrakan sekali

Jika Anda sudah memeriksa jika sebuah bertabrakan dengan b , Anda tidak perlu untuk memeriksa apakah b bertabrakan dengan sebuah juga.

Ini mengikuti dari menggunakan bilangan bulat id bahwa Anda menghindari pemeriksaan ini dengan pernyataan if sederhana. Jika id dari tabrakan potensial kurang dari atau sama dengan id saat ini sedang diperiksa, itu dapat dilewati! Dengan cara ini, Anda hanya akan memeriksa setiap kemungkinan pasangan satu kali; itu akan lebih dari setengah jumlah cek tabrakan.

unsigned * bitmap;
int bitmap_len;
...

for(int i=0; i<bitmap_len; i++) {
  unsigned mask = bitmap[i];
  while(mask) {
      const int j = __builtin_ffs(mask);
      const int slot = i*sizeof(unsigned)*8+j;
      for(int neighbour: get_neighbours(slot))
          if(neighbour > slot)
              check_for_collision(slot,neighbour);
      mask >>= j;
  }

hormati urutan tabrakan

Daripada mengevaluasi tabrakan segera setelah pasangan ditemukan, hitung jarak untuk memukul dan menyimpannya dalam tumpukan biner . Tumpukan ini adalah bagaimana Anda biasanya melakukan antrian prioritas dalam pencarian jalur, sehingga sangat berguna kode utilitas.

Tandai setiap node dengan nomor urut, sehingga Anda dapat mengatakan:

  • A 10 hit B 12 at 6
  • A 10 hit C 12 at 3

Tentunya setelah Anda mengumpulkan semua tabrakan, Anda mulai muncul dari antrian prioritas, paling cepat terlebih dahulu. Jadi yang pertama Anda dapatkan adalah A 10 hits C 12 pada 3. Anda menambah nomor urut setiap objek ( 10 bit), mengevaluasi tabrakan, dan menghitung jalur baru mereka, dan menyimpan tabrakan baru mereka dalam antrian yang sama. Tabrakan baru adalah A 11 hit B 12 at 7. Antrian sekarang memiliki:

  • A 10 hit B 12 at 6
  • A 11 hits B 12 pada 7

Kemudian Anda pop dari antrian prioritas dan yang A 10 B hit 12 di 6. Tapi Anda melihat bahwa A 10 adalah basi ; A saat ini di 11. Jadi Anda dapat membuang tabrakan ini.

Penting untuk tidak repot-repot mencoba menghapus semua tabrakan basi dari pohon; menghapus dari tumpukan itu mahal. Buang saja ketika Anda meletuskannya.

grid

Anda harus mempertimbangkan menggunakan quadtree sebagai gantinya. Ini adalah struktur data yang sangat mudah untuk diterapkan. Seringkali Anda melihat implementasi yang menyimpan poin tetapi saya lebih suka menyimpan rect, dan menyimpan elemen dalam node yang berisi itu. Ini berarti bahwa untuk memeriksa tabrakan, Anda hanya perlu mengulangi semua badan, dan, untuk masing-masing, memeriksanya terhadap benda-benda di simpul quad-tree yang sama (menggunakan trik penyortiran yang diuraikan di atas) dan semua yang ada di simpul quad-tree induk. Quad-tree itu sendiri adalah daftar kemungkinan tabrakan.

Ini Quadtree sederhana:

struct Object {
    Rect bounds;
    Point pos;
    Object * prev, * next;
    QuadTreeNode * parent;
};

struct QuadTreeNode {
    Rect bounds;
    Point centre;
    Object * staticObjects;
    Object * movableObjects;
    QuadTreeNode * parent; // null if root
    QuadTreeNode * children[4]; // null for unallocated children
};

Kami menyimpan objek bergerak secara terpisah karena kami tidak perlu memeriksa apakah objek statis akan bertabrakan dengan apa pun.

Kami memodelkan semua objek sebagai kotak pembatas sumbu-selaras (AABB) dan kami menempatkannya di QuadTreeNode terkecil yang berisi mereka. Ketika QuadTreeNode banyak anak, Anda dapat membaginya lebih lanjut (jika benda-benda itu mendistribusikan diri ke anak-anak dengan baik).

Setiap permainan mencentang, Anda harus berulang ke quadtree dan menghitung gerakan - dan tabrakan - dari setiap objek bergerak. Itu harus diperiksa untuk tabrakan dengan:

  • setiap objek statis di simpulnya
  • setiap objek bergerak di simpulnya sebelum (atau setelah itu; cukup pilih arah) di daftar movableObjects
  • setiap objek bergerak dan statis di semua node induk

Ini akan menghasilkan semua kemungkinan tabrakan, tidak berurutan. Anda kemudian melakukan gerakan. Anda harus memprioritaskan gerakan ini berdasarkan jarak dan 'siapa yang bergerak terlebih dahulu' (yang merupakan persyaratan khusus Anda), dan melaksanakannya dalam urutan itu. Gunakan heap untuk ini.

Anda dapat mengoptimalkan templat quadtree ini; Anda tidak perlu benar-benar menyimpan batas dan titik tengah; itu sepenuhnya turunan saat Anda berjalan di pohon. Anda tidak perlu memeriksa apakah model berada dalam batas, hanya periksa sisi mana yang merupakan titik pusat (tes "sumbu pemisahan").

Untuk memodelkan hal-hal yang terbang cepat seperti proyektil, alih-alih memindahkannya setiap langkah atau memiliki daftar 'peluru' terpisah yang selalu Anda periksa, cukup letakkan mereka di quadtree dengan persegi penerbangan mereka untuk beberapa langkah permainan. Ini berarti bahwa mereka bergerak di quadtree jauh lebih jarang, tetapi Anda tidak memeriksa peluru terhadap dinding yang jauh, jadi itu tradeoff yang bagus.

Objek statis besar harus dipecah menjadi bagian-bagian komponen; sebuah kubus besar harus menyimpan masing-masing wajah secara terpisah, misalnya.

Akan
sumber
"Lukisan" terdengar bagus, saya akan mencobanya dan melaporkan hasilnya sesegera mungkin. Saya tidak mengerti bagian kedua dari jawaban Anda - saya akan mencoba membaca sesuatu tentang pre-fetching.
Vittorio Romeo
Saya tidak akan merekomendasikan QuadTree, ini lebih rumit daripada melakukan kisi-kisi, dan jika tidak dilakukan dengan benar itu tidak akan bekerja secara akurat dan akan membuat / menghapus node terlalu sering.
ClickerMonkey
Tentang tumpukan Anda: apakah urutan gerakan dihormati? Pertimbangkan tubuh A dan tubuh B . A bergerak ke kanan menuju B, dan dan B bergerak ke kanan menuju A. Sekarang - ketika mereka bertabrakan secara bersamaan, yang bergerak pertama harus diselesaikan terlebih dahulu , dan yang lainnya tidak akan terpengaruh.
Vittorio Romeo
@VittorioRomeo jika A bergerak ke arah B dan B bergerak ke arah A dalam centang yang sama, dan pada kecepatan yang sama, apakah mereka bertemu di tengah? Atau apakah A, bergerak lebih dulu, bertemu B di mana B mulai?
Will
1
@Will youtube.com/watch?v=EExHVi8NMzA
Vittorio Romeo
3

Saya yakin Anda hanya memiliki satu ton cache yang meleset saat iterasi di atas tubuh. Apakah Anda menggabungkan semua tubuh Anda bersama-sama menggunakan skema desain berorientasi data? Dengan broadphase N ^ 2 saya dapat mensimulasikan ratusan dan ratusan , sambil merekam dengan fraps, tubuh tanpa tetes framerate ke daerah bawah (kurang dari 60), dan ini semua tanpa pengalokasi kustom. Bayangkan saja apa yang bisa dilakukan dengan penggunaan cache yang tepat.

Petunjuknya ada di sini:

const std::vector<Body *>

Ini segera menimbulkan bendera merah besar. Apakah Anda mengalokasikan badan-badan ini dengan panggilan baru yang mentah? Apakah ada pengalokasi khusus yang digunakan? Yang paling penting adalah Anda memiliki semua tubuh Anda dalam susunan besar di mana Anda melintasi secara linier . Jika melintasi memori secara linear bukanlah sesuatu yang Anda rasa dapat Anda terapkan, pertimbangkan untuk menggunakan daftar yang terhubung secara intrinsif.

Selain itu Anda tampaknya menggunakan std :: map. Apakah Anda tahu bagaimana memori di dalam std :: map dialokasikan? Anda akan memiliki kompleksitas O (lg (N)) untuk setiap kueri peta, dan ini kemungkinan dapat ditingkatkan menjadi O (1) dengan tabel hash. Di atas semua ini, memori yang dialokasikan oleh std :: map juga akan merusak cache Anda.

Solusi saya adalah dengan menggunakan tabel hash intrusi di tempat std :: map. Sebuah contoh yang baik dari daftar yang saling terkait dan tabel hash yang mengganggu secara intrinsik ada di basis Patrick Wyatt dalam proyek coho-nya: https://github.com/webcoyote/coho

Jadi singkatnya, Anda mungkin perlu membuat beberapa alat khusus untuk diri sendiri, yaitu pengalokasi dan beberapa wadah yang mengganggu. Ini adalah yang terbaik yang bisa saya lakukan tanpa membuat profil kode sendiri.

RandyGaul
sumber
"Apakah Anda mengalokasikan badan-badan ini dengan panggilan baru yang mentah?" Saya tidak secara eksplisit menelepon newketika mendorong tubuh ke getBodiesToCheckvektor - maksud Anda ini terjadi secara internal? Apakah ada cara untuk mencegah itu sementara masih memiliki koleksi tubuh berukuran dinamis?
Vittorio Romeo
std::mapbukan hambatan - Saya juga ingat mencoba dense_hash_setdan tidak mendapatkan kinerja apa pun.
Vittorio Romeo
@ Vittorio, lalu bagian mana dari getBodiesToCheck yang menjadi penghambat? Kami memerlukan informasi untuk membantu.
Maik Semder
@MaikSemder: profiler tidak masuk lebih dalam dari fungsi itu sendiri. Seluruh fungsi adalah hambatan, karena disebut sekali per frame per tubuh. 10.000 badan = 10.000 getBodiesToCheckpanggilan per bingkai. Saya menduga pembersihan / dorongan yang konstan dalam vektor adalah hambatan dari fungsi itu sendiri. The containsMetode ini juga merupakan bagian dari perlambatan, tapi karena bodiesToChecktidak pernah memiliki lebih dari 8-10 mayat di dalamnya, itu harus yang lambat
Vittorio Romeo
@Vittorio akan lebih baik jika Anda memasukkan info ini ke pertanyaan, itu adalah game-changer;) Terutama yang saya maksud adalah bagian yang getBodiesToCheck dipanggil untuk semua orang , jadi 10.000 kali setiap frame. Saya ingin tahu, Anda mengatakan mereka dalam kelompok, jadi mengapa memasukkan mereka ke dalam array bodyToCheck, jika Anda sudah memiliki informasi grup. Anda mungkin menguraikan bagian itu, sepertinya kandidat optimasi yang sangat baik bagi saya.
Maik Semder
1

Kurangi jumlah mayat untuk memeriksa setiap frame:

Hanya periksa badan yang benar-benar bisa bergerak. Objek statis hanya perlu ditugaskan ke sel tumbukan Anda sekali setelah dibuat. Sekarang hanya periksa tabrakan untuk grup yang berisi setidaknya satu objek dinamis. Ini harus mengurangi jumlah cek setiap frame.

Gunakan quadtree. Lihat jawaban terperinci saya di sini

Hapus semua alokasi dari kode fisika Anda. Anda mungkin ingin menggunakan profiler untuk ini. Tapi saya hanya menganalisis alokasi memori dalam C #, jadi saya tidak bisa membantu dengan C ++.

Semoga berhasil!

Stephen
sumber
0

Saya melihat dua kandidat masalah dalam fungsi bottleneck Anda:

Pertama adalah bagian "berisi" - ini mungkin merupakan alasan utama kemacetan. Itu iterasi melalui tubuh yang sudah ditemukan untuk setiap tubuh. Mungkin Anda sebaiknya menggunakan beberapa jenis hash_table / hash_map daripada vektor. Maka memasukkan harus lebih cepat (dengan mencari duplikasi). Tetapi saya tidak tahu angka spesifik - saya tidak tahu berapa banyak mayat yang diulang di sini.

Masalah kedua bisa berupa vektor :: clear dan push_back. Jelas mungkin atau mungkin tidak membangkitkan realokasi. Tetapi Anda mungkin ingin menghindarinya. Solusi bisa berupa beberapa flags array. Tetapi Anda mungkin memiliki banyak objek, jadi ingatannya tidak efektif untuk memiliki daftar semua objek untuk setiap objek. Beberapa pendekatan lain mungkin baik, tetapi saya tidak tahu pendekatan apa: /

zacharmarz
sumber
Tentang masalah pertama: Saya sudah mencoba menggunakan dense_hash_set alih-alih berisi vektor +, dan itu lebih lambat. Saya mencoba mengisi vektor dan kemudian menghapus semua duplikat, dan itu lebih lambat.
Vittorio Romeo
0

Catatan: Saya tidak tahu apa-apa tentang C ++, hanya Java, tetapi Anda harus bisa mengetahui kodenya. Fisika adalah bahasa universal bukan? Saya juga menyadari ini adalah postingan yang berumur setahun, namun saya hanya ingin membagikan ini dengan semua orang.

Saya memiliki pola pengamat yang pada dasarnya, setelah entitas bergerak, ia mengembalikan objek yang telah bertabrakan, termasuk objek NULL. Sederhananya:

( Saya membuat ulang minecraft )

public Block collided(){
   return World.getBlock(getLocation());
}

Jadi katakanlah Anda berkeliaran di dunia Anda. setiap kali Anda memanggil move(1)Anda maka panggilan collided(). jika Anda mendapatkan blok yang Anda inginkan, maka mungkin partikel terbang dan Anda dapat bergerak ke kiri dan ke belakang tetapi tidak maju.

Menggunakan ini lebih umum daripada hanya minecraft sebagai contoh:

public Object collided(){
   return threeDarray[3][2][3];
}

Sederhananya, memiliki sebuah array untuk menunjukkan koordinat yang, secara harfiah bagaimana Java melakukannya, menggunakan pointer.

Dengan menggunakan metode ini masih membutuhkan sesuatu yang lain dari apriori metode deteksi tabrakan. Anda bisa mengulang ini, tapi itu mengalahkan tujuannya. Anda dapat menerapkan ini pada teknik tabrakan Luas, menengah, dan sempit, tetapi sendirian, ini adalah binatang buas terutama saat ia bekerja untuk game 3D dan 2D dengan cukup baik.

Sekarang dengan melihat sekali lagi, ini berarti bahwa, menurut metode minecraft collide () saya, saya akan berakhir di dalam blok, jadi saya harus memindahkan pemain di luarnya. Alih-alih memeriksa pemain, saya perlu menambahkan kotak pembatas yang memeriksa blok mana yang mengenai setiap sisi kotak. Masalah diperbaiki.

paragraf di atas mungkin tidak mudah dengan poligon jika Anda menginginkan keakuratan. Untuk keakuratan, saya akan menyarankan mendefinisikan kotak pembatas poligon yang bukan kotak, tetapi tidak tesselated. jika tidak, maka persegi panjang baik-baik saja.

AMDG
sumber