2B haritada “anakarayı” tespit etmek için bir algoritma var mı?


28

Bu haritada "anakara" dört kardinal yönde haritanın merkezine bağlanabilen tüm arazidir (kuzey, güney, doğu, batı - diyagonal değil). görüntü tanımını buraya girin

Anakarayı tespit etmek ve içindeki delikleri doldurmak istiyorum. Üç şey düşündüm:

  1. Bir yol bulma algoritması kullanarak haritanın merkezine bağlanabilirse, her su olmayan (karanlık hücreler) hücrede arama yapın. Çok pahalı! Ancak bu adalar için işe yarayabilir.

  2. Anakara bir kova yeşil boya ile doldurulur. Her delik boyayla çevrili ... şimdi ne? Anakara içindeki her su noktasını bitişiklik açısından kontrol edersem, bazı yarımadaları ve kıyı şeridinde görüntülenen diğer coğrafi özellikleri silerim.

  3. Ana karayı bulmak için bir çeşit kenar algılama. İçinde ne varsa tutun, su varsa doldurun, dışarıda olanı çıkarın. Kompleks?

Belki bazı oyun tecrübeli geliştiriciler bu konuda bana yardımcı olabilir, muhtemelen bana bilinen bir algoritma veya tekniğin adını verir.


4
Bu haritayı oluşturmak için bir çeşit algoritma kullanıp kullanmadığınızı merak ediyorum. Ve eğer öyleyse, ne kullandın?
Jgallant

Tilebased ortamlarda çalışırken göz atmaya değer, Bando Kareleri Algoritmasıdır . Bununla beraber, daha küçük adaları da tespit edip tutabilir ve sonra tek hücreli adaları veya sahip olabileceğiniz ölçütleri dikkate alarak büyüklüğüne göre sıralayabilirsiniz.
William Mariager

@Jon evet! Bir yükseklik haritası üretmek için elmas kare algoritması, daha sonra tüm bir değer feryat su, geri kalan, karadır. Bunu kıta şekli olarak kullanmayı planlıyorum, sonra arazi detayını eklemek için başka bir paso.
Gabriel A. Zorrilla

@Mind Bando Kareleri Algoritması kıtanın sınırını boyamak için kullanışlı olacak. Teşekkürler güzel öneri!
Gabriel A. Zorrilla

Yanıtlar:


32

Adaları Kaldırma

Bu tür şeyleri daha önce oyunlarımdan birinde yaptım. Dış adalardan kurtulmak için, süreç temel olarak:

  1. Öncelikle haritanın merkezinin her zaman ana araziye ait olacağının bir garantisi olmalı ve her piksel "Arazi" veya "Su" (yani farklı renkler) olarak başlar.
  2. Ardından haritanın ortasından başlayıp herhangi bir "Arazi" döşemesine yayılan dört yönlü bir taşma dolgusu yapın . Bu sel dolgusunun ziyaret ettiği her pikseli "MainLand" gibi farklı bir tür olarak işaretleyin.
  3. Sonunda tüm haritayı gözden geçirin ve kalan diğer “Land” piksellerini diğer adalardan kurtulmak için “Su” ya dönüştürün.

Gölleri Çıkarma

Adanın içindeki deliklerden (veya göllerden) kurtulmak için, benzer bir işlem yapıyorsunuz, ancak haritanın köşelerinden başlayıp yerine "Su" çinileri arasında yayılıyor. Bu, "Deniz" i diğer su kiremitlerinden ayırmanıza izin verecek ve daha önce adalardan kurtulmuş olduğunuz gibi onlardan kurtulabileceksiniz.

Örnek

Beni Buralarda olduğunu taşkın dolgu benim uygulanmasını kazıp edelim (Emin vardır değilim bu yüzden reddi, ben, verimlilik umursamadı birçok uygulamak daha etkili yollar):

private void GenerateSea()
{
    // Initialize visited tiles list
    visited.Clear();

    // Start generating sea from the four corners
    GenerateSeaRecursive(new Point(0, 0));
    GenerateSeaRecursive(new Point(size.Width - 1, 0));
    GenerateSeaRecursive(new Point(0, size.Height - 1));
    GenerateSeaRecursive(new Point(size.Width - 1, size.Height - 1));
}

private void GenerateSeaRecursive(Point point)
{
    // End recursion if point is outside bounds
    if (!WithinBounds(point)) return;

    // End recursion if the current spot is a land
    if (tiles[point.X, point.Y].Land) return;

    // End recursion if this spot has already been visited
    if (visited.Contains(point)) return;

    // Add point to visited points list
    visited.Add(point);

    // Calculate neighboring tiles coordinates
    Point right = new Point(point.X + 1, point.Y);
    Point left = new Point(point.X - 1, point.Y);
    Point up = new Point(point.X, point.Y - 1);
    Point down = new Point(point.X, point.Y + 1);

    // Mark neighbouring tiles as Sea if they're not Land
    if (WithinBounds(right) && tiles[right.X, right.Y].Empty)
        tiles[right.X, right.Y].Sea = true;
    if (WithinBounds(left) && tiles[left.X, left.Y].Empty)
        tiles[left.X, left.Y].Sea = true;
    if (WithinBounds(up) && tiles[up.X, up.Y].Empty)
        tiles[up.X, up.Y].Sea = true;
    if (WithinBounds(down) && tiles[down.X, down.Y].Empty)
        tiles[down.X, down.Y].Sea = true;

    // Call the function recursively for the neighboring tiles
    GenerateSeaRecursive(right);
    GenerateSeaRecursive(left);
    GenerateSeaRecursive(up);
    GenerateSeaRecursive(down);
}

Bunu oyunumdaki göllerden kurtulmak için ilk adım olarak kullandım. Bunu çağırdıktan sonra tek yapmam gereken şey gibiydi:

private void RemoveLakes()
{
    // Now that sea is generated, any empty tile should be removed
    for (int j = 0; j != size.Height; j++)
        for (int i = 0; i != size.Width; i++)
            if (tiles[i, j].Empty) tiles[i, j].Land = true;
}

Düzenle

Yorumlara dayanarak bazı ek bilgiler eklemek. Arama alanınızın çok büyük olması durumunda, algoritmanın özyinelemeli sürümünü kullanırken bir yığın taşması yaşayabilirsiniz. İşte algoritma özyinelemeli olmayan bir versiyonuna istif akışına (pun amaçlı :-)) bir link ( Stack<T>bunun yerine cevabımla eşleşmek için C # dilinde), ancak diğer dillere adapte olmak kolay olmalı ve bu konuda başka uygulamalar da var. bağlantı da).


11
Merkez döşemenin her zaman kara ve ana kara parçası olacağını garanti edemezseniz, her kara kiremitini belirli bir adaya ait olarak işaretlemek için sel dolgusu kullanın (haritada her blok kimliğine sahip olmayan tüm kiremitleri doldurun, dolgulu kiremitleri işaretleyin Zaten bir tane değilse, aynı blobid). Sonra en çini ile blob hariç hepsini kaldırın.
George Duckett,

GeorgeDuckett'in söylediklerine uygun olarak - Googling blob algılama algoritmalarını deneyebilirsiniz: FTIR kullanarak çoklu dokunuşta sık karşılaşılan bir problemdir. Daha akıllı bir algoritma bulduğumu hatırlıyorum, ama nasıl çalıştığını hayatım boyunca hatırlayamıyorum.
Jonathan Dickinson

PHP'de yığın sorunları yaşadığım için LIFO taşma dolgusunu uyguladım, harika bir şekilde çalıştı.
Gabriel A. Zorrilla

Yalnızca bir DFS algoritması BFS algoritmasından çağrıldığında taşkın dolgusu değil midir? Lütfen birisini açıkla.
jcora

@Bane Ne demek istiyorsun? DFS veya BFS arasındaki fark, yalnızca düğümlerin ziyaret edildiği sıradır. Taşma dolgusu algoritmasının geçiş sırasını belirlediğini sanmıyorum - tüm bölgeleri düğümleri tekrar ziyaret etmeden doldurması umrunda değil. Sipariş uygulamaya bağlıdır. Bir yığın kullanırken bir sıra kullanırken sıra geçiş sırasını karşılaştıran bir resim için wikipedia girişinin en altına bakın . Özyinelemeli uygulama da bir yığın olarak kabul edilebilir (çağrı yığınını kullandığı için).
David Gouveia


5

Bu, görüntü işlemede standart bir işlemdir. İki aşamalı bir işlem kullanıyorsunuz.

Haritanın bir kopyasını oluşturarak başlayın. Bu haritadan, denizi çevreleyen tüm karasal pikselleri deniz piksellerine dönüştürün. Bunu bir kez yaparsanız, 2x2 adaları ortadan kaldıracak ve daha büyük adaları daraltacaktır. İki kez yaparsanız, 4x4 adaları vb.

İkinci aşamada, neredeyse tam tersini yaparsınız: toprağı sınırlayan tüm deniz piksellerini kara piksellerine dönüştürürsünüz, ancak yalnızca orijinal haritadaki kara pikselleri olsaydı (bu yüzden 1. aşamada bir kopya yaptınız). Bu, 1. aşamada tamamen elimine edilmedikleri sürece, adaları orijinal biçimlerine dönüştürür.


1

Benzer bir sorun vardı ama oyun geliştirmedim. Birbirine bitişik ve aynı değerde (bağlı bölgeler) olan bir görüntüdeki pikselleri bulmak zorundaydım. Özyinelemeli bir taşma dolgusu kullanmayı denedim, ancak yığın taşmalarına neden olmaya devam ettim (Acemi bir programcım: P). Sonra bu yöntemi denedim http://en.wikipedia.org/wiki/Connected-component_labeling zaten benim sorunum için aslında çok daha etkiliydi.


Taşkın algosunun yığın sürümünü denemeli ve yığın problemlerini kurtarmalıydınız. CCL'den çok daha basit olduğu sonucuna varıldı (son 2 haftadır şanssız çalışıyorum.)
Gabriel A. Zorrilla

Evet, ikisini de denedim, ama önce CCL'yi işe aldım: P ve temiz bir fikir olduğunu düşündüm. Sorununuzu çözdüğünüze sevindim :)
Elegant_Cow
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.