Önceden hesaplanmış yol bulma hala ilgili mi?


12

bağlam

Old Lucas Arts (ScummVM dönemi) gelin ve tıklayın grafik macera oyunları önceden hesaplanmış yol bulma kullanılır. İşte tekniğin kaba bir taslağı.

Aşama 1

Her odadaki zemin, bir gezinti ağındaki düğümlere hemen hemen eşdeğer olan ancak yamuk şekillerle sınırlı olan "yürüyüş kutuları" olarak adlandırıldıklarına ayrıldı. Örneğin:

 ______ _____ _________ _____
\   A  |  B  |    C    |  D   \
 \_____|     |         |_______\
       |_____|         |
             |_________|

Adım 2

Çevrimdışı bir algoritma (örneğin, Dijkstra veya A *), her bir düğüm çifti arasındaki en kısa yolu hesaplar ve yolun ilk adımını, kullanılan başlangıç ​​ve bitiş düğümü tarafından her boyutta dizine alınmış bir 2D matriste depolar . Yukarıdaki yürüyüş kutularını kullanarak:

      ___ ___ ___ ___
     | A | B | C | D | <- Start Node
  ___|___|___|___|___|
 | A | A | A | B | C |  ---
 |___|___|___|___|___|     |
 | B | B | B | B | C |     |
 |___|___|___|___|___|     |-- Next node in shortest path
 | C | B | C | C | C |     |   from Start to End
 |___|___|___|___|___|     | 
 | D | B | C | D | D |  ---
 |___|___|___|___|___| 
   ^
   |
End Node

Tahmin edebileceğiniz gibi, düğüm sayısı arttıkça bellek gereksinimleri hızla artar (N ^ 2). Bir kısa, genellikle her girişi matriste depolayacak kadar büyük olacağından, 300 düğümden oluşan karmaşık bir harita ile ekstra depolamaya neden olur:

300^2 * sizeof(short) = 176 kilobytes

Aşama 3

Öte yandan, iki düğüm arasındaki en kısa yolu hesaplamak son derece hızlı ve önemsizdi, sadece matrise bir dizi arama yapıldı. Gibi bir şey:

// Find shortest path from Start to End
Path = {Start}
Current = Start
WHILE Current != End
    Current = LookUp[Current, End]
    Path.Add(Current)
ENDWHILE

C'den A'ya en kısa yolu bulmak için bu basit algoritmayı uygulamak:

1) Path = { C }, Current = C
2) Path = { C, B }, Current = B
3) Path = { C, B, A }, Current = A, Exit

Soru

Bugünün güçlü donanımı ile, bunu her seviye için yapmanın bellek gereksinimleriyle birlikte, bu tekniğin bir zamanlar sahip olduğu tüm avantajların artık çalışma zamanında bir A * gerçekleştirerek daha ağır bastığından şüpheleniyorum.

Ayrıca, günümüzde bellek aramalarının genel hesaplamadan bile daha yavaş olabileceğini duydum, bu yüzden sinüs ve kosinüs arama tabloları oluşturmak artık popüler değil.

Ama itiraf etmeliyim ki, bu düşük seviyeli donanım verimliliği konularında henüz çok bilgili değilim, bu yüzden bu konuyu daha iyi tanıyanların görüşlerini sormak için kullanıyorum.

Motorumda, çalışma zamanında grafiğe dinamik olarak düğüm ekleme ve kaldırma yeteneğine de ihtiyacım vardı (buna bakın ), böylece önceden hesaplanmış yol sadece işleri daha karmaşık hale getirdi, bu yüzden onu hurdaya ayırdım (çalışma zamanı A * çözümümün zaten mükemmel çalıştığını belirtmiyorum) ). Yine de merak ediyordum ...

Sonuç olarak , bu teknik günümüzde hala herhangi bir senaryoda geçerli mi?


2
Sıkı bir CPU bütçesinde olmanızın hala geçerli olduğunu düşünüyorum. Ancak dinamik yollar istediğinizde artık kullanışlı değildir. Btw A * algoritmanızı nereden bulduğunuza baktım ve bir minheap ve diğer hileler kullanarak daha da optimize edebilirsiniz. Burada görebileceğiniz C # 'da A *' geliştirmenin birkaç yinelemesini yaptım: roy-t.nl/index.php/2011/09/24/… faydalı olabilir.
Roy T.

1
Teşekkürler, yer imi koydum ve uygulamamı optimize etmeye başladığımda içine bakacağım. Eric Lippert'in çözümünü birkaç küçük modifikasyonla kullandım çünkü takip etmek çok temiz ve kolaydı ... Ve tüm test vakalarım için hemen hemen "anında" koştu, bu yüzden optimize etmekle bile uğraşmadım.
David Gouveia

1
BTW ön hesaplama yapmaya karar verirseniz, Floyd-Warshall algoritmasına bakmak isteyebilirsiniz . “Bir sonraki adım” matrisini Dijkstra / A * kullanarak tekrar tekrar olduğundan daha verimli bir şekilde oluşturur.
amitp

@amitp Bahşiş için teşekkürler, bu alternatifleri bilmek her zaman iyidir! Çoğu durumda ön hesaplama çevrimdışı yapılacağından, daha verimli hale getirmekten kazanılacak çok fazla bir şey olmayacaktır. Gerçekten sabırsız değilseniz. :-)
David Gouveia

Kabul etti, Floyd-Warshall'ın uygulanması Dijkstra'nın algoritmasından çok daha basit olmasına rağmen, Dijkstra'nın uygulanmadıysanız, bir göz atmaya değer :)
amitp

Yanıtlar:


3

Motorumda, çalışma zamanında grafiğe dinamik olarak düğüm ekleme ve kaldırma yeteneğine de ihtiyacım vardı (buna bakın), böylece önceden hesaplanmış yol sadece işleri daha karmaşık hale getirdi, bu yüzden onu hurdaya ayırdım (çalışma zamanı A * çözümümün zaten mükemmel çalıştığını belirtmiyorum) ). Yine de merak ediyordum ...

Sonuç olarak, bu teknik günümüzde hala herhangi bir senaryoda geçerli mi?

Böyle bir tekniği kullanmaktan fayda göremiyorum.

Bir Grafiğin esnekliğinden yoksundur (farklı LOD'larınız olabilir, herhangi bir belirli şekil olması gerekmez, vb ...). Ayrıca motorunuzun herhangi bir kullanıcısı bir grafiğin ne olduğunu ve nasıl kullanılacağını bilecektir. Ekstra işlevsellik eklemek istiyorlarsa, uzantılarını kendilerine tamamen yeni bir durum kullanarak nasıl uygulayacaklarını öğrenmek zorunda kalacaklar.

Bahsettiğiniz gibi korkunç bir şekilde ölçeklendirilmiş gibi görünüyor. Ayrıca bir grafiğin nakit paraya uyması ve tüm yol bulgularınızı arka arkaya çalıştırmanızın gerçekten de IO zamanını azalttığını belirtmek gerekir. Görünüşe göre uygulama yakında herhangi bir önbelleğe sığmayacak kadar büyüyecek.

Ayrıca, günümüzde bellek aramalarının genel hesaplamadan bile daha yavaş olabileceğini duydum, bu yüzden sinüs ve kosinüs arama tabloları oluşturmak artık popüler değil.

Tüm programınızı ve ihtiyaç duyduğu belleği önbelleğe sığmayacak olmadıkça, işlemciyi şişeye almadan önce bir şeyleri bellek içine ve dışına çekerken şişe boynuna gidersiniz.

Bugünün güçlü donanımı ile, bunu her seviye için yapmanın bellek gereksinimleriyle birlikte, bu tekniğin bir zamanlar sahip olduğu tüm avantajların artık çalışma zamanında bir A * gerçekleştirerek daha ağır bastığından şüpheleniyorum.

Ayrıca, birçok oyunun AI'yi güncellemek için ayrı döngülere sahip olduğunu unutmayın. Projemin kurulduğu yolun 60hz'de kullanıcı girişi için bir güncelleme döngüsü olması, AI'nin sadece 20hz olması ve oyunların mümkün olduğunca hızlı bir şekilde çizilmesi olduğuna inanıyorum.

Ayrıca bir yan not olarak sadece eğlence için bazı GBA programlama yaptım ve hiçbir şey modern bir cihaz kullanmaya geçmiyor. GBA için her şey işlemcinin iş yükünü en aza indirmekle ilgiliydi (zavallı olduğu için). Ayrıca çoğu üst düzey dilin C # ve Java'nın (çok fazla C ++ veya C değil) sizin için tonlarca optimizasyon yaptığını fark etmelisiniz. En iyi duruma getirmeye gelince, belleğe erişimden olabildiğince az bir şey yapmaktan başka bir şey yapmazsınız ve önbellekten çıkacak yeni bir bellek getirmeden ve sadece bir kez bir şeyler yapıyor.

Düzenleme: Ayrıca başlığınıza cevap evet. Sık kullanılan yolları önceden hesaplamak mükemmel bir fikirdir ve oyun döngünüzün dışındaki herhangi bir yerde A * ile yapılabilir. Örneğin, bir RTS'deki bir kaynağa dayanırsınız, böylece toplar her ayrılmak veya geri dönmek istediklerinde yeniden hesaplamak zorunda kalmazlar.


Düzenlemeniz hakkında, sıkça kullanılan yolların önceden hesaplanmasından değil, kesinlikle olası her yolun önceden hesaplanmasında belirtilen teknikten bahsetmiştim . Ayrıca cevabınızın çoğunun önceden hesaplanmış yol bulmayı kullanmaya karşı olduğu konusunda biraz kafam karıştı, ama sonunda bunun mükemmel bir fikir olacağını söylediniz. Peki, GBA gibi CPU kısıtlamalı bir ortamda yararlı olur mu?
David Gouveia

1
Benim hatam, içeriğinizin bağlamdan çıkarılan cevabının evet olduğunu belirtmeye çalışıyordum. Sorunuzda açıklanan belirli algoritmaya ilişkin cevap hayır. Kısacası, olası tüm yolların önceden hesaplanması kötü bir fikirdir, ancak çok sık kullanılan birkaç yolun önceden hesaplanması iyi bir fikir olabilir.
ClassicThunder

2
@ClassicThunder: Birkaç yer işaretinden tüm yolları önceden hesaplama tekniğine genellikle ALT denir : Yer işaretleri ve üçgen eşitsizliği olan bir yıldız : cs.princeton.edu/courses/archive/spr06/cos423/Handouts/GW05. pdf
Pieter Geerkens
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.