Fizik motorumda momentum ve güncelleme problemlerinin sırası


22

görüntü tanımını buraya girin

Bu soru, burada bulabileceğiniz çarpışma tespiti ve çözümü ile ilgili bir önceki sorumdan bir "takip" sorusudur .


Önceki soruyu okumak istemiyorsanız, işte fizik motorumun nasıl çalıştığı hakkında kısa bir açıklama:

Her fiziksel varlık SSSPBody adlı bir sınıfta depolanır.

Sadece AABB'ler desteklenir.

Her SSSPBody, her gövdeyi güncelleyen ve yerçekimini idare eden SSSPWorld adlı bir sınıfta saklanır.

Her kare, SSSPWorld her gövdeyi günceller.

Güncellenen her vücut, uzamsal bir karma değerindeki yakındaki gövdeleri arar, bunlarla çarpışma algılaması gerekip gerekmediğini kontrol eder. Eğer evet ise, bir "çarpışma" olayı başlatırlar ve onlarla çarpışmaları çözmeleri gerekip gerekmediğini kontrol ederler. Eğer evet ise, penetrasyon vektörünü ve yön örtüşmesini hesaplarlar, daha sonra penetrasyonu çözmek için pozisyonlarını değiştirirler.

Bir vücut bir başkasıyla çarpıştığında, sadece vücut hızını kendi ayarlayarak hızını diğerine aktarır.

Bir gövde, son kareden pozisyon değişmediği zaman 0'a ayarlanır. Ayrıca hareketli bir gövdeyle (asansör veya hareketli platformlar gibi) çarpışırsa, vücudun son konumundan hareket edip etmediğini görmek için asansörün hareket farkını hesaplar.

Ayrıca, bir vücut bütün AABB köşeleri çerçevedeki bir şeyle üst üste geldiğinde "ezilmiş" bir olayı başlatır.

Bu , oyunumun TAM kaynak kodu. Üç projeye ayrılmıştır. SFMLStart, varlıkların yönetimi, çizilmesi ve güncellenmesi için basit bir kütüphanedir. SFMLStartPhysics, SSSPBody ve SSSPWorld sınıflarının olduğu en önemli olanıdır. PlatformerPhysicsTest, tüm oyun mantığını içeren oyun projesidir.

Ve bu , SSSPBody sınıfındaki "güncelleme" metodudur, yorumlu ve basitleştirilmiştir. Bütün bunlara SFMLStartSimplePhysics projesine bakmak istemiyorsanız bakın. (Yapsanız bile, yorum yapıldığından beri buna bir göz atmalısınız.)


.Gif iki problem gösterir.

  1. Vücutlar farklı bir sıraya yerleştirilirse, farklı sonuçlar olur. Soldaki kasalar, sağdaki kasalarla aynıdır, sadece ters sırada (editörde) yerleştirilir.
  2. Her iki kasa da ekranın üst kısmına doğru itilmelidir. Soldaki durumda, hiçbir kasa itilmiyor. Sağ tarafta, bunlardan sadece biri. Her iki durum da istenmeyen.

İlk sorun: güncelleme sırası

Bu anlaşılması oldukça basittir. Soldaki durumda, en üstteki kasa diğerinden önce güncellenir. Alttaki kasa diğerine "aktarır" olsa bile, bir sonraki karenin hareket etmesini beklemesi gerekir. Hareket etmediğinden, taban kasasının hızı 0 olarak ayarlanmıştır.

Bunu nasıl düzelteceğimi bilemiyorum. Çözümün güncelleme listesini "sıralamaya" bağlı olmamasını tercih ederim, çünkü tüm fizik motoru tasarımında yanlış bir şey yaptığımı hissediyorum.

Büyük fizik motorları (Box2D, Bullet, Chipmunk) güncelleme sırasını nasıl ele alıyor?


İkinci sorun: tavana doğru yalnızca bir kasa itiliyor

Bunun neden olduğunu henüz anlamadım. "Yay" öğesinin yaptığı, vücudun hızını -4000'e ayarlamak ve yayın üzerine tekrar yerleştirmektir. Yeniden konumlandırma kodunu devre dışı bıraksam bile, sorun devam ediyor.

Benim fikrim, alt sandık üst sandıkla çarpıştığında, hızının 0 olarak ayarlanmış olmasıdır.


İlk sorundan vazgeçmiş birine benzeme şansına rağmen, yukarıdaki tüm proje kaynak kodunu yayınladım. Bunu kanıtlayacak bir şeyim yok, ama inan bana, bunu düzeltmek için çok çalıştım ama bir çözüm bulamadım ve fizik ve çarpışmalarla ilgili daha önce hiçbir tecrübem yok. Bu iki sorunu bir haftadan fazla bir süredir çözmeye çalışıyorum ve şimdi çaresizim.

Oyundan pek çok özellik çıkarmadan tek başına bir çözüm bulabileceğimi sanmıyorum (örneğin hız transferi ve yaylar).

Bu soruyu okumak için harcadığınız zaman için çok teşekkürler ve bir çözüm ya da bir öneriyle gelmeye çalışsanız bile, daha çok teşekkür ederiz.


Kutuları ne zaman istifleyecekseniz, fiziklerini birleştirip tek bir nesne olarak kabul eder misiniz?
CiscoIPPhone

Yanıtlar:


12

Aslında, güncelleme problemleri normal dürtü fizik motorları için oldukça yaygındır, Vigil'in önerdiği gibi kuvvet uygulanmasını geciktiremezsiniz, bir nesne aynı anda diğer 2 ile çarpıştığında enerjinin korunmasını kıracaksınız. Genellikle farklı bir güncelleme sırası önemli ölçüde farklı bir sonuç vermiş olsa da, oldukça gerçek gibi görünen bir şey yapmayı başarırlar.

Her durumda, amacınız için, kitle yaylı bir model inşa etmenizi önereceğim dürtü sisteminde yeterince hıçkırık var.

Temel fikir, çarpışmayı bir adımda çözmek yerine, çarpışan nesnelere kuvvet uygularsanız, bu kuvvet nesneler arasındaki örtüşme miktarına eşdeğer olmalıdır, bu çarpışma sırasındaki gerçek nesnelerin çarpışma sırasındaki dönüşümü ile karşılaştırılabilir. hareket enerjisinin deformasyona dönüşmesi ve sonra tekrar harekete geçmesi, bu sistemle ilgili en büyük şey, nesnenin ileri geri sıçramak zorunda kalmadan bir nesneden geçmesine izin vermesidir ve tamamen güncellemeden bağımsız olarak yapılabilir.

Nesnelerin süresiz olarak zıplamak yerine durmalarını sağlamak için, bir tür sönümleme uygulamanız gerekecek, nasıl yaptığınıza bağlı olarak oyununuzun tarzını ve hissini büyük ölçüde etkileyebilirsiniz, ancak çok temel bir yaklaşım, iç hareketlerine eşdeğer iki dokunma nesnesine bir kuvvet uygulayın, yalnızca birbirlerine doğru hareket ederken ya da birbirlerinden uzağa hareket ettiklerinde uygulamayı seçebilirsiniz, ikincisi nesnelerin tamamen geri dönmesini önlemek için kullanılabilir Onlar yere çarptıklarında, ama aynı zamanda onları biraz yapışkan yapacaktır.

Bir çarpışmanın dikey yönünde bir nesneyi frenleyerek de sürtünme etkisi yaratabilirsiniz, fren miktarı üst üste binme miktarına eşit olmalıdır.

Tüm nesnelerin aynı kütleye sahip olmasını sağlayarak kütle kavramını kolayca çözebilirsiniz ve hareket ettirilemez nesneler, yalnızca hızlandırmayı ihmal ederseniz, sonsuz kütlelere sahip gibi çalışacaktır.

Bazı sahte kod, sadece yukarıdakilerin yeterince açık olmaması durumunda:

//Presuming that you have done collision checks between two objects and now have  
//numbers for how much they overlap in each direction.
overlapX
overlapY
if(overlapX<overlapY){ //Do collision in direction X
    if(obj1.X>obj2.X){
        swap(obj1,obj2)
    }
    //Spring effect:
    obj1.addXvelocity-=overlapX*0.1 //Constant, the lower this is set the softer the  
                                    //collision will be.
    obj2.addXvelocity+=overlapX*0.1
    //Dampener effect:
    velocityDifference=obj2.Xvelocity-obj1.Xvelocity
    //velocityDifference=min(velocityDifference,0) //Uncomment to only dampen when  
                                                   //objects move towards each other.
    obj1.addXvelocity+=velocityDifference*0.1 //Constant, higher for more dampening.
    obj2.addXvelocity-=velocityDifference*0.1
    //Friction effect:
    if(obj1.Yvelocity>obj2.Yvelocity){
        swap(obj1,obj2)
    }
    friction=overlapX*0.01
    if(2*friction>obj2.Yvelocity-obj1.Yvelocity){
        obj1.addYvelocity+=(obj2.Yvelocity-obj1.Yvelocity)/2
        obj2.addYvelocity-=(obj2.Yvelocity-obj1.Yvelocity)/2
    }
    else{
        obj1.addYvelocity+=friction
        obj2.addYvelocity-=friction
    }
}
else{ //Do collision in direction Y

}

AddXvelocity ve addYvelocity özelliklerinin amacı, tüm çarpışma işlemlerinden sonra bunların nesnelerinin hızlarına eklenmeleridir.

Düzenleme:
Bir sonraki gerçekleştirilmeden önce her bir merminin tüm elemanlar üzerinde gerçekleştirilmesi gereken aşağıdaki sırayla şeyler yapabilirsiniz:

  • Çarpışmaları algılar, algılanır algılanmaz çözülebilirler.
  • AddVelocity değerlerini hız değerlerine ekleyin, yerçekimi Yvelocity ekleyin, addVelocity değerlerini 0'a sıfırlayın, nesneleri hızlarına göre hareket ettirin.
  • Sahneyi hazırla.

Ayrıca, ilk gönderimde aşağıdakilerin tamamen net olamayacağının farkındayım, yerçekimi nesnelerinin birbirinin üzerine otururken üst üste geleceği örtüşecek, bu çarpışma kutusunun üst üste binmekten kaçınmak için grafik gösterimlerinden biraz daha yüksek olması gerektiğini ortaya koyuyor görsel. Fizik daha yüksek bir güncelleme hızında çalıştırılırsa, bu sorun daha az olacaktır. CPU zamanı ve fizik doğruluğu arasında makul bir uzlaşma için 120Hz'de çalışmayı denemenizi öneriyorum.

Edit2:
Çok temel fizik motoru akışı:

  • Çarpışmalar ve yerçekimi kuvvet / ivme oluşturur. acceleration = [Complicated formulas]
  • Kuvvet / ivme hıza eklenir. velocity += acceleration
  • Konuma hız eklenir. position += velocity

İyi görünüyor, platformcular için kütle yayı hakkında hiç düşünmedim. Aydınlatıcı bir şey için Thumbs up :)
EnoughTea

Eve döndüğümde bunu birkaç saat içinde uygulamaya çalışacağım. Vücutları (Pozisyon + = Hız) aynı anda taşımalı mıyım, sonra çarpışmaları kontrol etmeli mi, yoksa birer birer çarpışmaları kontrol etmeli mi? [Ayrıca, çarpışmaları çözmek için konumunu manuel olarak değiştirmem gerekir mi? Yoksa hızını değiştirirken buna dikkat eder mi?]
Vittorio Romeo

İlk sorunuzu nasıl yorumlayacağınızdan tam olarak emin değilim. Çarpışma çözünürlüğü hızı değiştirecek ve bu nedenle sadece dolaylı olarak pozisyonu etkileyecektir.
aaaaaaaaaaaa

Gerçek şu ki, varlıkları hızlarını elle belirli bir değere ayarlayarak hareket ettiriyorum. Örtüşmeleri çözmek için, üst üste binme mesafelerini konumlarından kaldırırım. Yönteminizi kullanırsam, kuvvetleri veya başka bir şeyi kullanarak varlıkları taşımak zorunda mıyım? Bunu daha önce hiç yapmadım.
Vittorio Romeo

Teknik olarak, evet güç kullanmak zorunda kalacaksınız, kod parçamda, ağırlık 1 olan tüm nesneler ile biraz basitleştirildi ve kuvvet bu yüzden ivmeye eşit olmadı.
aaaaaaaaaaaa

14

Belli ki kolay kolay pes eden biri değilsin, sen gerçek bir demir adamsın, ellerimi daha önce havaya fırlatırdım, çünkü bu proje bir kelp ormanına güçlü bir benzerlik gösteriyor :)

Her şeyden önce, konumlar ve hızlar her yere yerleştirilir, fizik alt sistemi açısından bir felaket için bir reçetedir. Ayrıca, çeşitli alt sistemler tarafından integral şeyleri değiştirirken, "ChangeVelocityByPhysicsEngine", "ChangeVelocityBySpring", "LimitVelocity", "TransferVelocity" veya benzeri bir özel yöntem oluşturun. Mantığın belirli bir kısmı tarafından yapılan değişiklikleri kontrol etme kabiliyetini artıracak ve bu hız değişikliklerine ek bir anlam sağlayacaktır. Bu şekilde hata ayıklama daha kolay olurdu.

İlk sorun.

Sorunun kendisine. Şimdi sadece konum ve hız düzeltmelerini görünüm ve oyun mantığı sırasına göre "gittikçe" uyguladınız. Bu her karmaşık şeyin fiziğini dikkatlice kodlamadan karmaşık etkileşimler için işe yaramaz. O zaman ayrı bir fizik motoru gerekmiyor.

Kesintisiz karmaşık etkileşimler yapabilmek için, başlangıç ​​hızları ile değiştirilen pozisyonlara dayalı çarpışmaların algılanması ile "hızdan sonra" esaslı pozisyonların nihai değişiklikleri arasında ilave bir adım eklemeniz gerekir. Bunun böyle olacağını hayal ediyorum:

  • bedenlere etki eden tüm kuvvetleri kullanarak hızı bütünleştirin (doğrudan şimdi hız düzeltmeleri uyguluyorsunuz, fizik motorunuza hız hesaplamaları bırakıyor ve bunun yerine bir şeyleri taşımak için kuvvetler kullanıyorsunuz) , sonra pozisyonları entegre etmek için yeni hızı kullanın.
  • çarpışmaları algıladıktan sonra hızı ve konumları geri getirin,
  • daha sonra, çarpışmaları işleyin (hemen konum güncellemesi yapılmayan darbeleri kullanarak, ofc, sadece son aşamaya kadar hız değiştirilir)
  • yeni hızı tekrar bütünleştirin ve çarpışmaların elastik olmadığı durumlar dışında tüm çarpışmaları tekrar darbeleri kullanarak işleyin.
  • Elde edilen hızı kullanarak pozisyonların son entegrasyonunu yapabilir

Mastürbasyonla uğraşmak, FPS küçük olduğunda yığılmayı reddetmek veya buna benzer başka şeyler hazırlamak gibi ek şeyler ortaya çıkabilir. :)

İkinci problem

Bu "ölü ağırlık" kasalarının her ikisinin dikey hızı asla sıfırdan değişmez. Garip bir şekilde, PhysSpring'in Güncelleme döngüsünde hız atarsınız, ancak PhysCrate'in Güncelleme döngüsünde zaten sıfırdır. Hızın yanlış gittiği bir hat bulmak mümkündür, ancak "Ne Yığın Yığın" durumu olduğu için burada hata ayıklamayı bıraktım. Kodlamayı bırakmanın ve hata ayıklamanın zorlaştığı her şeyi yeniden düşünmeye başlamanın zamanı geldi. Ancak, kod yazarının kodda neler olduğunu anlaması için bile imkansız olduğu bir noktaya gelirseniz, kod tabanınız siz farkında olmadan çoktan ölmüştür :)

Üçüncü problem

Karo temelli bir platformcu yapmak için Farseer'in bir kısmını yeniden yaratmanız gerektiğinde bir şeylerin kapalı olduğunu düşünüyorum. Şahsen, şu anki motorunuzu muazzam bir deneyim olarak düşünürdüm ve daha basit ve basit çini bazlı fizik için onu tamamen çukurlaştırırım. Bunu yaparken, daha önce beklenmedik şeyleri yakalamak mümkün olacak çünkü Debug.Assert ve hatta belki de, ahh, ünite testleri gibi şeyleri seçmek akıllıca olacaktır.


Bu "yosun ormanı" karşılaştırmasını beğendim.
Den

Aslında, bu kelimeleri kullanmaktan biraz utanıyorum, ancak bir yeniden düzenleme ile sonuçlanacağını hissettim, o zaman haklı çıkacağını hissettim.
EnoughTea,

Sadece t testinde yapılan tek bir testte, bunun gerçekleşmesi her zaman mümkün olmaz mı? Hızları t'ye eklemeniz ve ardından 0'a herhangi bir hız ayarlamadan önce t + 1'deki çarpışmaları kontrol etmeniz gerektiğini hayal ediyorum ?
Jonathan Connell,

Evet, Runge-Kutta veya benzeri bir şey kullanarak başlangıçtaki durumu t'den t + dt'ye entegre ettikten sonra öndeki çarpışmaları tespit ediyoruz.
EnoughTea,

"bedenlere etki eden tüm kuvvetleri kullanarak hızı birleştir" "" fizik motoruna hız hesaplamaları bırak "- ne söylemeye çalıştığını anlıyorum, ama bunun nasıl yapılacağına dair hiçbir fikrim yok. Bana gösterebileceğiniz herhangi bir örnek / makale var mı?
Vittorio Romeo,

7

Bir vücut bir başkasıyla çarpıştığında, sadece vücut hızını kendi ayarlayarak hızını diğerine aktarır.

Sorununuz, bunların hareketle ilgili temelde yanlış varsayımlar olmalarıdır; bu nedenle aldığınız şey aşina olduğunuz harekete benzemez.

Bir vücut başka biriyle çarpıştığında, momentum korunur. Bunu "B vuruşu A" ya karşı "B vuruşu A" olarak düşünmek geçişsiz bir duruma geçişli bir fiil uygulamaktır. A ve B çarpışır; Elde edilen momentum ilk momentuma eşit olmalıdır. Yani, eğer A ve B eşit kütle ise, şimdi ikisi de orijinal hızlarının ortalaması ile seyahat ediyorlar.

Ayrıca muhtemelen bir çarpma eğimine ve yinelemeli bir çözücüye ihtiyacınız olacak veya stabilite sorunlarıyla karşılaşacaksınız. Muhtemelen Erin Catto'nun GDC sunumlarından bazılarını okumalısınız.


2
Çarpışma tamamen elastik değilse, örneğin A ve B hamur parçaları ise, orijinal hızın ortalamasını elde ederler.
Mikael Öhman

"sadece vücudun hızını kendi ayarlayarak". Neden işe yaramadığını aydınlatan böyle ifadeler. Genel olarak her zaman deneyimsiz kişilerin ilgili temel prensipleri anlamadan fizik sistemleri yazdıklarını buldum. Hiç 'sadece hız' ya da 'basitçe ...' yapmazsınız. Bir vücudun özelliklerinde yapılan her değişiklik, dinamik yasaların doğrudan bir uygulaması olmalıdır; momentumun, enerjinin vb. korunmasını da içerir. Evet, dengesizlikleri telafi etmek için her zaman geçiştirici faktörler olacaktır, ancak hiçbir zaman bir bedenin hızını sihirli bir şekilde değiştiremezsiniz.
MrCranky

İlk etapta bir motor çalıştırmaya çalışırken esnek olmayan gövdeleri varsaymak en kolayıdır, sorun çözme için daha az karmaşık olur.
Patrick Hughes,

4

Bence asil bir çaba gösterdin, ama kodun nasıl yapılandırıldığına dair temel problemler var gibi görünüyor. Diğerlerinin önerdiği gibi, operasyonları gizli parçalara ayırmak, örneğin:

  1. Geniş faz : Tüm nesneler arasında dolaşın - hangi nesnelerin çarpışacağını belirlemek için hızlı bir test yapın (örn. AABB) - olmayanları atın.
  2. Dar faz : Tüm çarpışan nesneler arasında döngü - çarpışma için bir penetrasyon vektörünü hesaplayın (örneğin, SAT kullanarak).
  3. Çarpışma tepkisi : Çarpışma vektörleri listesinde dolaşın - kütle bazında bir kuvvet vektörü hesaplayın, sonra bunu bir hızlanma vektörünü hesaplamak için kullanın.
  4. Entegrasyon : Tüm hızlanma vektörleri boyunca döngü yapın ve pozisyonu entegre edin (ve gerekirse döndürün).
  5. İşleme : Hesaplanan tüm konumların arasında dolaşın ve her nesneyi işleyin.

Aşamaları ayırarak, tüm nesneler senkronize olarak aşamalı olarak güncellenir ve şu anda uğraştığınız düzen bağımlılıklarına sahip olmazsınız. Kod ayrıca genellikle daha basit ve değiştirmesi daha kolay olur. Bu aşamaların her biri oldukça geneldir ve çalışan bir sisteme sahip olduktan sonra daha iyi algoritmalar kullanmak genellikle mümkündür.

Bununla birlikte, bu bölümlerin her biri kendi başına bir bilimdir ve en uygun çözümü bulmaya çalışırken çok fazla zaman alabilir. En sık kullanılan algoritmalardan bazılarıyla başlamak daha iyi olabilir:

  • Geniş faz çarpışma tespiti : Mekansal karma .
  • Dar faz çarpışma tespiti : Basit kiremit fiziği için, sadece Eksen Hizalanmış Sınırlama Kutusu (AABB) kavşak testlerini uygulayabilirsiniz. Daha karmaşık şekiller için Ayırma Ekseni Teoremini kullanabilirsiniz . Hangi algoritmayı kullanırsanız kullanın, iki nesne (penetrasyon vektörü denir) arasındaki kesişimin yönünü ve derinliğini döndürmelidir.
  • Çarpışma tepkisi : Penetrasyonları çözmek için Projeksiyon kullanın .
  • Entegrasyon : Entegratör , motorun stabilitesinin ve hızının en büyük belirleyicisidir. İki popüler seçenek Verlet (hızlı ama basit) veya RK4 (doğru ancak yavaş) entegrasyondur. Verlet entegrasyonunu kullanmak, çoğu fiziksel davranış (sıçrama, döndürme) çok fazla çaba harcamadan çalıştığı için son derece basit bir tasarıma yol açabilir. RK4 entegrasyonunu öğrenmek için gördüğüm en iyi referanslardan biri Glen Fiedler'in oyunlar için fizik konusundaki serisi .

Başlamak için iyi (ve açık) bir yer Newton'un hareket yasaları ile .


Yanıt için teşekkürler. Yine de, bedenler arasındaki hızı nasıl transfer edebilirim? Entegrasyon aşamasında mı oluyor?
Vittorio Romeo,

Bir anlamda, evet. Hız transferi çarpışma tepki aşaması ile başlar. İşte o zaman bedenlere etki eden kuvvetleri hesaplarsın. Kuvvet, formül ivmesi = kuvvet / kütle kullanılarak ivmeye çevrilir. Entegrasyon aşamasında hızlanma kullanılır, daha sonra pozisyonu hesaplamak için kullanılır. Entegrasyon fazının doğruluğu, hızın (ve ardından pozisyonun) zaman içinde ne kadar doğru değiştiğini belirler.
Luke Van
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.