2D Platformer AABB çarpışma problemleri


51

http://dl.dropbox.com/u/3724424/Programming/Gifs/game6.gif

AABB çarpışma çözünürlüğü ile ilgili bir sorunum var.


AABB kesişimini önce X eksenini, sonra Y eksenini çözerek çözerim. Bu, bu hatayı önlemek için yapılır: http://i.stack.imgur.com/NLg4j.png


Bir nesne oynatıcıya taşındığında ve oynatıcının yatay olarak itilmesi gerektiğinde mevcut yöntem iyi çalışır. .Gif'te gördüğünüz gibi, yatay çiviler oynatıcıyı doğru şekilde itiyor.


Ancak dikey çiviler oynatıcıya girdiğinde, ilk önce X ekseni hala çözülür. Bu "çivileri asansör olarak kullanmak" imkansız kılar.

Oynatıcı dikey ani hareket ettiğinde (yerçekiminden etkilenir, içine düşer), Y eksenine itilir çünkü başlangıçta X ekseninde üst üste binme yoktur.


Denedim bir şey bu bağlantının ilk cevabında açıklanan yöntem oldu: 2D dikdörtgen nesne çarpışma algılama

Bununla birlikte, sivri uçlar ve hareket eden nesneler, hızlarını değil konumlarını değiştirerek hareket ederler ve Update () yöntemi çağrılana kadar bir sonraki öngörülen konumlarını hesaplamam. Tabii ki bu çözüm de işe yaramadı. :(


AABB çarpışmasını yukarıda açıklanan durumların her ikisinin de amaçlandığı şekilde çalışacağı şekilde çözmem gerekiyor.

Bu benim şu anki çarpışma kaynak kodum: http://pastebin.com/MiCi3nA1

Buna birileri bakarsa çok minnettar olurum, çünkü bu hata motorda baştan beri mevcuttu ve başarılı bir çözüm bulmak için iyi bir çözüm bulmakta zorlanıyorum. Bu ciddi bir şekilde beni geceleri çarpışma koduna bakarak geçirmeme ve "eğlenceli kısma" girmeme ve oyun mantığını kodlamama engelliyor :(


XNA AppHub platform demo'sunda olduğu gibi aynı çarpışma sistemini uygulamaya çalıştım (malzemelerin çoğunu kopyalayıp yapıştırarak). Ancak, "atlama" hatası benim oyunumda meydana gelirken, AppHub demosunda gerçekleşmez. [atlama hatası: http://i.stack.imgur.com/NLg4j.png ]

Atlamak için oyuncunun "onGround" olup olmadığını kontrol et, sonra Velocity.Y'ye -5 ekle.

Oyuncunun Velocity.X'i Velocity.Y'den daha yüksek olduğundan (şemadaki dördüncü panele bakın), onGround, olması gerekmediğinde true olarak ayarlanır ve böylece oyuncunun havada zıplamasını sağlar.

Bunun AppHub demosunda olmayacağına inanıyorum, çünkü oyuncunun Velocity.X'i asla Velocity.Y'den daha yüksek olmayacak, ama hatalı olabilirim.

Bunu önce X ekseninde, sonra Y ekseninde çözerek çözdüm. Ama bu yukarıda belirttiğim gibi sivri uçlarla çarpışmayı berbat ediyor.


5
Yanlış giden hiçbir şey yok. Kullandığım yöntem önce X eksenine, ardından Y eksenine iter. Bu yüzden oyuncu dikey çivilerde bile yatay olarak itiliyor. "Atlama sorununu" önleyen ve oynatıcıyı sığ eksene (en az nüfuz eden eksen) iten bir çözüm bulmam gerekiyor, ancak hiçbirini bulamıyorum.
Vittorio Romeo,

1
Oyuncunun dokunma engelinin hangi yüzü olduğunu tespit edebilir ve bunu çözebilir
Ioachim

4
@Johnathan Hobbs, soruyu okuyun. Vee, kodunun ne yaptığını tam olarak biliyor ama belli bir sorunu nasıl çözeceğini bilmiyor. Kodda adım atmak bu durumda ona yardımcı olmaz.
AttackingHobo

4
@Maik @Jonathan: Programda hiçbir şey "yanlış gitmiyor" - algoritmasının tam olarak nerede ve neden istediğini yapmadığını anlıyor. İstediği şeyi yapmak için algoritmayı nasıl değiştireceğini bilmiyor. Yani hata ayıklayıcı bu durumda yararlı değildir.
BlueRaja - Danny Pflughoeft

1
@AttackingHobo @BlueRaja Katılıyorum ve yorumumu sildim. Neler olup bittiğiyle ilgili sonuca varmak için bendim. Bunu açıklamanız ve en az bir kişiyi yanlış yönlendirmeniz için gerçekten özür dilerim. Dürüst olmak gerekirse, bir dahaki sefere herhangi bir yanıt bırakmadan önce soruyu gerçekten doğru bir şekilde özümseme konusunda bana güvenebilirsiniz.
doppelgreener

Yanıtlar:


9

Tamam, XNA AppHub platform demosunun neden "atlama" hatası olmadığını anladım : Demo çarpışma panellerini yukarıdan aşağıya doğru test ediyor . Bir "duvar" a karşı çıktığında, oyuncu çoklu taşlarla üst üste gelebilir. Çözme sırası önemlidir, çünkü bir çarpışmayı çözmek diğer çarpışmaları da çözebilir (ancak farklı bir yöne). Bu onGroundözellik yalnızca, müzikçaları y ekseni üzerinde yukarı iterek çarpışma çözüldüğünde ayarlanır. Önceki çözünürlük oynatıcıyı aşağı ve / veya yatay olarak iterse, bu çözünürlük gerçekleşmez.

Bu satırı değiştirerek XNA demosundaki "atlama" hatasını çoğaltabildim:

for (int y = topTile; y <= bottomTile; ++y)

buna:

for (int y = bottomTile; y >= topTile; --y)

(Ayrıca fizikle ilgili sabitlerin bir kısmını da ayarladım, ama bunun önemi olmamalı.)

Belki de bodiesToCheckoyununuzdaki çarpışmaları çözmeden önce y ekseninde sıralama yapmak "zıplama" hatasını düzeltecektir. XNA demosunun yaptığı ve Trevor'un önerdiği gibi çarpışmanın “sığ” ekseninde çözülmesini öneriyorum . Ayrıca, XNA demo oynatıcısının çarpışabilir döşemelerden iki kat daha uzun olduğunu ve çarpışma durumunun daha muhtemel olduğunu unutmayın.


Kulağa ilginç geliyor ... Çarpışmalardan önce her şeyi Y koordinatına göre sıralamanın tüm sorunlarımı çözebileceğini düşünüyor musunuz? Herhangi bir yakalama var mı?
Vittorio Romeo,

Aaave "avı" buldum. Bu yöntemi kullanarak, atlama hatası düzeltildi, ancak bir tavana çarptıktan sonra Velocity.Y'yi 0'a ayarlarsam, tekrar meydana gelir.
Vittorio Romeo

@Vee: Position = nextPositionhemen fordöngünün içine ayarlanır , aksi takdirde istenmeyen çarpışma çözünürlükleri onGroundhala oluşur. Müzikçalar tavana çarptığında dikey olarak aşağı (ve asla yukarı doğru) itilmemelidir, bu nedenle onGroundasla ayarlanmamalıdır. XNA demosunun yaptığı budur ve oradaki "tavan" hatasını düzeltemiyorum.
Leftium

pastebin.com/TLrQPeBU - bu benim kodum: Aslında bir "nextPosition" değişkeni kullanmadan, Pozisyon üzerinde çalışıyorum. XNA demosunda olduğu gibi çalışmalıdır, ancak Velocity.Y'yi 0'a (satır 57) ayarlayan çizgiyi uncommented olarak tutarsam atlama hatası oluşur. Çıkarırsam, oyuncu tavana atlarken sürekli yüzer. Bu düzeltilebilir mi?
Vittorio Romeo

@Vee: Kodunuz nasıl onGroundayarlandığını göstermediğinden , neden atlamanın yanlış yapılmasına izin verildiğini araştıramıyorum. XNA demosu isOnGroundiçindeki benzer özelliğini günceller HandleCollisions(). Ayrıca, Velocity.Y0'a ayarlandıktan sonra , yerçekimi oynatıcıyı tekrar aşağı indirmeye neden başlamıyor? Benim tahminim onGroundmülk yanlış ayarlanmış olmasıdır. XNA demosunun nasıl güncellediğine previousBottomve isOnGround( IsOnGround) neler olduğuna bakın .
Leftium

9

Sizin için en basit çözüm, herhangi bir çarpışmayı çözmeden önce her iki çarpışma yönünü de dünyadaki her nesneye göre kontrol etmek ve sonuçta ortaya çıkan iki "bileşik çarpışma" dan daha küçük olanı çözmek olacaktır. Bu, her zaman x'i ilk kez çözmek veya her zaman önce y'yi çözmek yerine, mümkün olan en düşük miktarda çözeceğiniz anlamına gelir.

Kodunuz şuna benzer:

// This loop repeats, until our object has been fully pushed outside of all
// collision objects
while ( StillCollidingWithSomething(object) )
{
  float xDistanceToResolve = XDistanceToMoveToResolveCollisions( object );
  float yDistanceToResolve = YDistanceToMoveToResolveCollisions( object );
  bool xIsColliding = (xDistanceToResolve != 0.f);

  // if we aren't colliding on x (not possible for normal solid collision 
  // shapes, but can happen for unidirectional collision objects, such as 
  // platforms which can be jumped up through, but support the player from 
  // above), or if a correction along y would simply require a smaller move 
  // than one along x, then resolve our collision by moving along y.

  if ( !xIsColliding || fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) )
  {
    object->Move( 0.f, yDistanceToResolve );
  }
  else // otherwise, resolve the collision by moving along x
  {
    object->Move( xDistanceToResolve, 0.f );
  }
}

büyük revizyon : Diğer cevaplar hakkındaki yorumları okumaktan sonra, nihayetinde kararsız bir varsayım fark ettim, bu yaklaşımın işe yaramamasına neden olacak (ve neden problemleri neden anlayamadığımı açıklar - hepsi değil - insanlar bu yaklaşımla gördü). Daha ayrıntılı olarak açıklamak gerekirse, burada daha önce başvuruda bulunduğum işlevlerin gerçekte ne yapması gerektiğini açıkça gösteren, daha fazla sözde kod:

bool StillCollidingWithSomething( MovingObject object )
{
  // loop over every collision object in the world.  (Implementation detail:
  // don't test 'object' against itself!)
  for( int i = 0; i < collisionObjectCount; i++ )
  {
    // if the moving object overlaps any collision object in the world, then
    // it's colliding
    if ( Overlaps( collisionObject[i], object ) )
      return true;
  }
  return false;
}

float XDistanceToMoveToResolveCollisions( MovingObject object )
{
  // check how far we'd have to move left or right to stop colliding with anything
  // return whichever move is smaller
  float moveOutLeft = FindDistanceToEmptySpaceAlongNegativeX(object->GetPosition());
  float moveOutRight = FindDistanceToEmptySpaceAlongX(object->GetPosition());
  float bestMoveOut = min( fabs(moveOutLeft), fabs(moveOutRight) );

  return minimumMove;
}

float FindDistanceToEmptySpaceAlongX( Vector2D position )
{
  Vector2D cursor = position;
  bool colliding = true;
  // until we stop colliding...
  while ( colliding )
  {
    colliding = false;
    // loop over all collision objects...
    for( int i = 0; i < collisionObjectCount; i++ )
    {
      // and if we hit an object...
      if ( Overlaps( collisionObject[i], cursor ) )
      {
        // move outside of the object, and repeat.
        cursor.x = collisionObject[i].rightSide;
        colliding = true;

        // break back to the 'while' loop, to re-test collisions with
        // our new cursor position
        break;
      }
    }
  }
  // return how far we had to move, to reach empty space
  return cursor.x - position.x;  
}

Bu bir "nesne çifti başına" testi değildir; Hareketli nesneyi bir dünya haritasının her bir döşemesine karşı ayrı ayrı test ederek ve çözerek çalışmaz (bu yaklaşım asla güvenilir bir şekilde çalışmaz ve döşemelerin boyutları azaldıkça gittikçe yıkıcı şekilde başarısız olur). Bunun yerine, hareketli nesneyi dünya haritasındaki her nesneye karşı aynı anda test ediyor ve ardından tüm dünya haritasına karşı çarpışmalara dayanarak çözüyor.

Bu, (örneğin) bir duvar içindeki ayrı duvar karolarının oynatıcıyı hiçbir zaman bitişik iki fayans arasında yukarı ve aşağı sıçramamasını sağlayarak oyuncunun aralarında bulunmayan bir boşlukta sıkışıp kalmasına neden olur; Çarpışma çözme mesafeleri, yalnızca o zaman üstünde bir başka katı karo bulunabilecek tek bir karonun sınırına değil , dünyadaki boş alana kadar her zaman hesaplanır .


1
i.stack.imgur.com/NLg4j.png - Yukarıdaki yöntemi kullanırsam olur.
Vittorio Romeo

1
Belki de kodunuzu doğru anlamıyorum, fakat sorunu şemada açıklamama izin verin: oyuncu X ekseninde daha hızlı olduğundan, X penetrasyonu> Y penetrasyonundan>. Çarpışma Y eksenini seçer (çünkü penetrasyon daha küçüktür) ve oynatıcıyı dikey olarak iter. Bu, oynatıcı yeterince yavaşsa, Y penetrasyonunun her zaman X penetrasyonundan daha> olması durumunda gerçekleşmez.
Vittorio Romeo,

1
@Vee Bu yaklaşımı kullanarak yanlış bir davranış sergilemek için, diyagramın hangi bölmesine atıfta bulunuyorsunuz? Oyuncu 5'in X'e göre X'e daha az nüfuz ettiğini ve bunun da X ekseni boyunca itilebileceğini - ki bu doğru davranış olduğunu - belirlediğim bölme 5'i kabul ediyorum. Kötü davranışı yalnızca hıza dayalı (görüntüdeki gibi) çözünürlük için bir eksen seçtiyseniz ve gerçek penetrasyona dayanmadığınızda (önerdiğim gibi) elde edersiniz.
Trevor Powell,

1
@Trevor: Ah, ne dediğinizi anlıyorum. Bu durumda, basitçe yapabilirsinizif(fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) || xDistanceToResolve == 0) { object->Move( 0.f, yDistanceToResolve ); } else { object->Move( xDistanceToResolve, 0.f ); }
BlueRaja - Danny Pflughoeft 10:11

1
@BlueRaja Harika bir nokta. Cevabımı, önerdiğiniz gibi daha az koşul kullanacak şekilde değiştirdim. Teşekkürler!
Trevor Powell,

6

Düzenle

Ben var bunu geliştirilmiş

Görünüşe göre değiştirdiğim anahtar şey kesişimleri sınıflandırmak.

Kavşaklar ya da:

  • Tavan dikenleri
  • Zemin vuruşları (yerden iterek)
  • Hava duvarı çarpışmaları
  • Topraklanmış duvar çarpışmaları

ve onları bu sırayla çözerim

Bir yer çarpışmasını oyuncunun döşemede en az 1 / 4'lü şekilde tanımlayın

Yani bu bir çarpışmadır ve oyuncu ( mavi ) kiremitin üstüne oturur ( siyah ) kara çarpışması

Fakat bu bir zemin çarpması DEĞİLDİR ve oyuncu kayanın sağ tarafına "kayacak" bir yere çarpışma oyuncusu olmaya gönüllü değil

Bu yöntemle oyuncu artık duvarların kenarlarına takılmayacak


Metodunuzu denedim (uygulamama bakın: pastebin.com/HarnNnnp ) ancak oyuncunun Hızını 0 olarak nerede ayarlayacağımı bilmiyorum. Eğer encrY <= 0 ise 0'ı ayarlamaya çalıştım ancak oyuncunun orta havada durmasına izin verdim bir duvar boyunca kayarken aynı zamanda art arda duvar zıplamasını sağlar.
Vittorio Romeo

Ancak, oyuncu çivilerle etkileşime girdiğinde düzgün çalışır (.gif'te gösterildiği gibi). Duvara sıçramanın (duvarlara sıkışıp kalmama) sorununu çözmeme yardım edebilirseniz gerçekten minnettar olurum. Duvarlarımın birkaç AABB döşemesinden yapıldığını unutmayın.
Vittorio Romeo,

Başka bir yorum gönderdiğim için üzgünüm, ancak kodunuzu yepyeni bir projeye kopyaladığınızda, sp değerini 5 olarak değiştirdikten ve döşemeleri duvar şeklinde oluşturduktan sonra, atlama hatası oluyor (bu şemaya bakın: i.stack.imgur.com) /NLg4j.png)
Vittorio Romeo

Tamam, şimdi dene.
bobobobo

Çalışma kodu örneği için +1 (yatay olarak hareket eden "sivri" blokları eksik olsa da :)
Leftium

3

Önceden gelenler: Motorum
, yalnızca bir nesne hareket ettiğinde ve yalnızca o anda kapladığı ızgara karelerinde tüm nesneleri gerçek zamanlı olarak çarpışmaları kontrol eden bir çarpışma algılama sınıfı kullanır.

Çözümüm:

2d platformumda bu gibi sorunlara karşı koştuğumda aşağıdaki gibi bir şey yaptım:

- (motor muhtemel çarpışmaları hızlı bir şekilde tespit eder)
- (oyun motoru belirli bir nesne için tam çarpışmaları kontrol etmesine bildirir) [nesnenin vektörünü döndürür]]
- (nesne, diğer nesnenin önceki konumuna göre önceki pozisyonun yanı sıra penetrasyon derinliğine bakar, hangi tarafın dışarı kaydırılacağını belirlemek için)
- (nesne hareket eder ve nesnenin hızı ile nesnenin hızını ortalaması alır (nesne hareket ediyorsa)


“(nesne nüfuz etme derinliğine ve aynı zamanda diğer nesnenin önceki konumuna göre, hangi tarafın dışarı kaydırılacağına karar vermek için önceki pozisyona bakar)” bu ilgimi çeken kısımdır. .Gif ve diyagram tarafından gösterilen durumda başarılı mı?
Vittorio Romeo,

Bu yöntemin işe yaramadığı bir durum (yüksek hızlar dışında) henüz bulamadım.
ultifinitus

Kulağa hoş geliyor ama penetrasyonları nasıl çözdüğünüzü gerçekten açıklayabilir misiniz? Her zaman en küçük eksende mi çözüyorsunuz? Çarpışmanın tarafını kontrol ediyor musun?
Vittorio Romeo

Aslına bakarsanız hayır, küçük eksen gitmek için iyi (birincil) bir yol değildir. BENİM NACİZANE FİKRİME GÖRE. Genelde, hangi tarafın daha önce diğer nesnenin tarafının dışında olduğunu çözerim, en yaygın olanıdır. Bu başarısız olduğunda, o zaman hız ve derinliğe göre kontrol ederim.
ultifinitus

2

Programladığım video oyunlarında, yaklaşımınızın konumunuzun geçerli olup olmadığını söyleyen bir işlevi olmasıydı. bool ValidPos (int x, int y, int wi, int he);

İşlev, sınırlama kutusunu (x, y, wi, he) oyundaki tüm geometriye karşı kontrol eder ve herhangi bir kesişme varsa false döndürür.

Hareket etmek istiyorsanız, sağa doğru söyleyin, oyuncu pozisyonunu alın, x'e 4 ekleyin ve pozisyonun geçerli olup olmadığını kontrol edin, geçerli değilse, + 3, + 2 vb. İle kontrol edin.

Yerçekimine de ihtiyacınız varsa, yere vurmadığınız sürece büyüyen bir değişkene ihtiyacınız vardır (yere çarpma: ValidPos (x, y + 1, wi, o) == doğru, y aşağıya doğru pozitif olur). eğer bu mesafeyi hareket ettirirseniz (yani ValidPos (x, y + gravity, wi, o) true değerini döndürürseniz) düşersiniz, bazen düşerken karakterinizi kontrol edememeniz gerektiğinde yararlı olur.

Şimdi, sizin probleminiz dünyanızda hareket eden nesneleriniz olması, bu yüzden her şeyden önce eski pozisyonunuzun hala geçerli olup olmadığını kontrol etmeniz gerekiyor!

Değilse, olan bir pozisyon bulmanız gerekir. Oyun içindeki nesneler oyun devri başına 2 piksel demekten daha hızlı hareket edemezse, konumun (x, y-1) geçerli olup olmadığını, sonra (x, y-2) sonra (x + 1, y) vb. vb. (x-2, y-2) ila (x + 2, y + 2) arasındaki tüm boşluk kontrol edilmelidir. Geçerli bir pozisyon yoksa, o zaman 'ezilmişsin' demektir.

HTH

Valmond


Game Maker günlerimde bu yaklaşımı kullanırdım. Gittiğiniz mesafeye ve geometriye, belki de daha yavaş olmasına rağmen, daha iyi aramalarla (seyahat ettiğiniz alanda ikili arama yapın) söyleyerek hızlandırmanın / iyileştirmenin yollarını düşünebilirim, ancak bu yaklaşımın ana dezavantajı; tüm olası çarpışmalara karşı bir kontrol yapmak yerine, pozisyondaki değişimin çek ne olursa olsun yaptığını.
Jeff

"Pozisyondaki değişimin çek ne olursa olsun yapıyorsun" Üzgünüm ama tam olarak ne anlama geliyor? BTW elbette oyunu hızlandırmak için uzay bölümleme tekniklerini (BSP, Octree, quadtree, Tilemap vb.) Kullanacaksınız, ancak her zaman bunu yapmanız gerekecek (harita büyükse), bununla ilgili değil. oynatıcıyı (doğru şekilde) hareket ettirmek için kullanılan algoritma hakkında.
Valmond

@Valmond Bence @Jeff, oynatıcınız 10 piksel sola hareket ederse, 10 farklı çarpışma algılamanıza sahip olabileceğiniz anlamına gelir.
Jonathan Connell

Eğer anlamı buysa, haklıdır ve bu şekilde olmalıdır (aksi takdirde +6'da durup durmadığımızı kim söyleyebilir? +7?). Bilgisayarlar hızlı, bunu S40'ta kullandım (~ 200mhz ve çok hızlı bir kvm) ve çekicilik gibi çalıştı. Her zaman ilk algoyu optimize etmeye çalışabilirsiniz, ancak bu daima OP'nin sahip olduğu gibi köşe kasaları verecektir.
Valmond

Tam olarak kastettiğim buydu
Jeff

2

Buna cevap vermeden önce birkaç sorum olacak. Birincisi, duvarlara sıkışıp kaldığınız asıl böceğin içinde, tek tek büyük karoların üzerindeki fayanslar, büyük bir karoya karşılık mıydı? Ve eğer öyleyse, oyuncu aralarında sıkışıp kalıyor muydu? Her iki soruya da evet ise, yeni pozisyonunuzun geçerli olduğundan emin olun . Bu, oyuncuya hareket etmesini söylediğiniz yerde bir çarpışma olup olmadığını kontrol etmeniz gerektiği anlamına gelir. Aşağıda açıklandığı gibi Yani asgari yerinden çözmek ve sonra o dayalı oyuncu hareket sadece kendisinin ise canoraya taşın. Hemen hemen burnun altında: P Bu aslında "köşe kılıfları" olarak adlandırdığım başka bir hatayı ortaya çıkaracak. Temelde köşeler açısından (.gif'inizdeki yatay çivilerin çıktığı sol alt gibi, ancak çiviler olmasaydı) bir çarpışmayı çözmezdi, çünkü ürettiğiniz kararların hiçbirinin geçerli bir konuma yol açmayacağını düşünüyordu. . Bunu çözmek için, çarpışmanın çözülüp çözülmediğinin yanı sıra tüm asgari penetrasyon çözünürlüklerinin bir listesini de saklayın. Daha sonra, çarpışma çözülmediyse, oluşturduğunuz her çözünürlük üzerinde döngü yapın ve maksimum X ve maksimum Y çözünürlüklerini takip edin (maksimumların aynı çözünürlükten gelmesi gerekmez). Ardından çarpışmayı bu maksimum değerlerde çözün. Bu, tüm problemlerinizi ve karşılaştığım problemleri çözüyor gibi görünüyor.

    List<Vector2> collisions = new List<Vector2>();
        bool resolved = false;
        foreach (Platform p in testPlat)
        {
            Vector2 dif = p.resolveCollision(player.getCollisionMask());

            RectangleF newPos = player.getCollisionMask();

            newPos.X -= dif.X;
            newPos.Y -= dif.Y;


            if (!PlatformCollision(newPos)) //This checks if there's a collision (ie if we're entering an invalid space)
            {
                if (dif.X != 0)
                    player.velocity.X = 0; //Do whatever you want here, I like to stop my player on collisions
                if (dif.Y != 0)
                    player.velocity.Y = 0;

                player.MoveY(-dif.Y);
                player.MoveX(-dif.X);
                resolved = true;
            }
            collisions.Add(dif);
        }

        if (!resolved)
        {
            Vector2 max = Vector2.Zero;

            foreach (Vector2 v in collisions)
            {
                if (Math.Abs(v.X) > Math.Abs(max.X))
                {
                    max.X = v.X;
                }
                if (Math.Abs(v.Y) > Math.Abs(max.Y))
                {
                    max.Y = v.Y;
                }
            }

            player.MoveY(-max.Y);

            if (max.Y != 0)
                player.velocity.Y = 0;
            player.MoveX(-max.X);

            if (max.X != 0)
                player.velocity.X = 0;
        }

Başka bir soru, bir kiremit mi, yoksa tek bir kiremit mi gösteriyorsunuz? Bireysel ince fayanslarsa, yatay olanlar ve dikey olanlar için aşağıda tanımladığımdan farklı bir yaklaşım kullanmanız gerekebilir. Ama bütün fayanslar ise, bu çalışması gerekir.


Tamam, yani temelde bu @ Trevor Powell'ın tarif ettiği şeydi. Sadece AABB kullandığınız için tek yapmanız gereken bir dikdörtgenin diğerine ne kadar nüfuz ettiğini bulmak. Bu size X ekseni ve Y'de bir miktar verecektir. İkisinden en düşük olanı seçin ve çarpışan nesneyi bu miktar boyunca o eksen boyunca hareket ettirin. Bir AABB çarpışmasını çözmek için tek ihtiyacınız olan şey bu. ASLA böyle bir çarpışmada birden fazla eksen boyunca hareket etmenize gerek kalmayacaktır, bu nedenle, yalnızca minimum hareket edeceğinizden, ilk önce neyin hareket edeceği konusunda kafanız karışmamalıdır.

Metanet yazılımı burada bir yaklaşım hakkında klasik bir eğiticiye sahiptir . Aynı zamanda başka şekillere de girer.

İki dikdörtgenin üst üste gelme vektörünü bulmak için yaptığım bir XNA işlevi:

    public Point resolveCollision(Rectangle otherRect)
    {
        if (!isCollision(otherRect))
        {
            return Point.Zero;
        }

        int minOtherX = otherRect.X;
        int maxOtherX = otherRect.X + otherRect.Width;

        int minMyX = collisionMask.X;
        int maxMyX = collisionMask.X + collisionMask.Width;

        int minOtherY = otherRect.Y;
        int maxOtherY = otherRect.Y + otherRect.Height;

        int minMyY = collisionMask.Y;
        int maxMyY = collisionMask.Y + collisionMask.Height;

        int dx, dy;

        if (maxOtherX - minMyX < maxMyX - minOtherX)
        {
            dx = (maxOtherX - minMyX);
        }
        else
        {
            dx = -(maxMyX - minOtherX);
        }

        if (maxOtherY - minMyY < maxMyY - minOtherY)
        {
            dy = (maxOtherY - minMyY);
        }
        else
        {
            dy = -(maxMyY - minOtherY);
        }

        if (Math.Abs(dx) < Math.Abs(dy))
            return new Point(dx, 0);
        else
            return new Point(0, dy);
    }

(Umarım takip etmesi kolaydır, çünkü uygulamanın daha iyi yolları olduğuna eminim ...)

isCollision (Rectangle) tam anlamıyla sadece XNA'nın Rectangle.Intersects (Rectangle) ifadesidir.

Bunu hareketli platformlarla test ettim ve iyi görünüyor. İşe yaramazsa geri bildirmek için .gif'inize benzer daha çok test yapacağım.



İnce platformlarla çalışmadığı hakkında yaptığım yorumdan şüpheliyim. Olmamasının bir sebebini düşünemiyorum. Ayrıca kaynak istiyorsanız, sizin için bir XNA projesi yükleyebilirim
Jeff

Evet, bunu sadece biraz daha deniyordum ve aynı sıkıntıya takılmaya devam ettim. Bunun nedeni, iki döşemenin birbiri ile aynı hizada olması, size en düşük olandan yukarı doğru hareket etmenizi ve daha sonra üstünüz için dışarı çıkıp, hızınız yeterince yavaşsa, yerçekimi hareketini etkin bir şekilde olumsuz etkilemenizi söylemesidir. . Bunun için bir çözüme bakmam gerekecek, daha önce bu sorunu yaşadığımı hatırlıyor gibiyim.
Jeff,

Tamam, ilk sorununuzu çözmenin son derece zorlu bir yolunu buldum. Çözüm, her AABB için minimum nüfuzu bulmayı, yalnızca en büyük X olanı çözmeyi, ardından sadece en büyük Y'yi çözen ikinci bir geçişi bulmayı içerir. Ezilirken kötü görünür, ancak işi kaldırır ve itilebilir yatay olarak ve orijinal yapıştırma probleminiz gider. Bu bir hokey ve başka şekillere nasıl dönüştüğü hakkında hiçbir fikrim yok. Başka bir olasılık dokunma geometrilerini kaynaklamak olabilir, ancak ben bunu hiç denemedim
Jeff

Duvarlar 16x16 karodan yapılmıştır. Sivri uçlar 16x16 karodur. Minimum eksende çözersem, atlama hatası oluşur (şemaya bakın). "Aşırı derecede saldırgan" çözümünüz.
Vittorio Romeo,

Evet, benim madende çalışıyor. Karakterinin boyu kaç?
Jeff,

1

Basit.

Sivri uçları yeniden yazın. Çivilerin hatası.

Çarpışmalarınız ayrı, somut birimlerde yapılmalıdır. Görebildiğim kadarıyla sorun:

1. Player is outside spikes
2. Spikes move into intersection position with the player
3. Player resolves incorrectly

Sorun adım 2 değil, 3!

Bazı şeyleri sağlam hissetmeye çalışıyorsanız, öğelerin bu şekilde birbirine kaymasına izin vermemelisiniz. Bir kez kesişme konumundaysanız, yerinizi kaybederseniz, sorunu çözmek zorlaşır.

Çiviler ideal olarak oyuncunun varlığını kontrol etmeli ve hareket ettiklerinde onu gerektiği gibi zorlamalılar .

Bunu başarmanın kolay bir yolu Oyuncu için manzarayı anlayan ve belirli bir delta ile veya bir engele çarpmadan ellerinden geldiğince zorlayacak oyuncuya moveXve moveYişlevlere sahip olmaktır .

Normalde onlar olay döngüsü tarafından çağrılır. Ancak müzikçaları itmek için nesneler tarafından da çağrılabilirler.

function spikes going up:

    move spikes up

    if intersecting player:
        d = player bottom edge + spikes top edge

        player.moveY(-d)
    end if

end function

Açıkçası oyuncu hala eğer ani tepki gerekiyor diye gider onlara .

Genel kural, herhangi bir nesne hareket ettikten sonra, kesişmeden bir konumda bitmesi gerektiğidir. Bu şekilde, gördüğünüz aksaklıkları elde etmek imkansızdır.


moveYkesişme noktasını kaldıramayan aşırı durumda (çünkü başka bir duvar da olduğu için) tekrar kesişim için kontrol edebilir ve ya moveX'i deneyebilir veya onu öldürebilirsiniz.
Chris Burt-Brown,
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.