Koridor ve oda bağımlılığı olmayan zindan üretimi


15

Oyunun başında oluşturulan, ızgaralarla temsil edilen birkaç alandan oluşan (8x8, 9x6, boyutlar ideal olarak keyfi olurdu) prosedürel olarak oluşturulmuş bir dünyayla bir oyun yapıyorum. Bu alanların bir bağımlılık listesi aracılığıyla birbirine bağlanması gerekiyor.

Bu ızgaranın en az 3 alanı bu iki alan arasında açığa çıktığında bir bağlantı vardır. Bu 3 boşluk bağlantı alanının orta hücresinde alanlar arasındaki giriş kapısı bulunur:

Onları birleştirmenin bir yolunu bulmaya çalışıyorum, ancak aynı anda dikkate almanız gereken daha fazla alan giderek daha karmaşık hale geliyor.

Bazı kağıt prototipleme denedim ve görsel olarak yaparken çok basit bir süreç olsa da, ben aynı kod ile oda yerleştirmek için izin veren iyi bir matematiksel ifadeler seti bulamadım.

İşte şu anda mücadele ettiğim "basit" bir örnek:

  • 'A' alanının 'b' ve 'c' ile bağlantılı olması gerekir
  • 'B' alanının 'a' ve 'd' ile bağlantılı olması gerekir
  • 'C' alanının 'a' ve 'd' ile bağlantılı olması gerekir
  • 'D' alanının 'b' ve 'c' ile bağlantılı olması gerekir

Basitlik açısından, odaları listedeki görünüm sırasına göre yerleştiriyoruz (başkalarını denedim). Bu yüzden standart prosedürel Dungeon Generation algoritmanız olarak yaklaşıyorum.

İlk alan olduğu için tahtada herhangi bir yere 'a' yerleştiriyoruz. Sonra, rastgele bir duvar seçiyoruz ve o duvara hiçbir şey bağlı olmadığından, oraya 'b' yerleştirebiliriz:

Şimdi 'c' koymalıyız, ama 'a' zaten tahtada ve işgal edilmiş bir duvarı var, bu yüzden başka bir duvara koymaya karar veriyoruz. Ancak her yerleşim gerçekleşmeyecek, çünkü 'd' geliyor ve 'b' ve 'c' ile de bağlanması gerekiyor:

Aynı bağımlılık kümesine sahip 2 odanın karşı duvarlarda olamayacağı, ancak bu bile başarıyı garanti etmeyecek olası bir sınırlama denedim:

Ve alanların farklı boyutlara sahip olduğu diğer durumlarda, karşıt duvarlarda olmak işe yarayabilir:

Ayrıca, kullanılan bir duvarı düşünmemek, geçerli çözümleri dışladığı için kusurlu bir varsayımdır:

Optimal Dikdörtgen Paketleme ve Grafik Düzeni algoritmaları gibi diğer Prosedür Üretimi algoritmaları veya benzeri araştırmalar yapmaya çalıştım, ancak genellikle bu algoritmalar bu sorunun her kısıtlamasını dikkate almaz ve birlikte karıştırılması zordur.

Uygun bir yerleşim bulunana kadar bir alan ve backtrack yerleştirmeyi de içeren bir dizi yaklaşımı düşündüm, ancak deneme ve yanılmaya çok bağlı ve hesaplama açısından maliyetli görünüyorlar. Ancak, bahsettiğim son iki sorun hakkında kapsamlı bir araştırma yapıldığında, bu tek / en iyi çözüm olabilir mi?

Birisinin geçmişte benzer problemleri olup olmadığını veya algoritma ile nereden başlamam gerektiğine dair birkaç ipucu vermeme yardımcı olup olmadığını görmek istedim. Ya da başarısız olursa, belirlediğim kısıtlamaları gevşetmek zorunda kalacağım.


Odalar tamamen kare olmalı?
AturSams

Eğer 4 duvarları olması gerekiyorsa ve o zaman evet değil, yani dünya uzayını basitleştirmek için yaptım. Her alanın kapladığı alanı kolayca hesaplamam gerekiyor, bu yüzden istediğim her şeyi koyabileceğimi biliyorum.
Joana Almeida

Yanıtlar:


6

Bu harika bir problem. Oda yerleşimleri alanında eylem planlaması kullanılarak çözülebileceğine inanıyorum.

Define Devlet aşağıdaki gibi dünyanın:

//State:
//    A list of room states.
//    Room state:
//      - Does room exist?
//      - Where is room's location?
//      - What is the room's size?

Bir Kısıtlamayı şu şekilde tanımlayın :

 // Constraint(<1>, <2>):
 //  - If room <1> and <2> exist, Room <1> is adjacent to Room <2>

"Bitişik" ifadesinin açıklandığı gibi olduğu yerde (en az 3 komşuyu paylaşarak)

İki oda bitişik olmadığında ve her iki oda da mevcut olduğunda bir Sınırlamanın geçersiz olduğu söylenir .

Aşağıdaki durumlarda geçerli olacak bir Devlet tanımlayın :

// foreach Constraint:
//        The Constraint is "not invalidated".
// foreach Room:
//       The room does not intersect another room.

Bir Eylemi , mevcut bir Devlet verildiğinde bir odanın yerleşimi olarak tanımlayın . Eylem ise geçerli eylem kaynaklanan devlet geçerli olduğunda. Bu nedenle, her durum için bir eylem listesi oluşturabiliriz:

// Returns a list of valid actions from the current state
function List<Action> GetValidActions(State current, List<Constraint> constraints):
    List<Action> actions = new List<Action>();
    // For each non-existent room..
    foreach Room room in current.Rooms:
        if(!room.Exists)
            // Try to put it at every possible location
            foreach Position position in Dungeon:
                 State next = current.PlaceRoom(room, position)
                 // If the next state is valid, we add that action to the list.
                 if(next.IsValid(constraints))
                     actions.Add(new Action(room, position));

Şimdi, bıraktığınız şey , Devletlerin düğüm olduğu ve İşlemlerin bağlantı olduğu bir grafik . Hedefi bulmaktır Devleti olan hem geçerli, ve tüm odalar yerleştirilmiştir. Belki derinlemesine bir arama kullanarak, grafikte gelişigüzel bir şekilde arama yaparak geçerli bir yerleşim bulabiliriz. Arama şöyle görünecektir:

// Given a start state (with all rooms set to *not exist*), and a set of
// constraints, finds a valid end state where all the constraints are met,
// using a depth-first search.
// Notice that this gives you the power to pre-define the starting conditions
// of the search, to for instance define some key areas of your dungeon by hand.
function State GetFinalState(State start, List<Constraint> constraints)
    Stack<State> stateStack = new Stack<State>();
    State current = start;
    stateStack.push(start);
    while not stateStack.IsEmpty():
        current = stateStack.pop();
        // Consider a new state to expand from.
        if not current.checked:
            current.checked = true;
            // If the state meets all the constraints, we found a solution!
            if(current.IsValid(constraints) and current.AllRoomsExist()):
                return current;

            // Otherwise, get all the valid actions
            List<Action> actions = GetValidActions(current, constraints);

            // Add the resulting state to the stack.
            foreach Action action in actions:
                State next = current.PlaceRoom(action.room, action.position);
                stateStack.push(next);

    // If the stack is completely empty, there is no solution!
    return NO_SOLUTION;

Şimdi üretilen zindanın kalitesi , odaların ve eylemlerin dikkate alınma sırasına bağlı olacaktır. Muhtemelen her aşamada gerçekleştirdiğiniz eylemlere rastgele izin vererek ve böylece durum-eylem grafiğinde rastgele bir yürüyüş yaparak ilginç ve farklı sonuçlar elde edebilirsiniz . Arama verimliliği büyük ölçüde geçersiz durumları ne kadar hızlı reddedebileceğinize bağlı olacaktır. Geçerli eylemleri bulmak istediğinizde, kısıtlamalardan geçerli durumlar oluşturmak yardımcı olabilir .


Komik bu çözümden bahsetmelisin. Daha önce bir arkadaşımla konuştum ve muhtemelen Ağaç Tabanlı Arama algoritmalarına bakmam gerektiğini söyledi, ancak bunları bu bağlamda nasıl kullanacağımdan emin değildim. Gönderiniz göz açıyor! Şube üretimini yönetirseniz ve kötü şubeleri en kısa zamanda kesmek için birkaç optimizasyon yaparsanız kesinlikle uygun bir çözüm gibi görünüyor.
Joana Almeida

7

Üretim öncelikleriniz birbiriyle çelişiyor. Seviyeler oluştururken, ilk hedefiniz düzlemsel (örtüşmeyen), bağlantılı noktalar ağı olmalıdır ölçeğe bakılmaksızın . Ardından bu web içindeki noktalardan odalar oluşturmaya devam edin. Öncelikle oda şekilleri oluşturmak, genel olarak konuşursak bir hatadır. Önce bağlantı oluşturun ve ardından bu odaya hangi oda formlarının yerleştirilebileceğini görün.

Genel Algoritma

  1. Bir 2D dizi veya görüntü kullanarak seviyenizi desteklemek için yeterli büyüklükte nicelikli bir zemin ızgarası oluşturun.

  2. Bu boş zemin üzerinde rastgele rastgele dağılım. Bir nokta alıp almadığını görmek için her döşemede düz bir rastgele kontrol kullanabilir veya noktaları dağıtmak için standart / Gauss dağılımını kullanabilirsiniz. Her noktaya benzersiz bir renk / sayısal değer atayın. Bunlar kimlikler. (PS Bu adımdan sonra alanınızı büyütmeniz gerektiğini düşünüyorsanız, kesinlikle yapın.)

  3. Bu tür her oluşturulan noktası için, sırayla, aşamalı bir sınır daire büyümesi veya sınırları (tipik olarak 0.5-1.0 hücrelerinin adım başına / piksel bir oranda) tek bir aşamada tarafından dikdörtgen xve y. Tüm sınırları aynı adımda sıfır boyutundan başlayarak paralel olarak büyütebilirsiniz veya farklı zamanlarda ve farklı oranlarda büyütmeye başlayarak, daha erken başlayanların boyutuna önyargı verebilirsiniz (fidelerin büyüdüğünü hayal edin, burada geç başlayınız). "Büyümek" ile kastedilen yeni artan sınırları bu sınırların başlangıç ​​noktasına özgü renk / kimlikle doldurmaktır. Bunun için bir metafor, kalemleri bir kağıdın arkasına tutmak ve farklı renklere sahip mürekkep lekelerinin buluşana kadar büyümesini izlemek olacaktır.

  4. Bir noktada büyüme aşaması sırasında bir noktanın ve başka bir noktanın sınırları çarpışacaktır. Bu, bu iki nokta için sınırları büyütmeyi bırakmanız gereken noktadır - en azından 3. adımda açıklanan tekdüze anlamda.

  5. Tüm puanların sınırlarını mümkün olduğunca büyütüp tüm büyüme süreçlerini durdurduktan sonra, büyük ölçüde olması gereken, ancak tamamen doldurulmamış bir haritanız olacak. Artık beyaz olduğunu farz edeceğim boş alanı bir kağıtta renklendiriyormuş gibi paketlemek isteyebilirsiniz.

İşlem sonrası boşluk doldurma

5. adım başına kalan boş / beyaz boşlukları doldurmak için çeşitli teknikler kullanılabilir:

  • Tek bir komşu, zaten renkli bir alanın alanı doldurarak, bu rengi doldurarak tümüyle birleşmesini sağlayın.
  • Tamamen yeni alanlar oluşturacak şekilde yeni, henüz kullanılmamış renkler / sayılar / kimliklerle taşkın.
  • Yuvarlak robin yaklaşımı, zaten dolu olan her bir komşu alanın boş alana biraz "büyür". Bir sulama deliğinin etrafında içki içen hayvanları düşünün: hepsinde su var.
  • Boş alanı tamamen doldurmayın, sadece düz geçişler kullanarak mevcut alanları bağlamak için çaprazlayın.

tedirginlik

İşlerin daha organik görünmesini sağlamak için son bir adım olarak, alanların kenar hücrelerinde değişen derecelerde kenar pertürbasyonu yapabilirsiniz. Sadece önemli hareket yollarını engellemediğinizden emin olun.

İlgi Alanı için Teori

Bu, Voronoi Diagrams / Delaunay Triangulation'da alınan yaklaşıma benzer , ancak yukarıda açıkça açıkça kenarlar oluşturmuyorsunuz - bunun yerine, sınırlayıcı alanlar çarpıştığında büyüme durur. Voronoi Diyagramlarının boşluk doldurduğunu fark edeceksiniz; bunun nedeni, sadece dokunma ile değil, aynı zamanda bir dereceye kadar nominal çakışma ile büyümeyi durdurmalarıdır. Benzer deneyebilirsiniz.

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.