2B dikdörtgen nesne çarpışmalarının yönünü nasıl tespit edebilirim?


11

Bu sorudan sonra biraz daha yardıma ihtiyacım var.

Bir çarpışmanın dikdörtgenin hangi tarafından geldiğini ve buna göre tepki verdiğini nasıl öğrenebilirim?

dikdörtgenler her taraftan çarpışıyor

Mavi oklar, kutuyla çarpışmadan önce ve sonra bazı dairesel nesnelerin izleyeceği yollardır.

Bunu nasıl hesaplayabilirim?

Yanıtlar:


8

Bu, diğer sorunuza dayandığından, dikdörtgenin eksen hizalaması için bir çözüm vereceğim.

İlk olarak, mevcut nesnenin dikdörtgenini aşağıdaki değerlerle oluşturursunuz:

int boxLeft = box.X;
int boxRight = boxLeft + box.Width;
int boxTop = box.Y;
int boxBottom = boxTop + box.Height;

Daha sonra, eski nesnenin dikdörtgenini oluşturmak için (çarpışmadığı zaman) eski nesnenin konumuna sahip olmalısınız (her nesnede saklayabileceğiniz veya bir işleve iletebilirsiniz):

int oldBoxLeft = box.OldX;
int oldBoxRight = oldBoxLeft + box.Width;
int oldBoxTop = box.OldY;
int oldBoxBottom = oldBoxTop + box.Height;

Şimdi, çarpışmanın nereden geldiğini bilmek için, eski pozisyonun çarpışma alanında olmadığı ve yeni pozisyonunun bulunduğu tarafı bulmalısınız. Çünkü, düşündüğünüzde, çarpıştığınızda böyle olur: çarpışmayan bir taraf başka bir dikdörtgene girer.

Bunu şu şekilde yapabilirsiniz (bu işlevler bir çarpışma olduğunu varsayar. Çarpışma yoksa çağrılmamalıdır):

 bool collidedFromLeft(Object otherObj)
{
    return oldBoxRight < otherObj.Left && // was not colliding
           boxRight >= otherObj.Left;
}

Rince ve tekrarlayın.

bool collidedFromRight(Object otherObj)
{
    return oldBoxLeft >= otherObj.Right && // was not colliding
           boxLeft < otherObj.Right;
}

bool collidedFromTop(Object otherObj)
{
    return oldBoxBottom < otherObj.Top && // was not colliding
           boxBottom >= otherObj.Top;
}

bool collidedFromBottom(Object otherObj)
{
    return oldBoxTop >= otherObj.Bottom && // was not colliding
           boxTop < otherObj.Bottom;
}

Şimdi, diğer sorudan gelen çarpışma tepkisi ile gerçek kullanım için:

if (collidedFromTop(otherObj) || collidedFromBottom(otherObj))
    obj.Velocity.Y = -obj.Velocity.Y;
if (collidedFromLeft(otherObj) || collidedFromRight(otherObj))
    obj.Velocity.X = -obj.Velocity.X;

Yine, bu en iyi çözüm olmayabilir, ancak genellikle çarpışma tespiti için bu şekilde giderim.


Bir kez daha haklıydın! ; D Teşekkürler ... (bir dahaki sefere bana daha fazla kartpostal
yolla

Ahhh ne yazık ki ben ne için kullanabileceğini bilmiyordum .. belki bir dahaki sefere!
Jesse Emond

7

Soru bu soru ile kısmen aynı olduğundan , sorunuzun yanıtını denemek için cevabımın bazı kısımlarını tekrar kullanacağım.


Aşağıdaki açıklamayı daha anlaşılır hale getirmek için bir bağlam ve bazı değişkenler tanımlayalım. Burada kullanacağımız temsil formu muhtemelen kendi verilerinizin biçimine uymuyor, ancak bu yolu anlamak daha basit olmalı (aslında prensibi anladıktan sonra başka tür sunumları kullanarak aşağıdaki yöntemleri kullanabilirsiniz)

Bu nedenle, Eksen Hizalanmış Sınırlayıcı Kutu (veya Yönlendirilmiş Sınırlı Kutu ) ve hareketli bir Varlık dikkate alacağız .

  • Sınırlayıcı Kutu 4 kenardan oluşur ve her birini şu şekilde tanımlayacağız:
    Side1 = [x1, y1, x2, y2] (iki nokta [x1, y1] ve [x2, y2])

  • Hareketli Varlık bir hız vektörü (konum + hız) olarak tanımlanır:
    bir konum [pozX, posY] ve bir hız [speedX, speedY] .


AABB / OBB'nin hangi tarafına bir vektör tarafından vurulduğunu aşağıdaki yöntemi kullanarak belirleyebilirsiniz:

  • 1 / AABB'nin dört tarafından geçen sonsuz çizgiler ile varlık hızı vektörünü eğim olarak kullanan sonsuz çizgi (çarpışma öncesi) arasındaki kesişme noktalarını bulun. (Paralel veya çakışan çizgilere karşılık gelen bir çarpışma noktası veya tanımlanmamış bir sayı bulabilirsiniz)

  • 2 / Kavşak noktalarını (varsa) öğrendikten sonra, segment sınırlarına girenleri arayabilirsiniz.

  • 3 / Son olarak, listede hala birden fazla nokta varsa (hız vektörü birden çok taraftan geçebilir), kesişimden varlık kaynağına vektörün büyüklüklerini kullanarak varlık kaynağından en yakın noktayı arayabilirsiniz.

Daha sonra basit bir nokta ürünü kullanarak çarpışma açısını belirleyebilirsiniz.

  • 4 / Vuruş tarafı vektörü ile objenin bir nokta ürününü (muhtemelen bir top?) Kullanarak çarpışma arasındaki açıyı bulun.

----------

Daha fazla detay:

  • 1 / Kavşakları bulun

    • a / Parametrik formlarını (P (t) = Po + tD) kullanarak sonsuz çizgileri (Ax + Bx = D) belirleyin.

      Nokta kaynağı: Po = [posX, posY]
      Yön vektörü: D = [speedX, speedY]

      A = Dy = speedY
      B = -Dx = -speedX
      D = (Po.x * Dy) - (Po.y * Dx) = (posX speedY) - (posY speedX)

      Ax + By = D <====> (speedY x) + (-speedX y) = (posX speedY) - (posY speedX)

      Yöntemi göstermek için varlık noktası değerlerini kullandım, ancak bu sınırlama kutusunun 4 yan sonsuz satırını belirlemek için tamamen aynı yöntemdir (Po = [x1, y1] ve D = [x2-x1; y2-y1] yerine).

    • b / Sonra, iki sonsuz çizginin kesişimini bulmak için aşağıdaki sistemi çözebiliriz:

      A1x + B1x = D1 <== Hız vektörü eğim olarak varlık noktasından geçen çizgi.
      A2x + B2x = D2 <== AABB kenarlarından geçen hatlardan biri.

      müdahale için aşağıdaki koordinatları verir:

      Durdurma x = (( B 2 * D 1) - ( B 1 * D 2)) / (( A 1 * B 2) - ( A 2 * B 1))
      Durdurma y = (( A 1 * D 2) - ( A 2 * D 1)) / (( A 1 * B 2) - ( A 2 * B 1))

      Payda ((A1 * B2) - (A2 * B1)) sıfıra eşitse, her iki çizgi paralel veya örtüşüyorsa, aksi takdirde bir kavşak bulmalısınız.

  • 2 / Segment sınırları için test yapın. Bunu doğrulamak kolay olduğu için daha fazla ayrıntıya gerek yoktur.

  • 3 / En yakın noktayı arayın. Listede hala birden fazla nokta varsa, hangi tarafın kuruluşun başlangıç ​​noktasına en yakın olduğunu bulabiliriz.

    • a / Kavşak noktasından varlık başlangıç ​​noktasına giden vektörü belirleme

      V = Po - Int = [Po.x - Int.x; Po.y - Int.y]

    • b / Vektör büyüklüğünü hesapla

      || V || = sqrt (V.x² + V.y²)

    • c / en küçük olanı bul.
  • 4 / Artık hangi tarafa çarpılacağını bildiğinize göre, bir nokta ürünü kullanarak açıyı belirleyebilirsiniz.

    • a / Let S = [x2-x1; y2-y1] vurulacak yan vektör ve E = [speedX; speedY] varlık hızı vektörüdür.

      Vector dot ürün kuralını kullanarak biliyoruz ki

      S · E = Sx Ex + Sy Ey
      ve
      S · E = || S || || e || çünkü θ

      Böylece, bu denklemi biraz değiştirerek θ belirleyebiliriz ...

      cos θ = (S · E) / (|| S || || E ||)

      θ = acos ((S · E) / (|| S || || E ||))

      ile

      S · E = Sx * Ex + Sy * Ey
      || S || = sqrt (Sx² + Sy²)
      || E || = sqrt (Ex² + Ey²)


Not: Diğer soru dizisinde söylediğim gibi, bu muhtemelen en etkili ya da en basit yol değildir, akla gelen şey budur ve matematiğin bir kısmı belki yardımcı olabilir.

Somut bir OBB örneği (AABB ile yaptım) ile doğrulamamıştım ama çok da işe yaramalı.


6

Basit bir yol, çarpışmayı çözmek, sonra hareket eden nesnenin çarpışma kutusunu sırayla her yönde bir piksel çevirmek ve hangilerinin çarpışma ile sonuçlandığını görmek.

Eğer bunu "düzgün" ve döndürülmüş çarpışma şekilleri veya keyfi çokgenler ile yapmak istiyorsanız, Ayırma Ekseni Teoremini okumanızı öneririm. Örneğin Metanet yazılımı (N oyununu yapan kişiler) SAT konusunda harika bir geliştirme makalesine sahiptir . Ayrıca ilgili fiziği de tartışıyorlar.


2

Bunun bir yolu, dünyayı dikdörtgenin etrafında döndürmek olacaktır. Bu durumda "dünya" sadece önem verdiğiniz nesnelerdir: dikdörtgen ve top. Dikdörtgenin sınırları x- / y eksenleri ile hizalanana kadar merkezi etrafında döndürürsünüz, sonra topu aynı miktarda döndürürsünüz.

Buradaki önemli nokta, topu kendi değil, dikdörtgenin merkezinin etrafında döndürmenizdir.

Ardından, diğer döndürülmemiş dikdörtgenlerde olduğu gibi çarpışmayı kolayca test edebilirsiniz.


Başka bir seçenek, dikdörtgeni dört ayrı çizgi parçası olarak işlemek ve her biri ile ayrı ayrı çarpışma testi yapmaktır. Bu çarpışma test etmek için izin verir ve yan aynı zamanda çarpıştı edildiği dışarı figür.


1

Hesaplamalarımda sabit açılar kullandım, ancak bu size yardımcı olacak

void Bullet::Ricochet(C_Rect *r)
{
    C_Line Line;
    //the next two lines are because I detected 
    // a collision in my main loop so I need to take a step back.

    x = x + ceil(speed * ((double)fcos(itofix(angle)) / 65536));
    y = y + ceil(speed * ((double)fsin(itofix(angle)) / 65536));
    C_Point Prev(x,y);

    //the following checks our position to all the lines will give us
    // an answer which line we will hit due to no lines
    // with angles > 90 lines of a rect always shield the other lines.

    Line = r->Get_Closest_Line(Prev);    
    int langle = 0;
    if(!Line.Is_Horizontal())   //we need to rotate the line to a horizontal position
    {
        langle = Line.Get_Point1().Find_Fixed_Angle(Line.Get_Point2());
        angle = angle - langle;  //to give us the new angle of approach
    }
    //at this point the line is horizontal and the bullet is ready to be fixed.
    angle = 256 - angle;
    angle += langle;
}
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.