2D fizik motorunda bir sarma teli (Worms Ninja Halat gibi) uygulama


34

Son zamanlarda bazı ip fiziği deniyordum ve "standart" çözümün - yaylar veya eklemlerle birlikte dizilmiş bir dizi nesneden bir ip yapmanın - tatmin edici olmadığını gördüm. Özellikle ip sallanması oyunla ilgili olduğunda. Bir ipin sarılma veya sarkma kabiliyetini gerçekten umursamıyorum (bu yine de görseller için sahte olabilir).

Oynanış için önemli olan, ipin çevreye dolanma ve ardından çözülme yeteneğidir. İp gibi davranması bile gerekmez - düz çizgi parçalarından oluşan bir "tel". İşte bir örnek:

Ninja ipi, engellerin etrafından dolanıyor

Bu, Worms oyunundaki "Ninja Rope" a çok benziyor.

Çünkü bir 2B fizik motoru kullanıyorum - ortamım 2B dışbükey çokgenlerden oluşuyor. (Özellikle Farseer'da SAT kullanıyorum.)

Öyleyse benim sorum şu: “sarma” etkisini nasıl uygularsınız?

Kablonun, "ayrılan" ve "birleşen" bir dizi çizgi parçasından oluşacağı açıktır. Ve hareketli nesnenin tutturulduğu çizginin son (aktif) bölümü sabit uzunlukta bir bağlantı olacaktır.

Fakat aktif çizgi segmentinin ne zaman ve nerede bölünmesi gerektiğini belirlemek için gereken matematik / algoritma nedir? Ve bir önceki bölüme ne zaman katılması gerekiyor?

(Önceden bu soru ayrıca bunu dinamik bir ortam için yapmayı da istedi - Bunu diğer sorulara bölmeye karar verdim.)

Yanıtlar:


18

İpi ne zaman ayıracağınızı belirlemek için, ipin her çerçeveyi kapladığı alana bakmalısınız. Yaptığınız şey, kapalı alan ve seviye geometrinizle çarpışma kontrolü yapmanızdır. Bir salıncak kaplayan alan bir yay olmalıdır. Bir çarpışma varsa, ipe yeni bir parça yapmanız gerekir. Sallanan yayla çarpışan köşeleri kontrol edin. Salınım yayı ile çarpışan çoklu köşeler varsa, önceki çerçeve ile halat arasındaki açının en küçük olduğu noktadan birini seçmelisiniz.

Helpful diagram of the ninja rope situation

Çarpışma algılamayı yapma biçiminiz, geçerli ip bölümünün kökü, O, önceki karedeki ipin son konumu, A, mevcut karedeki ipin son konumu, B ve çok köşeli her bir P köşesidir. seviye geometrisi, (OA x OP), (OP x OB) ve (OA x OB) değerini hesaplar, burada "x", iki vektör arasındaki çapraz ürünün Z koordinatını almayı gösterir. Üç sonucun tümü aynı işarete sahipse, negatif veya pozitif ve OP'nin uzunluğu, ip parçasının uzunluğundan daha küçükse, P noktası, salıncak tarafından kaplanan alan içindedir ve ipi ayırmanız gerekir. Birden fazla çarpışan köşe noktasına sahipseniz, OA ile OP arasındaki açının en küçük olduğu anlamına gelen, ipi vuran ilkini kullanmak isteyeceksiniz. Açıyı belirlemek için nokta ürünü kullanın.

Segmentleri birleştirmek için, önceki segment ile mevcut segmentinizin yayı arasında bir karşılaştırma yapın. Geçerli bölüm soldan sağa doğru ya da tam tersi yöndeyse, bölümlere katılmalısınız.

Segmentleri birleştirmek için kullanılan matematik için, önceki halat bölümünün (Q) bağlantı noktasını ve aynı zamanda bölme durumundakileri kullanacağız. Şimdi, QO, OA ve OB vektörlerini karşılaştırmak isteyeceksiniz. (QO x OA) işareti (QO x OB) işaretinden farklıysa, halat soldan sağa veya tam tersi yönde geçti ve bölümlere katılmalısınız. Bu elbette, ip 180 derece sallanırsa da olabilir, bu yüzden ipin poligonal bir şekil yerine uzayda tek bir noktaya sarılmasını istiyorsanız, bunun için özel bir durum eklemek isteyebilirsiniz.

Elbette bu yöntem, ipten sarkan nesne için çarpışma algılaması yaptığınızı ve böylece seviye geometrisinin içinde bitmeyeceğini varsaymaktadır.


1
Bu yaklaşımdaki sorun, kayan nokta hassasiyet hatalarının ipin bir noktadan "geçmesine" olanak sağlamasıdır.
Andrew Russell

16

Solucanlar oynadığımdan bu yana bir süre geçti, ama hatırladığım kadarıyla - ip bir şeyleri sardığında, ipin herhangi bir zamanda hareket eden sadece bir (düz) kısmı var. İpin geri kalanı statik hale gelir

Bu yüzden çok az gerçek fizik var. Aktif bölüm, ucunda bir kütle bulunan tek bir sert yay olarak modellenebilir

İlginç olan, ipin aktif olmayan bölümlerini aktif bölüme / bölümünden ayırma / birleştirme mantığı olacaktır.

2 ana işlem olacağını hayal ediyorum:

'Bölünmüş' - İp bir şeyle kesişti. Kavşakta aktif olmayan bir bölüme ve yeni, daha kısa, aktif bölüme ayırın

'Birleştir' - Aktif halat, en yakın kavşağın artık bulunmadığı bir konuma taşındı (bu sadece basit bir açı / nokta ürün testi olabilir mi?). Yeni, daha uzun ve etkin bir bölüm oluşturarak 2 bölüme yeniden katılın

2B poligonlardan yapılmış bir sahnede, tüm ayrık noktalar, çarpışma ağının tepe noktasında olmalıdır. Çarpışma tespiti, 'Bu güncelleme sırasında ip bir köşeden geçerse, ipi bu köşede ayırıp birleştirin mi?


2
Bu adam tam yerindeydi ... Aslında, "sert" bir bahar değildir, sadece etrafta düz bir çizgi döndürürsünüz ...
speeder

Cevabınız teknik olarak doğru. Ancak, çizgi bölümlerine sahip olmanın ve bunları bölmenin ve birleştirmenin açık olduğunu kabul ediyorum. Bunu yapmak için gerçek algoritma / matematik ile ilgileniyorum. Sorumu daha belirgin hale getirdim.
Andrew Russell

3

Gusanos'taki ninja ipinin nasıl uygulandığını inceleyin:

  • İp bir şeye bağlanıncaya kadar bir parçacık gibi davranır.
  • Bir kez takıldığında, ip sadece solucanın üzerine bir kuvvet uygular.
  • Dinamik nesnelere (diğer solucanlar gibi) eklemek hala bir TODO: bu kodda.
  • Nesnelerin / köşelerin etrafını sarmanın desteklenip desteklenmediğini hatırlayamıyorum ...

Ninjarope.cpp'den alakalı alıntı :


void NinjaRope::think()
{
    if ( m_length > game.options.ninja_rope_maxLength )
        m_length = game.options.ninja_rope_maxLength;

    if (!active)
        return;

    if ( justCreated && m_type->creation )
    {
        m_type->creation->run(this);
        justCreated = false;
    }

    for ( int i = 0; !deleteMe && i < m_type->repeat; ++i)
    {
        pos += spd;

        BaseVec<long> ipos(pos);

        // TODO: Try to attach to worms/objects

        Vec diff(m_worm->pos, pos);
        float curLen = diff.length();
        Vec force(diff * game.options.ninja_rope_pullForce);

        if(!game.level.getMaterial( ipos.x, ipos.y ).particle_pass)
        {
            if(!attached)
            {
                m_length = 450.f / 16.f - 1.0f;
                attached = true;
                spd.zero();
                if ( m_type->groundCollision  )
                    m_type->groundCollision->run(this);
            }
        }
        else
            attached = false;

        if(attached)
        {
            if(curLen > m_length)
            {
                m_worm->addSpeed(force / curLen);
            }
        }
        else
        {
            spd.y += m_type->gravity;

            if(curLen > m_length)
            {
                spd -= force / curLen;
            }
        }
    }
}

1
Uhmn ... bu sorumu hiç cevaplamıyor gibi görünüyor. Sorumun tüm amacı, çokgenlerden oluşan bir dünyanın etrafına ip sarmak. Gusanos'un sarma ve bitmap dünyası yok gibi görünüyor.
Andrew Russell

1

Korkarım kafamın tepesinden somut bir algoritma veremem, ama bana göre ninja halatı için bir çarpışmayı tespit etmek için önemli olan sadece iki şey var: engeller üzerindeki potansiyel olarak çarpışan köşeler segmentin kalan uzunluğuna eşit olan son "ayrık" yarıçapı içinde; ve geçerli dönme yönü (saat yönünde veya saatin tersi yönünde). "Bölünmüş" tepe noktasından yakındaki tepe noktalarının her birine geçici bir açılar listesi oluşturduysanız, algoritmanızın, segmentinizin herhangi bir tepe noktası için bu açıyı geçip geçmeyeceğini düşünmesi gerekir. Eğer öyleyse, o zaman pasta kadar kolay bir bölme işlemi yapmanız gerekir - Bu sadece son bölme köşesinden yeni bölmeye bir çizgidir ve sonra yeni bir geri kalan hesaplanır.

Bence sadece köşeler önemli. Bir engel üzerindeki köşeler arasında bir bölüme vurma tehlikesi altındaysanız, ipin ucunda asılı olan adam için normal çarpışma algılamanız tekmelemelidir. Başka bir deyişle, ipiniz yalnızca "sıkı" Yine de köşeler, bu yüzden arasındaki segment önemli değil.

Üzgünüm somut bir şeyim yok, ama umarım bu gerçekleşmesi için kavramsal olarak olmanız gereken yere ulaşır. :)


1

Benzer simülasyonlarla ilgili makalelere bağlantıları olan bir yazı (oyunlarda değil mühendislik / akademik bağlamlarda): https://gamedev.stackexchange.com/a/10350/6398

Bu tür "tel" simülasyonu için (Umihara Kawase oyununda görüldüğü gibi) çarpışma algılamasına + yanıtına en az iki farklı yaklaşım denedim; en azından sanırım bunun peşinde olduğunuzu düşünüyorum - bu tür bir simülasyon için belirli bir terim yok gibi görünüyor, ben sadece "halat" yerine "tel" demeye meyilliyim çünkü çoğu insan gibi görünüyor “ip” i “parçacık zinciri” ile eşanlamlı olarak kabul edin. Ve, ninja ipinin yapışma davranışını istiyorsanız (yani iterek ve çekerek), bu bir ipten çok sert bir tel gibidir. Neyse ..

Pekuja'nın cevabı iyidir, üç noktanın işaretli alanının 0 olduğu zamanları çözerek sürekli çarpışma tespitini uygulayabilirsiniz.

(OTOH'i tam olarak hatırlayamıyorum ancak aşağıdaki gibi yaklaşabilirsiniz: a noktasının b, c'den geçen çizgide bulunduğunda t zamanını bulun (sanırım bunu nokta (ab, cb) = T) değerlerini bulmak için 0 ve ardından geçerli bir süre 0 <= t <1 olduğunda, bc segmentindeki a parametrik pozisyonunu bulun, yani a = (1-s) b + s c ve eğer a b ve c (yani eğer 0 <= s <= 1 ise) geçerli bir çarpışma.

AFAICR, buna diğer tarafa da yaklaşabilirsiniz (yani, s'yi çözün ve daha sonra t'yi bulmak için bunu takın), ancak çok daha az sezgiseldir. (Üzgünüm, bu bir anlam ifade etmiyorsa, notlarımı kazmaya vaktim yok ve birkaç yıl oldu!)

Böylece, olayların gerçekleştiği zamanları hesaplayabilirsiniz (örn. İp düğümlerinin takılması veya çıkarılması gerekir); en eski olayı işle (bir düğüm ekle ya da kaldır) ve ardından t = 0 ve t = 1 arasında başka bir olay kalmayıncaya kadar tekrarlayın / tekrarlayın.

Bu yaklaşımla ilgili bir uyarı: eğer ipin etrafına dolanabilecek nesneler dinamikse (özellikle onları simüle ediyorsanız ve bunların ip üzerindeki etkilerini ve bunun tersini yaparsanız), o zaman bu nesnelerin her birinin klipslenip / geçtiği durumlarda sorunlar olabilir diğer - tel karışık olabilir. Box2d tarzı bir fizik simülasyonunda bu tür bir etkileşimin / hareketin (birbirinin içinden kayan nesnelerin köşeleri) bu şekilde engellenmesi kesinlikle zor olacaktır. Bu bağlamda nesneler arasında az miktarda sızma olması normal davranıştır.

(En azından .. bu benim "wire" uygulamalarımdan biriyle ilgili bir problemdi.)

Çok daha istikrarlı olan ancak belirli şartlarda bazı çarpışmaları kaçıran farklı bir çözüm, sadece statik testler kullanmaktır (yani zamanla sipariş verme konusunda endişe etmeyin, her segmenti bulduğunuzda çarpışma halinde tekrar tekrar alt bölümlere ayırın). çok daha sağlam - tel köşelerden dolaştırılmaz ve az miktarda penetrasyon iyi olur.

Bence Pekuja'nın yaklaşımı da bunun için işe yarıyor, ancak alternatif yaklaşımlar da var. Kullandığım bir yaklaşım, yardımcı çarpışma verilerini eklemek: dünyadaki her dışbükey v'de (yani, ipin etrafına dolanabilecek şekillerin köşeleri), yönlendirilmiş çizgi parçasını uv, yani u nokta "köşenin içinde" (yani dünyanın içinde, "arkada" v; u hesaplamak için, bir enterpolasyonu normal boyunca v'den içe doğru bir ışın yayınlayabilir ve v'den sonra veya ışının dünyanın bir kenarı ile kesişmesinden bir süre önce durabilirsiniz ve Sağlam bölgeden çıkar ya da sadece görsel bir araç / seviye editörü kullanarak parçaları dünyaya manuel olarak boyayabilirsiniz).

Neyse, artık bir takım "köşe çizgileri" uv; her bir uv ve kablodaki her bir ab için, ab ve uv'nin kesiştiğinden emin olun (yani statik, boolean lineseg-lineseg kesişim sorgusu); eğer öyleyse, tekrar kablolayın (lineseg ab'yi av ve vb'ye ayırın, yani v'yi ekleyin), ipin v yönünde büküldüğünü kaydeder. b'nin oluşturulduğu zamankiyle aynıdır (bu "viraj yönü" testlerinin tümü yalnızca işaretli alan testleridir); değilse, iki parçayı ac şeklinde birleştirin (yani b'yi çıkarın).

Ya da belki birleştim ve sonra ayrıldım, unutuyorum - ama kesinlikle iki olası emirden birinde çalışıyor! :)

Geçerli çerçeve için hesaplanan tüm tel bölümleri göz önüne alındığında, iki tel uç noktası arasındaki mesafeyi sınırlandırabilirsiniz (ve hatta iç ve hatta tel ve dünya arasındaki temas noktaları gibi iç noktaları da dahil edebilirsiniz) ).

Her neyse, umarım bu biraz yarar sağlayacak ... bağlantıda bulunduğum yazıların da size bazı fikirler vermesi gerekiyor.


0

Buna bir yaklaşım, ipin yaylarla birbirine bağlanmış çarpışan parçacıklar olarak modellenmesidir. (oldukça sert olanlar, muhtemelen sadece bir kemik gibi). Parçacıklar çevreyle çarpışarak, ipin öğelerin etrafına dolandığından emin olun.

İşte kaynak olan bir demo: http://www.ewjordan.com/rgbDemo/

(Birinci seviyede sağa doğru hareket edin, etkileşime geçebileceğiniz kırmızı bir ip var)


2
Uh - bu özellikle istemiyorum budur (soruya bakın).
Andrew Russell

Ah. Bu asıl sorudan net değildi. Bu kadar açıklığa kavuşturmak için zaman ayırdığınız için teşekkür ederiz. (Harika diyagram!) Dinamik ayrılmaları yapmak yerine, bir dizi çok küçük sabit bağlantıyla giderdim - ortamınızdaki büyük bir performans sorunu olmadığı sürece kodlaması çok daha kolaydır.
Rachel Blum
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.