İzometrik spriteları doğru sıraya göre nasıl sıralayabilirim?


19

Normal bir yukarıdan aşağıya 2D oyunda görüntüleri sıralamak için ekran y ekseni kullanılabilir. Bu örnekte ağaçlar uygun şekilde sıralanmıştır ancak izometrik duvarlar sıralanmamıştır:

Örnek resim: y ekranına göre sıralanmış

Duvar 2 duvar 1'in altında bir pikseldir, bu nedenle duvar 1'den sonra çizilir ve üste doğru biter.

İzometrik y eksenine göre sıralarsam, duvarlar doğru sırada görünür, ancak ağaçlar olmaz:

Örnek resim: izometrik y'ye göre sıralanmış

Bunu nasıl doğru bir şekilde yapabilirim?


3
Görünüşe göre Ressam Algoritması'nda sıkça karşılaştığınız sorunlara değindiniz. "Ortak" çözüm z-buffer =) kullanmaktır. Bazı geçici çözümler nesne merkezini köşe yerine sıralama anahtarı olarak kullanmayı içerir.
Jari Komppa

Yanıtlar:


12

İzometrik oyunlar işlevsel olarak 3D'dir, bu nedenle dahili olarak, oyundaki her varlık için 3D koordinatlarını saklamalısınız. Seçtiğiniz gerçek koordinatlar keyfi, ancak diyelim ki X ve Y, yerdeki iki eksen ve Z yerden havaya doğru.

Oluşturucunun daha sonra ekranda bir şeyler çizmek için bunu 2B'ye yansıtması gerekir. "İzometrik" böyle bir projeksiyon. 3D'den 2D'ye izometrik olarak yansıtmak oldukça basittir. Diyelim ki X ekseni her iki yatay piksel için bir soldan sağa ve bir piksel aşağı gidiyor. Benzer şekilde, Y ekseni sağdan sağa sola gider. Z ekseni yukarı doğru gider. 3D'den 2D'ye dönüştürmek için:

function projectIso(x, y, z) {
    return {
        x: x - y,
        y: (x / 2) + (y / 2) - z
    };
}

Şimdi orijinal sorunuza göre sıralama. Artık nesnelerimizle doğrudan 3D olarak çalıştığımıza göre sıralama çok daha kolay hale geliyor. Buradaki koordinat alanımızda, en uzak hareketli grafik en düşük x, y ve z koordinatlarına sahiptir (yani her üç eksen de ekrandan işaret eder). Yani onları sadece bunların toplamına göre sıralıyorsunuz:

function nearness(obj) {
    return obj.x + obj.y + obj.z;
}

function closer(a, b) {
    if (nearness(a) > nearness(b)) {
        return "a";
    } else {
        return "b";
    }
}

Varlıklarınızı her karede yeniden sıralamaktan kaçınmak için, burada ayrıntılı bir güvercin deliği türü kullanın .


1
Bunun eski olduğunu biliyorum, iyi bir başlangıç ​​ama sadece çevirisini değil, bir nesnenin 3B sınırlarını nasıl dahil edebileceğinizi biliyor musunuz? Üst üste geldiklerinde derinlik sıralaması daha karmaşık hale gelir.
Rob

4

Spritelarınızın dikdörtgenler olan fayans kümeleri içerdiğini varsayarsak (rastgele kümeler işgal ederse, genel durumda doğru şekilde çizemezsiniz), sorun, öğeler arasında toplam sipariş ilişkisi olmamasıdır. O (nlogn) karşılaştırmalarıyla sonuçlanacak bir sıralama kullanır.

Herhangi iki A ve B nesnesi için A'nın B (A <- B) 'den önce çizilmesi gerektiğini, B'nin A (B <- A)' dan önce çizilmesi gerektiğini veya herhangi bir sırada çizilebileceğini unutmayın. Kısmi bir düzen oluştururlar. Kendinizi üst üste gelen 3 nesne ile birkaç örnek çizerseniz, 1. ve 3. nesnelerin çakışmamasına rağmen, doğrudan bir bağımlılığa sahip olmasa bile, çizim sıralarının aralarındaki 2. nesneye bağlı olduğunu fark edebilirsiniz. yerleştirirseniz, farklı çizim siparişleri alırsınız. Özetle - geleneksel türler burada işe yaramıyor.

Bir çözüm (Dani tarafından belirtilen) karşılaştırmayı kullanmak ve bağımlılıklarını belirlemek ve bir bağımlılık grafiği (bir DAG olacak) oluşturmak için her nesneyi birbiriyle karşılaştırmaktır. Ardından çizim sırasını belirlemek için grafik üzerinde topolojik bir sıralama yapın. Çok fazla nesne yoksa, bu yeterince hızlı olabilir O(n^2).

Başka bir çözüm (dengeleme için - sözde ) dört ağaç kullanmak ve tüm nesnelerin dikdörtgenlerini içine saklamaktır.

Daha sonra tüm X nesnelerini tekrarlayın ve dört ağacını kullanarak X nesnesinin üzerindeki şeritte X nesnesinin en soluyla başlayan ve X nesnesinin en sağ köşesiyle biten - Y gibi tüm Y, Y < - X. Bunun gibi, yine de bir grafik oluşturmalı ve topolojik olarak sıralamalısınız.

Ama bundan kaçınabilirsiniz. Q nesnelerinin bir listesini ve T nesnelerinin bir tablosunu kullanırsınız. X eksenindeki (bir satır) daha küçük olandan daha büyük değerlere kadar tüm görünür yuvaları yineleyerek y ekseninde satır satır ilerlersiniz. Bu yuvada bir nesnenin alt köşesi varsa, bağımlılıkları belirlemek için yukarıdaki prosedürü uygulayın. X nesnesi, kısmen üstünde olan başka bir Y nesnesine (Y <- X) bağlıysa ve bu Y'nin her biri zaten Q'da ise, X'i Q'ya ekleyin. Q'da olmayan bir Y varsa, X'i T ve Y <- X olduğunu belirtin. Q'ya her nesne eklediğinizde, T'de bekleyen nesnelerin bağımlılıklarını kaldırırsınız. Tüm bağımlılıklar kaldırılırsa, T'deki bir nesne Q'ya taşınır.

Nesne spritelarının alt, sol veya sağdaki yuvalarından dışarı bakmadığını varsayıyoruz (sadece üstte, resminizdeki ağaçlar gibi). Bu, çok sayıda nesne için performansı iyileştirmelidir. Bu yaklaşım yine olacaktır O(n^2), ancak sadece garip boyutlu nesneler ve / veya nesnelerin garip konfigürasyonlarını içeren en kötü durumda olacaktır. Çoğu durumda, öyle O(n * logn * sqrt(n)). Spritelarınızın yüksekliğini bilmek, ortadan kaldırabilir sqrt(n), çünkü yukarıdaki tüm şeridi kontrol etmeniz gerekmez. Ekrandaki nesne sayısına bağlı olarak, dörtlü ağacı hangi yuvaların çekildiğini belirten bir dizi ile değiştirmeyi deneyebilirsiniz (çok sayıda nesne varsa mantıklıdır).

Son olarak, bu kaynak kodunu bazı fikirler için incelemekten çekinmeyin: https://github.com/axel22/sages/blob/master/src/gui/scala/name/brijest/sages/gui/Canvas.scala


2

Matematiksel bir çözüm olduğunu düşünmüyorum. Muhtemelen 2B dünyasında eşyalarınızın yaşadığı kadar yeterli veriye sahip değilsiniz. Duvarlarınız X üzerinde yansıtılmış olsaydı "doğru" sırayla olurdu. Daha sonra, bir şekilde görüntünün sınırlayıcı kutusuyla çakışma testi yapabilirsiniz, ancak bu aşina olmadığım bir alandır.

Döşeme başına ekran Y'ye göre sıralamanız ve daha karmaşık bir şeyin "tasarım sorunu" olduğunu söylemelisiniz. Örneğin, içeriği yazıyorsanız, tasarımcılarınıza sıralama algoritmasını söyleyin ve sorunu gidermek için wall2 2 piksel yukarı itmeniz yeterlidir. Üzerinde çalıştığım izometrik oyunda bunu düzeltmek zorunda kaldık. Bu, "uzun" öğeleri alıp bunları karo boyutlu parçalara ayırmayı içerebilir.

Kullanıcıların içeriği düzenlemesine izin veriyorsanız, yapılacak en güvenli şey her şeyi döşeme tabanlı ve en fazla bir döşeme büyük yapmaktır. Bu şekilde sorunu önlersiniz. Her şeyi bir karodan daha büyük hale getirerek elde edebilirsiniz, ancak muhtemelen sadece kare ise. Bununla oynamadım.


2

Mükemmel izometrik sıralama zordur. Ancak ilk yaklaşımda, öğelerinizi doğru bir şekilde sıralamak için, üst üste binen her nesne ile daha karmaşık bir karşılaştırma işlevi kullanmalısınız. Bu işlev bu koşulu kontrol etmelidir: Üst üste gelen bir "a" nesnesi "b" nin arkasındaysa:

(a.posX + a.sizeX <= b.posX) veya (a.posY + a.sizeY <= b.posY) veya (a.posZ + a.sizeZ <= b.posZ)

Tabii ki, bu naif bir izometrik uygulama için ilk fikirdir. Daha karmaşık bir senaryo için (görünüm döndürme, z ekseni üzerindeki piksel başına konumlar vb.) Daha fazla koşul kontrol etmeniz gerekir.


+1, farklı genişliklere / yüksekliklere sahip karolarınız varsa, bu cevaptaki karşılaştırma işlevine bakın.
mucaho

2

OpenGL'deki küçük izometrik motorumla iyi çalışan çok basit bir formül kullanıyorum:

Nesnelerinizin her biri (ağaçlar, yer karoları, karakterler, ...) ekranda X ve Y konumuna sahiptir. DERİNLİK TESTİNİ etkinleştirmeniz ve her değer için iyi Z değerini bulmanız gerekir. Basitçe aşağıdakileri yapabilirsiniz:

z = x + y + objectIndex

Zemini ve zeminin üzerinde olacak nesneler için farklı indeks kullanıyorum (zemin için 0 ve yüksekliği olan tüm nesneler için 1). Bu işe yarayacaktır.



1

Aynı x konumunda y değerlerini karşılaştırırsanız, her seferinde çalışır. Yani, merkezi merkezle karşılaştırmayın. Bunun yerine, bir hareketli grafiğin merkezini diğer hareketli hareketli grafiğin aynı x konumu ile karşılaştırın.

resim açıklamasını buraya girin

Ancak bu, her hareketli grafik için bazı geometri verileri gerektirir. Hareketli grafiğin alt sınırını tanımlayan soldan sağa iki nokta kadar kolay olabilir. Veya sprite görüntü verilerini analiz edebilir ve saydam olmayan ilk pikseli bulabilirsiniz.

Daha kolay bir yaklaşım tüm spriteları üç gruba ayırmaktır: x ekseni boyunca diyagonal, y ekseni boyunca diyagonal ve düz. İki nesnenin ikisi de aynı eksen boyunca diyagonal ise, bunları diğer eksene göre sıralayın.


0

Aynı türdeki tüm nesnelere benzersiz kimlikler atamanız gerekir. Sonra tüm nesneleri konumlarına göre sıralarsınız ve nesne gruplarını kimliklerine göre çizersiniz. Böylece A grubundaki nesne 1, A grubundaki vb. Nesne 2'yi asla geri çekmez.


0

Bu gerçekten bir cevap değil, sadece bir yorum ve bu oylamada bu axel22'nin cevabına vermek istediğim bir oylama /gamedev//a/8181/112940

Yukarı oy vermek veya diğer cevapları yorumlamak için yeterli üne sahip değilim, ancak cevabının ikinci paragrafı, "modern" 3D'ye dayanmadan izometrik bir oyunda varlıkları sıralamaya çalışırken farkında olması gereken en önemli şeydir. Z-tamponu gibi teknikler.

Motorumda "eski okul", saf 2D şeyler yapmak istedim. Ve neden "sıralama" (benim durumumda c ++ std :: sort) çağrımın belirli harita yapılandırmalarında düzgün çalışmadığını anlamaya çalışırken beynimi yakıp geçirmeye çok zaman harcadım.

Ancak bunun "kısmi düzen" bir durum olduğunu fark ettiğimde, bunu çözebildim.

Şimdiye kadar web'de bulduğum tüm çalışma çözümleri, problemi doğru bir şekilde ele almak için bir çeşit topolojik sıralama kullandı. Topolojik sıralama bir yol gibi görünüyor.

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.