Daire daire çarpışma içinde


9

Projelerimden birinde daire şeklinde bir oyun alanım var. Bu dairenin içinde başka bir küçük daire dolaşıyor. Yapmak istediğim şey, küçük dairenin daha büyük olanın dışına çıkmasını önlemek. Aşağıda, 2. karede küçük dairenin kısmen dışarıda olduğunu, dışarı doğru hareket etmeden hemen önce geri taşımak için bir yola ihtiyacım olduğunu görebilirsiniz. Bu nasıl yapılabilir?

Temel örnek

Ayrıca, küçük dairenin hızını güncelleyebilmem için büyük dairenin yayı boyunca çarpışma noktasına ihtiyacım var. Kişi bu noktayı nasıl hesaplar?

Küçük daireyi hareket ettirmeden önce yapmak istediğim şey, bir sonraki konumunu tahmin ediyorum ve eğer dışarıdaysa, t = 0 ile t = 1 (t = 1 tam zamanlı adım) arasındaki çarpışma süresini buluyorum. Eğer çarpışma zamanı t ise, o zaman t sırasında küçük daireyi tam zamanlı adım yerine hareket ettiririm. Ama yine, sorun şu ki, o zaman çarpışma iki daire söz konusu olduğunda ve biri diğerinin içinde olduğunda nasıl tespit edeceğimi bilmiyorum.

DÜZENLE:

Çarpışma noktası örneği (yeşil) bulmak istiyorum. Belki resim biraz kapalı ama fikri anladınız.

resim açıklamasını buraya girin

Yanıtlar:


10

Büyük dairenin merkez Ave yarıçapı olduğunu Rve küçük dairenin merkez Bve yarıçapın rkonuma doğru hareket ettiğini varsayalım C.

Zarif kullanarak bu sorunu çözmek için bir yol yoktur Minkovski toplamları radius diski değiştirin: (aslında çıkarmalar,) Ryarıçaplı bir disk ile R-rve radius disk ryarıçaplı bir diski ile 0, yani. basit bir nokta B. Sorun, bir çizgi-daire kavşak problemi haline gelir.

Ardından AC, mesafenin küçük olup olmadığını kontrol etmeniz yeterlidir R-r. Eğer öyleyse, daireler çarpışmaz. Eğer daha geniş ise, sadece noktasını bulmak Düzerine BCmesafede R-rait Ave bu küçük dairenin merkezinin yeni konumudur. Bu, şu şekilde bulmaya eşdeğerdir k:

  vec(BD) = k*vec(BC)
and
  norm(vec(AD)) = R-r

İkame vec(AD)ile vec(AB) + vec(BD)verir:

AB² + k² BC² + 2k vec(AB).vec(BC) = (R-r

Başlangıç ​​pozisyonunun büyük dairenin içinde olması şartıyla, bu kuadratik denklemin kbir pozitif kökü vardır. Pseudocode ile denklemi nasıl çözeceğiniz aşağıda açıklanmıştır:

b = - vec(AB).vec(BC) / BC²    // dot product
c = (AB² - (R-r)²) / BC²
d = b*b - c
k = b - sqrt(d)
if (k < 0)
    k = b + sqrt(d)
if (k < 0)
    // no solution! we must be slightly out of the large circle

Bu değeriyle k, küçük çemberin yeni merkez olan Döyle ki BD = kBC.

Düzenleme : ikinci dereceden denklem çözümü ekle


Teşekkürler, zarif görünüyor ama anladığımdan emin değilim. Örneğin: "BC'deki A noktasını Rr mesafesinden bulun". Daha iyi anlamaya çalışmak için bir resim çizdim . Yani eğer B (AX, AY- (Rr)) ile başlarsak ve C, mevcut hız ile sonuçlanacağımız yerdir. Alıntılanan metni anlama şeklim: BC çizgi segmentinde D'den A'ya uzak bir Rr mesafesi olan bir D noktası bulun. diğerleri puan> A'dan Rr uzakta olacak. Ne eksik?
dbostream

@dbostream Hiçbir şey eksik değil. İki daire zaten temas halindeyse, algılanacak gerçek bir çarpışma olmaz: çarpışma gerçekleşir Bve k=0. Şimdi eğer istediğiniz çarpışma çözünürlüğü ise, cevabımda bunu ele almadım çünkü nesnelerin fiziksel özellikleri hakkında bilgi gerektirecektir. Ne olması gerekiyordu? İç daire içeride sekmeli mi? Yoksa yuvarlan? Sweep?
sam hocevar

Küçük dairenin büyük dairenin yayı boyunca kaymaya başlamasını istiyorum. Eğer yanılmıyorsam, çarpma noktasının büyük dairenin yayında olmasını istiyorum, böylece hızı güncellemek için normalini kullanabilirim.
dbostream

@dbostream, hareketin bu şekilde kısıtlanması gerekiyorsa, mümkün olan en kısa sürede bu kısıtlamayı izlemenizi öneririm: eğer hız ise V, iç daireyi dairenin V*tçevresi boyunca ilerletin R-r. Bu, açı V*t/(R-r)radyanlarının nokta etrafında dönmesi anlamına gelir A. Ve hız vektörü aynı şekilde döndürülebilir. Normali (her zaman çemberin merkezine doğru yönlendirilmiş olan) bilmeye veya hızı başka bir şekilde güncellemeye gerek yoktur.
sam hocevar

Döndürmeden önce hala küçük daireyi çarpışma noktasına taşımam gerekiyor. Ve bir rotasyon matrisi kullanarak pozisyonu döndürmeye çalıştığımda, yeni pozisyon tam olarak (ama neredeyse) büyük dairenin merkezinden uzakta değildi, ama bu küçük fark çarpışma testimi bir sonraki çalıştırmada başarısız hale getirmek için yeterliydi. Yenisini bulmak için konumu döndürmek zorunda mıyım, düz bir duvarla bir şey çarpışırsa, olabildiğince vektör işlemlerini kullanmak mümkün değil mi?
dbostream

4

Büyük dairenin A dairesi ve küçük dairenin B dairesi olduğunu varsayalım.

B'nin A içinde olup olmadığını kontrol edin:

distance = sqrt((B.x - A.x)^2 + (B.y - A.y)^2))
if(distance > A.Radius + B.Radius) { // B is outside A }

n-1B karesinde A'nın içinde ve nB karesinde A'nın dışındaysa ve kareler arasındaki zaman çok büyük değilse (aka B çok hızlı hareket etmiyorsa), sadece B'nin Kartezyen koordinatlarını bularak çarpışma noktasına yaklaşabiliriz A'ya:

collision.X = B.X - A.X;
collision.Y = B.Y - A.Y;

Daha sonra bu noktaları bir açıya dönüştürebiliriz:

collision.Normalize(); //not 100% certain if this step is necessary     
radians = atan2(collision.Y, collision.X)

tB'nin A dışında ilk kez ne olduğunu tam olarak bilmek istiyorsanız, her karede bir ışın-daire kavşağı yapabilir ve B'den çarpışma noktasına kadar olan mesafenin daha büyük olup olmadığını karşılaştırabilirsiniz. Geçerli hız. Eğer öyleyse, çarpışma zamanını tam olarak hesaplayabilirsiniz.


Teşekkürler, ancak bu kesişim testini yaparken küçük dairenin merkezinden bir ışın çekmek gerçekten doğru mu? Bu resmin ortasında senaryo ile sonuçlanmayacak mıyız ? Demek istediğim, küçük dairenin arkındaki büyük daireyle çarpışan ilk nokta mutlaka yay üzerindeki hız yönünde olan nokta değildir. Bağlandığım resmin alt senaryosundaki gibi bir şeye ihtiyacım olduğunu düşünüyorum. İlk gönderiye ihtiyacım olduğunu düşündüğüm bir örneği gösteren yeni bir resim ekledim.
dbostream

Hmm, sanırım bu senaryo mümkün. Belki de B.Radius + B'nin bu çerçevenin maksimum hareketine sahip yeni bir daire C ile test edin, bunun A ile çarpışıp çarpışmadığını kontrol edin ve sonra A'dan tüylü olan C üzerindeki noktayı çalışın (Bu btw'yi denemedim)
Roy T.

3
Daha az kelime kullanarak: (mesafe (A, B))> (Ra-Rb) bir çarpışma olursa ve Ra-Rb'ye eşit bir mesafe elde etmek için küçük daireyi hareket ettirirsiniz. Yoksa küçük daireyi normal olarak hareket ettirirsiniz. Bu arada @dbostream, Spekülatif Kişilerin basitleştirilmiş bir formuna benzer bir şey kullanıyorsunuz, aramayı deneyin.
Darkwings

@Darkwings +1 kesinlikle haklısınız ve bu da sesi daha basit hale getiriyor!
Roy T.

Kulağa basit geliyor çünkü gerekli tüm temel geometriyi çıkardım. 'Çarpışma' olarak adlandırmak yerine, `` boundAB '' adını vermiş olabilirsiniz, çünkü gerçekte olan budur: (AB) 'ye bağlı ücretsiz AB vektörü. Normalleştirdikten sonra, hem düz çizgilerin AB demetine paralel denklemi hem de kullanışlı bir birim vektörü alırsınız. Daha sonra bu birim vektörü herhangi bir D mesafesi için çarpabilir ve yeni bulunan paramenterleri A'ya ihtiyacınız olan çarpışma noktasını bularak ekleyebilirsiniz: C (Ax + Dx, Ay + Dy). Şimdi daha karmaşık geliyor ama aynı şey: P
Darkwings

0

(Xa, Ya) büyük dairenin konumunu ve yarıçapını R, ve (Xb, Yb) küçük dairenin konumunu ve yarıçapını r olsun.

Bu iki dairenin çarpışıp çarpışmadığını kontrol edebilirsiniz.

DistanceTest = sqrt(((Xa - Xb) ^ 2) + ((Ya - Yb) ^ 2)) >= (R - r)

Çarpışmanın yerini bulmak için, ikili bir arama kullanarak ancak sabit sayıda adımla çevrelerin çarpıştığı anı tam olarak bulun. Oyununuzun nasıl yapıldığına bağlı olarak, kodun bu bölümünü optimize edebilirsiniz (bu çözümü küçük topun nasıl davrandığından bağımsız olacak şekilde sağladım. Sabit hızlanma veya sabit hız varsa, kodun bu kısmı optimize edilebilir ve basit bir formülle değiştirilir).

left = 0 //the frame with no collision
right = 1 //the frame after collision
steps = 8 //or some other value, depending on how accurate you want it to be
while (steps > 0)
    checktime = left + (right - left) / 2
    if DistanceTest(checktime) is inside circle //if at the moment in the middle of the interval [left;right] the small circle is still inside the bigger one
        then left = checktime //the collision is after this moment of time
        else right = checktime //the collision is before
    steps -= 1
finaltime = left + (right - left) / 2 // the moment of time will be somewhere in between, so we take the moment in the middle of interval to have a small overall error

Çarpışma süresini öğrendikten sonra, iki dairenin pozisyonlarını son anda hesaplayın ve son çarpışma noktası

CollisionX = (Xb - Xa)*R/(R-r) + Xa
CollisionY = (Yb - Ya)*R/(R-r) + Ya

0

Sam Hocevar tarafından açıklanan algoritmayı kullanarak jsfiddle üzerinde bir daire içinde zıplayan bir top demosu uyguladım :

http://jsfiddle.net/klenwell/3ZdXf/

İletişim noktasını tanımlayan javascript şöyledir:

find_contact_point: function(world, ball) {
    // see https://gamedev.stackexchange.com/a/29658
    var A = world.point();
    var B = ball.point().subtract(ball.velocity());
    var C = ball.point();
    var R = world.r;
    var r = ball.r;

    var AB = B.subtract(A);
    var BC = C.subtract(B);
    var AB_len = AB.get_length();
    var BC_len = BC.get_length();

    var b = AB.dot(BC) / Math.pow(BC_len, 2) * -1;
    var c = (Math.pow(AB_len, 2) - Math.pow(R - r, 2)) / Math.pow(BC_len, 2);
    var d = b * b - c;
    var k = b - Math.sqrt(d);

    if ( k < 0 ) {
        k = b + Math.sqrt(d);
    }

    var BD = C.subtract(B);
    var BD_len = BC_len * k;
    BD.set_length(BD_len);

    var D = B.add(BD);
    return D;
}
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.