Nesne grubuna göre siparişin önemli ve çarpışmanın koşullu olduğu bir çarpışma motorunu nasıl optimize edebilirim?


14

Bu soruya ilk kez geliyorsanız, önce aşağıdaki ön güncelleme bölümünü, ardından bu bölümü okumanızı öneririz. Yine de sorunun bir sentezi:

Temel olarak, çarpışma sırası ve çarpışma gruplarının önemli olduğu bir ızgara uzamsal bölümleme sistemine sahip bir çarpışma algılama ve çözünürlük motorum var. Her seferinde bir gövde hareket etmeli, sonra çarpışmayı tespit etmeli, ardından çarpışmaları çözmelidir. Tüm bedenleri bir kerede hareket ettirirsem, daha sonra olası çarpışma çiftleri üretirsem, bu daha hızlıdır, ancak çözünürlük kırılır, çünkü çarpışma sırasına uyulmaz. Bir gövdeyi bir seferde hareket ettirirsem, bedenleri çarpışmaları kontrol ettirmeye zorlarım ve bu bir ^ 2 problemi haline gelir. Grupları karışıma koyun ve birçok gövdede neden çok yavaş yavaş olduğunu hayal edebilirsiniz.


Güncelleme: Bu konuda çok çalıştım, ancak hiçbir şeyi optimize etmeyi başaramadım.

Will tarafından tarif edilen "resmi" başarıyla uyguladım ve grupları bit setlerine dönüştürdüm, ancak bu çok küçük bir hızlanma.

Ayrıca büyük bir sorun keşfettim : motorum çarpışma sırasına bağlı.

Her şeyi kesinlikle çok hızlandıran, ancak çarpışma sırasını bozan benzersiz çarpışma çifti nesli uygulamasını denedim .

Açıklamama izin ver:

  • Orijinal tasarımımda (çift oluşturmama), bu olur:

    1. tek bir beden hareket eder
    2. taşındıktan sonra hücrelerini yeniler ve çarpıştığı bedenleri alır
    3. eğer çözmesi gereken bir bedenle çakışıyorsa, çarpışmayı giderin

    bu, bir cismin hareket etmesi ve bir duvara (veya başka bir cisme) çarpması durumunda, sadece hareket eden cismin çarpışmasını çözeceği ve diğer cismin etkilenmeyeceği anlamına gelir.

    İstediğim davranış bu .

    Fizik motorları için yaygın olmadığını anlıyorum, ancak retro tarzı oyunlar için birçok avantajı var .

  • olağan ızgara tasarımında (benzersiz çiftler oluşturma), bu olur:

    1. tüm bedenler hareket eder
    2. tüm bedenler taşındıktan sonra tüm hücreleri yenile
    3. benzersiz çarpışma çiftleri üret
    4. her çift için, çarpışma algılama ve çözünürlük işlemek

    bu durumda, eşzamanlı bir hareket iki cismin üst üste binmesine neden olabilir ve aynı zamanda çözülürler - bu, cisimlerin etkili bir şekilde "birbirlerini itmesini" sağlar ve birden fazla cisimle çarpışma kararlılığını bozar

    Bu davranış fizik motorları için yaygındır, ancak benim durumumda kabul edilemez .

Ayrıca önemli olan başka bir sorun buldum (gerçek dünya durumunda olma ihtimali olmasa bile):

  • A, B ve W grubu organlarını düşünün
  • A çarpışır ve W ve A'ya karşı çözer
  • B çarpışır ve W ve B'ye karşı çözer
  • A, B'ye karşı hiçbir şey yapmaz
  • B, A'ya karşı hiçbir şey yapmaz

bir çok A gövdesi ve B gövdesinin aynı hücreyi işgal ettiği bir durum olabilir - bu durumda, bedenler arasında birbirine tepki vermemesi gereken (veya sadece çarpışmayı tespit eden ancak onları çözmeyen) çok fazla gereksiz yineleme vardır. .

Aynı hücreyi işgal eden 100 beden için, 100 ^ 100 yineleme! Bu, benzersiz çiftlerin üretilmemesi nedeniyle olur - ancak benzersiz çiftler oluşturamıyorum , aksi takdirde arzu etmediğim bir davranış elde ederim.

Bu tür çarpışma motorunu optimize etmenin bir yolu var mı?

Bunlar, uyulması gereken yönergelerdir:

  • Çarpışma sırası son derece önemlidir!

    • Oluşumu taşımanız gerekir birer birer , sonra çarpışmaları denetlemek teker teker ve çözmek hareketi sonrasında birer birer .
  • Gövdelerde 3 grup biti bulunmalıdır

    • Gruplar : vücudun ait olduğu gruplar
    • GroupsToCheck : vücudun çarpışmayı tespit etmesi gereken grupları
    • Çözülmeyen Çözümler : Vücudun çarpışmayı çözmemesi gereken grupları
    • Yalnızca bir çarpışmanın algılanmasını istediğim, ancak çözülmediği durumlar olabilir



Ön güncelleme:

Önsöz : Bu darboğazın optimize edilmesinin bir gereklilik olmadığının farkındayım - motor zaten çok hızlı. Bununla birlikte, eğlenceli ve eğitim amaçlı olarak, motoru daha da hızlı hale getirmenin bir yolunu bulmak isterim.


Genel amaçlı bir C ++ 2D çarpışma algılama / yanıt motoru oluşturuyorum, esnekliğe ve hıza önem veriyorum.

İşte mimarisinin çok temel bir şeması:

Temel motor mimarisi

Temel olarak, ana sınıf, Worlda ResolverBase*, a SpatialBase*ve a'nın sahibi (hafızayı yönetir) vector<Body*>.

SpatialBase geniş fazlı çarpışma tespitiyle uğraşan saf bir sanal sınıftır.

ResolverBase çarpışma çözünürlüğü ile ilgilenen saf bir sanal sınıftır.

Bedenler , kendilerine ait World::SpatialBase*olan SpatialInfonesnelerle iletişim kurarlar.


Currenly bir mekansal sınıf var: Grid : SpatialBasetemel bir sabit 2B ızgara. Kendi bilgi sınıfı var GridInfo : SpatialInfo.

Mimarisi şöyle görünüyor:

Izgara uzamlı motor mimarisi

GridSınıf bir 2D dizi sahiptir Cell*. CellSınıf bir koleksiyon (sahibi değil) içerir Body*: Bir vector<Body*>hücrede tüm organları içeren.

GridInfo nesneler ayrıca vücudun içinde bulunduğu hücrelere sahip olmayan işaretçiler içerir.


Daha önce söylediğim gibi, motor gruplara dayanıyor.

  • Body::getGroups()std::bitsetvücudun parçası olduğu tüm gruplardan birisini döndürür .
  • Body::getGroupsToCheck()std::bitsetvücudun çarpışmayı kontrol etmesi gereken tüm gruplardan birini döndürür .

Bedenler tek bir hücreden daha fazlasını işgal edebilir. GridInfo, işgal altındaki hücrelere her zaman sahip olmayan işaretçileri saklar.


Tek bir gövde hareket ettikten sonra çarpışma algılama gerçekleşir. Tüm cisimlerin eksene hizalanmış sınırlayıcı kutular olduğunu varsayıyorum.

Geniş fazlı çarpışma tespiti nasıl çalışır:

Bölüm 1: Konumsal Bilgi Güncellemesi

Her biri için Body body:

    • En soldaki en işgal edilen hücre ve en sağdaki en işgal edilen hücreler hesaplanır.
    • Önceki hücrelerden farklıysa body.gridInfo.cells, temizlenir ve vücudun kapladığı tüm hücrelerle doldurulursa (en soldaki hücreden en sağdaki hücreye kadar döngü için 2D).
  1. body artık hangi hücreleri kapladığını bilecek.

Bölüm 2: Gerçek Çarpışma Kontrolleri

Her biri için Body body:

  • body.gridInfo.handleCollisions denir:

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);
    }

  • Çarpışma daha sonra içindeki her beden için çözülür bodiesToResolve.

  • Bu kadar.


Bu geniş fazlı çarpışma tespitini bir süredir optimize etmeye çalışıyorum. Mevcut mimari / kurulumdan başka bir şey denediğimde, bir şey planlandığı gibi gitmez ya da daha sonra yanlış olduğu kanıtlanan simülasyon hakkında varsayımda bulunurum.

Sorum şu: Çarpışma motorumun geniş fazını nasıl optimize edebilirim ?

Burada uygulanabilecek bir çeşit sihirli C ++ optimizasyonu var mı?

Daha fazla performans elde etmek için mimari yeniden tasarlanabilir mi?


En son sürüm için callgrind çıktısı: http://txtup.co/rLJgz


Darboğazları profil haline getirin ve tanımlayın. Nerede olduklarını bize bildirin, üzerinde çalışacak bir şeyimiz var.
Maik Semder

@MaikSemder: Bunu yaptım ve yazıya yazdım. Darboğazın tek kod pasajı bu. Uzun ve ayrıntılıysa özür dilerim, ama bu sorunun bir parçası çünkü bu darboğazın sadece motor tasarımındaki bir şeyi değiştirerek çözülebileceğinden eminim.
Vittorio Romeo

Üzgünüm, bulmak zordu. Bize numara verebilir misiniz? İşlev zamanı ve bu işlevde işlenen nesne sayısı?
Maik Semder

@MaikSemder: Clang 3.4 SVN-O3: 10000 dinamik gövdeli derlenmiş bir ikili dosya üzerinde Callgrind ile test edildi - fonksiyon getBodiesToCheck()5462334 kez çağrıldı ve tüm profil oluşturma süresinin% 35,1'ini aldı (Talimat okuma erişim süresi)
Vittorio Romeo

2
@Quonux: suç alınmadı. Sadece "tekerleği yeniden icat etmeyi" seviyorum . Bullet veya Box2D'yi alıp bu kütüphanelerle oyun oynayabilirdim, ama bu benim amacım değil. Çok daha tatmin edici hissediyorum ve sıfırdan bir şeyler yaratarak ve ortaya çıkan engellerin üstesinden gelmeye çalışarak çok daha fazla şey öğreniyorum - bu sinirli olmak ve yardım istemek bile. Sıfırdan kodlamanın öğrenme amaçları için çok değerli olduğuna inancım dışında, aynı zamanda çok eğlenceli ve boş zamanımı geçirmek için büyük bir zevk buldum.
Vittorio Romeo

Yanıtlar:


14

getBodiesToCheck()

İşlevle ilgili iki sorun olabilir getBodiesToCheck(); ilk:

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

Bu kısım O (n 2 ) değil mi?

Vücudun listede olup olmadığını kontrol etmek yerine , resim kullanın.

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;

İşaretçiyi toplama aşamasında silme işlemini gerçekleştiriyorsunuz, ancak yine de test aşamasında silme işlemini kaldırmış olursunuz, bu yüzden yeterli L1'e sahipseniz, bu önemli değildir. __builtin_prefetchKlasik for(int i=q->length; i-->0; )döngüler ve benzeri durumlarda daha kolay olmasına rağmen , derleyiciye önceden getirme ipuçları ekleyerek performansı artırabilirsiniz .

Bu basit bir değişiklik, ama ikinci düşüncem bunu organize etmenin daha hızlı bir yolu olabilir:

Bunun yerine bitmap'leri kullanmaya ve tüm bodiesToCheckvektörden kaçınmaya geçebilirsiniz . İşte bir yaklaşım:

Zaten gövdeler için tamsayı anahtarları kullanıyorsunuz, ancak bunları haritalarda ve şeylerde arıyor ve bunların listelerini tutuyorsunuz. Temel olarak sadece bir dizi veya vektör olan bir yuva ayırıcıya geçebilirsiniz. Örneğin:

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

Bunun anlamı, gerçek çarpışmaları yapmak için gereken tüm şeylerin doğrusal önbellek dostu bellekte olmasıdır ve sadece uygulamaya özel bite gidersiniz ve bazı ihtiyaçlar varsa bu yuvalardan birine bağlarsınız.

Organlarının bu vektörle ayırmalarını izlemek için bir şekilde tamsayı dizisi kullanabilirsiniz bitmap ve kullanım bit twiddling ya __builtin_ffsBu, şu anda işgal yuvaları taşımak veya dizideki boş bir yuva bulmak için süper verimli vb. Hatta bazen mantıksız şekilde büyürse ve sonra boşlukları doldurmak için uçları hareket ettirerek lotlar silindi olarak işaretlenirse diziyi bile sıkıştırabilirsiniz.

her bir çarpışmayı sadece bir kez kontrol edin

Eğer kontrol ettiyseniz bir çarptığı b , sen olmadığını kontrol etmek gerekmez b çarpışır bir de.

Basit bir if-ifadesi ile bu kontrollerden kaçındığınız tamsayı kimlikleri kullanılır. Potansiyel bir çarpışmanın kimliği, denetlenmekte olan geçerli kimliğe eşit veya daha azsa, atlanabilir! Bu şekilde, her olası eşleştirmeyi yalnızca bir kez kontrol edersiniz; çarpışma kontrolü sayısının yarısından fazlasını alacaktır.

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;
  }

çarpışmaların sırasına saygı göstermek

Bir çift bulunur bulunmaz çarpışmayı değerlendirmek yerine, çarpma mesafesini hesaplayın ve bunu ikili bir yığın halinde saklayın . Bu yığınlar genellikle yol bulmada öncelik sıralarını nasıl yaptığınızdır, bu nedenle çok yararlı bir yardımcı program kodudur.

Her düğümü bir sıra numarası ile işaretleyin, böylece şunları söyleyebilirsiniz:

  • Bir 10 isabet B 12 6'da
  • Bir 10 isabet Cı 12 3'te

Açıkçası, tüm çarpışmaları topladıktan sonra, onları en kısa sürede öncelik sırasından atmaya başlarsınız. Yani ilk aldığınız A 10 isabet C 12'dir . Her nesnenin sıra numarasını ( 10 bit) arttırır, çarpışmayı değerlendirir ve yeni yollarını hesaplar ve yeni çarpışmalarını aynı kuyrukta depolarsınız. Yeni çarpışma, A 11'in 7'de B 12'ye çarpması . Kuyruk şu anda:

  • Bir 10 isabet B 12 6'da
  • Bir 11 isabet B 12 7 de

Sonra öncelik sırası ve A'dan pop 10 hit B 12 6. at Ama bir görüyoruz 10 olan bayat ; A şu anda 11 yaşında. Böylece bu çarpışmayı atabilirsiniz.

Ağaçtan tüm eski çarpışmaları silmeye çalışmaktan rahatsız etmemek önemlidir; yığından çıkarmak pahalıdır. Onları patlattığınızda onları atın.

ızgara

Bunun yerine bir dörtlü kullanmayı düşünmelisiniz. Uygulanması çok basit bir veri yapısıdır. Genellikle noktaları saklayan uygulamalar görürsünüz, ancak rektları saklamayı ve öğeyi onu içeren düğümde saklamayı tercih ederim. Bu, çarpışmaları kontrol etmek için yalnızca tüm gövdeler üzerinde yineleme yapmanız ve her biri için aynı dört ağaç düğümündeki (yukarıda özetlenen sıralama hilesini kullanarak) ve üst dört ağaç düğümlerindeki tüm gövdelere karşı kontrol etmeniz gerektiği anlamına gelir. Dört ağacın kendisi olası çarpışma listesidir.

İşte basit bir Quadtree:

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
};

Hareketli nesneleri ayrı ayrı saklarız çünkü statik nesnelerin herhangi bir şeyle çarpışıp çarpışmadığını kontrol etmek zorunda değiliz.

Tüm nesneleri eksen hizalı sınırlama kutuları (AABB) olarak modelliyoruz ve bunları içeren en küçük QuadTreeNode'a yerleştiriyoruz. Bir QuadTreeNode çok sayıda çocuk olduğunda, daha fazla alt bölümlere ayırabilirsiniz (bu nesneler kendilerini çocuklara güzelce dağıtırsa).

Her oyun kene, dörtlü geri çekilmesi ve her hareketli nesnenin hareketini ve çarpışmalarını hesaplamanız gerekir. Şunlar ile çarpışmalara karşı kontrol edilmelidir:

  • düğümündeki her statik nesne
  • movableObjects listesinde düğümündeki kendisinden önceki (veya sonraki; yalnızca bir yön seçin) her hareketli nesne
  • tüm üst düğümlerdeki her taşınabilir ve statik nesne

Bu, sırasız tüm olası çarpışmaları meydana getirecektir. Sonra hamle yaparsın. Bu hareketlere mesafeye ve 'önce hareket edenlere' (özel gereksiniminiz olan) göre öncelik vermeli ve bu sırayla gerçekleştirmelisiniz. Bunun için bir yığın kullanın.

Bu dörtlü şablonu optimize edebilirsiniz; sınırları ve merkez noktasını saklamanız gerekmez; ağaçta yürüdüğünüzde bu tamamen türetilebilir. Bir modelin sınırlar içinde olup olmadığını kontrol etmenize gerek yoktur, yalnızca merkez noktasının hangi tarafında olduğunu kontrol edin (bir "ayırma ekseni" testi).

Mermiler gibi hızlı uçan şeyleri modellemek için, her bir adımı hareket ettirmek veya her zaman kontrol ettiğiniz ayrı bir 'madde işareti' listesine sahip olmak yerine, onları birkaç oyun adımı için uçuşlarının rektresiyle dörtlü olarak yerleştirin. Bu, quadtree'de çok daha nadiren hareket ettikleri anlamına gelir, ancak duvarlardan uzaktaki mermileri kontrol etmiyorsunuz, bu yüzden iyi bir ödün.

Büyük statik nesneler bileşen parçalarına bölünmelidir; örneğin büyük bir küpün her yüzü ayrı ayrı saklanmalıdır.


"Resim" kulağa hoş geliyor, bunu bir deneyeceğim ve sonuçları en kısa zamanda rapor edeceğim. Yine de cevabınızın ikinci kısmını anlamıyorum - ön getirme hakkında bir şeyler okumaya çalışacağım.
Vittorio Romeo

QuadTree'yi tavsiye etmem, bir ızgara yapmaktan daha karmaşıktır ve düzgün yapılmazsa doğru çalışmaz ve düğümleri çok sık oluşturur / kaldırır.
ClickerMonkey

Yığınınız hakkında: hareket sırasına uyuluyor mu? A ve B vücutlarını ele alalım . Şimdi A'ya doğru sağa A'dan B'ye doğru sağa hareket eder ve ve B hamle - eş zamanlı çarpıştığında, tek ilk hareket almalısınız ilk çözüldü , diğeri etkilenmemiş olur.
Vittorio Romeo

@VittorioRomeo, A B'ye doğru hareket ederse ve B aynı kene ile A'ya doğru hareket ederse ve aynı hızda, ortada buluşurlar mı? Yoksa önce hareket eden A, B'nin başladığı yerde B ile buluşuyor mu?
Will


3

Bahse girerim cesetler üzerinde yineleme yaparken sadece bir ton önbellek özlüyor. Bazı veri odaklı tasarım şemasını kullanarak tüm bedenlerinizi bir araya getiriyor musunuz? Bir N ^ 2 geniş fazı ile, yüzlerce ve yüzlerce , fraplarla kayıt yaparken, diğer bölgelere (60'tan az) herhangi bir kare damlası olmayan gövdeleri taklit edebilirim ve hepsi özel bir ayırıcı olmadan. Sadece uygun önbellek kullanımı ile neler yapılabileceğini hayal edin.

İpucu burada:

const std::vector<Body *>

Bu hemen büyük bir kırmızı bayrak kaldırır. Bu organlara yeni ham çağrılar mı veriyorsunuz? Kullanımda özel bir ayırıcı var mı? Tüm bedenlerinizin doğrusal olarak ilerlediğiniz devasa bir dizide olması çok önemlidir. . Belleği doğrusal olarak gezdirmek, uygulayabileceğinizi düşündüğünüz bir şey değilse, bunun yerine müdahaleci bir bağlantı listesi kullanmayı düşünün.

Ayrıca std :: map kullanıyor gibi görünüyorsunuz. Std :: map içindeki hafızanın nasıl tahsis edildiğini biliyor musunuz? Her bir harita sorgusu için O (lg (N)) karmaşıklığına sahip olursunuz ve bu bir karma tablosu ile muhtemelen O (1) 'e yükseltilebilir. Bunun da ötesinde, std :: map tarafından ayrılan bellek önbelleğinizi de büyük ölçüde yıkacak.

Benim çözüm std :: map yerine müdahaleci bir karma tablo kullanmaktır. Hem müdahaleci bir şekilde bağlantılı listelere hem de müdahaleci karma tablolarına iyi bir örnek, Coho projesi içindeki Patrick Wyatt'ın tabanındadır: https://github.com/webcoyote/coho

Kısacası, muhtemelen kendiniz için bir allocator ve bazı müdahaleci kaplar gibi bazı özel araçlar oluşturmanız gerekecektir. Bu, kodu kendim için profillemeden yapabileceğim en iyisidir.


"Bu organlara yeni ham çağrılar mı veriyorsunuz?" newVücudları getBodiesToCheckvektöre iterken açıkça aramıyorum - bu dahili olarak mı oluyor demek? Dinamik boyutlu bir beden koleksiyonuna sahipken bunu önlemenin bir yolu var mı?
Vittorio Romeo

std::mapbir darboğaz değil - aynı zamanda dense_hash_setherhangi bir performans kazanmaya çalıştığımı da hatırlıyorum .
Vittorio Romeo

@Vittorio ardından hangi bölümünün getBodiesToCheck darboğaz nedir? Yardım etmek için bilgiye ihtiyacımız var.
Maik Semder

@MaikSemder: Profil oluşturucu işlevin kendisinden daha derine inmez. Tüm işlev darboğazdır, çünkü gövde başına çerçeve başına bir kez çağrılır. 10000 gövde = getBodiesToCheckçerçeve başına 10000 çağrı. Vektörde sürekli temizlik / itme fonksiyonunun kendisinin darboğazı olduğundan şüpheleniyorum. containsYöntem ayrıca yavaşlama parçası olmakla birlikte, beri bodiesToCheckOrada fazla 8-10 organları vardır asla, o kadar yavaş olmalıdır
Vittorio Romeo

@Vittorio bu bilgileri sorulara koyarsanız iyi olur, bu bir oyun değiştirici;) Özellikle getBodiesToCheck'in tüm bedenler için çağrıldığı kısmı , yani her bir karenin 10000 katı olduğunu kastediyorum . Acaba, grup halinde olduklarını söyledin, öyleyse neden grup bilgilerine sahipseniz onları neden bodyToCheck dizisine koydunuz? Bu kısımda biraz ayrıntı verebilir, bana çok iyi bir optimizasyon adayı gibi gelebilir.
Maik Semder

1

Her bir kareyi kontrol etmek için gövde sayısını azaltın:

Sadece gerçekten hareket edebilen gövdeleri kontrol edin. Statik nesneler, oluşturulduktan sonra çarpışma hücrelerine yalnızca bir kez atanmalıdır. Şimdi sadece en az bir dinamik nesne içeren gruplar için çarpışmaları kontrol edin. Bu, her bir karedeki kontrol sayısını azaltmalıdır.

Dörtlü kullanın. Ayrıntılı cevabımı burada görün

Fizik kodunuzdaki tüm tahsisleri kaldırın. Bunun için bir profil oluşturucu kullanmak isteyebilirsiniz. Ama ben sadece C # bellek ayırma analiz ettim, bu yüzden C ++ ile yardımcı olamaz.

İyi şanslar!


0

Darboğaz fonksiyonunuzda iki problem adayı görüyorum:

Birincisi "içerir" kısmı - bu muhtemelen darboğazın ana sebebidir. Her beden için zaten bulunan bedenler aracılığıyla tekrar ediyor. Belki vektör yerine bir tür hash_table / hash_map kullanmalısınız. Daha sonra ekleme işlemi daha hızlı olmalıdır (yinelemelerin aranmasıyla). Ama belirli bir sayı bilmiyorum - burada kaç ceset yinelendiğine dair hiçbir fikrim yok.

İkinci sorun vector :: clear ve push_back olabilir. Açık yeniden tahsisi uyandırabilir veya uyandırmayabilir. Ama bundan kaçınmak isteyebilirsiniz. Çözüm bazı bayraklar dizisi olabilir. Ancak muhtemelen çok fazla nesneniz olabilir, bu nedenle her nesne için tüm nesnelerin listesine sahip olmak bellek etkisizdir. Başka bir yaklaşım güzel olabilir, ama hangi yaklaşımı bilmiyorum: /


İlk sorun hakkında: vector + içeren yerine bir dense_hash_set kullanmayı denedim ve daha yavaştı. Vektörü doldurmayı ve sonra tüm kopyaları kaldırmayı denedim ve daha yavaştı.
Vittorio Romeo

0

Not: C ++ hakkında hiçbir şey bilmiyorum, sadece Java, ancak kodu anlayabilmelisiniz. Fizik evrensel dil değil mi? Ayrıca bunun eski bir yazı olduğunu da fark ettim, ama bunu herkesle paylaşmak istedim.

Temelde varlık hareket sonra NULL nesne de dahil olmak üzere çarpıştığı nesneyi döndüren bir gözlemci desen var. Basit ifadeyle:

( Minecraft'ı yeniden yapıyorum )

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

Diyelim ki dünyanızda dolaşıyorsunuz. ne zaman ararsan move(1)o zaman ara collided(). İstediğiniz bloğu alırsanız, belki de parçacıklar uçar ve sağa ve sola hareket edebilirsiniz, ancak ileri doğru değil.

Bunu örnek olarak sadece minecraft'tan daha genel olarak kullanmak:

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

Basitçe, Java'nın yaptığı gibi işaretçileri kullanan koordinatları gösteren bir dizi oluşturun.

Bu yöntemin kullanılması yine de a priori çarpışma tespit yönteminden başka bir şey gerektirir . Bunu döngüye sokabilirsiniz, ama bu amacı yendi. Bunu Geniş, orta ve dar çarpışma tekniklerine uygulayabilirsiniz, ancak tek başına, özellikle 3D ve 2D oyunlar için oldukça iyi olduğunda bir canavar.

Şimdi bir kez daha baktığımızda, bu, minecraft collide () yöntemime göre, bloğun içine gireceğim, bu yüzden oyuncuyu bunun dışına taşımak zorunda kalacağım anlamına geliyor. Oynatıcıyı kontrol etmek yerine, kutunun her iki tarafına hangi bloğun vurduğunu kontrol eden bir sınırlayıcı kutu eklemem gerekiyor. Sorun çözüldü.

doğruluk istiyorsanız yukarıdaki paragraf çokgenler ile o kadar kolay olmayabilir. Doğruluk için, kare olmayan, ancak renklendirilmemiş çokgen sınırlayıcı bir kutu tanımlamanızı öneririm. değilse, bir dikdörtgen gayet iyi.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.