İzometrik oyun dünyaları çizim


178

Bir 2D oyunda izometrik fayans çizmenin doğru yolu nedir?

I (örneğin okuma referanslar ettik bu bir fayans bir şekilde işlenip göstermektedir), haritanın 2D dizi gösterimi her bir sütuna zik-zak. Ekrana çizilenlerin 2D dizisinin nasıl görüneceğiyle daha yakından ilişkili olduğu elmas şeklinde daha fazla çizilmeleri gerektiğini hayal ediyorum, sadece biraz döndürüldü.

Her iki yöntemin de avantajları veya dezavantajları var mı?

Yanıtlar:


504

Güncelleme: Düzeltilmiş harita oluşturma algoritması, daha fazla resim ekledi, biçimlendirmeyi değiştirdi.

Belki de karoları ekrana eşlemek için "zig-zag" tekniğinin avantajı, karonun xve ykoordinatların dikey ve yatay eksenler üzerinde olduğu söylenebilir .

"Elmasla çizim" yaklaşımı:

"Bir elmas çizim" kullanarak bir izometrik harita çizerek, sadece forbu örnek gibi iki boyutlu dizi üzerinde iç içe bir döngü kullanarak haritayı oluşturma anlamına gelir :

tile_map[][] = [[...],...]

for (cellY = 0; cellY < tile_map.size; cellY++):
    for (cellX = 0; cellX < tile_map[cellY].size cellX++):
        draw(
            tile_map[cellX][cellY],
            screenX = (cellX * tile_width  / 2) + (cellY * tile_width  / 2)
            screenY = (cellY * tile_height / 2) - (cellX * tile_height / 2)
        )

Avantajı:

Yaklaşımın avantajı, fortüm karolarda tutarlı bir şekilde çalışan oldukça düz ileri mantığa sahip basit bir iç içe geçmiş döngü olmasıdır.

dezavantajı:

Bu yaklaşımın bir dezavantajı , harita üzerindeki döşemelerin xve ykoordinatlarının çapraz çizgilerde artmasıdır, bu da ekrandaki konumu bir dizi olarak temsil edilen haritaya görsel olarak eşlemeyi zorlaştırabilir:

Döşeme haritasının görüntüsü

Bununla birlikte, yukarıdaki örnek kodun uygulanması için bir tuzak olacaktır - oluşturma sırası, belirli karoların arkasında olduğu düşünülen karoların önündeki karoların üstüne çizilmesine neden olacaktır:

Yanlış oluşturma siparişinden sonuçlanan resim

Bu sorunu değiştirmek için, iç- forloop'un sırası en yüksek değerden başlayıp daha düşük bir değere doğru döndürülmelidir:

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    for (j = tile_map[i].size; j >= 0; j--):  // Changed loop condition here.
        draw(
            tile_map[i][j],
            x = (j * tile_width / 2) + (i * tile_width / 2)
            y = (i * tile_height / 2) - (j * tile_height / 2)
        )

Yukarıdaki düzeltme ile haritanın görüntülenmesi düzeltilmelidir:

Doğru oluşturma siparişinden sonuçlanan görüntü

"Zig-zag" yaklaşımı:

Avantajı:

Belki de "zig-zag" yaklaşımının avantajı, oluşturulan haritanın "elmas" yaklaşımdan biraz daha dikey olarak kompakt görünebilmesidir:

Oluşturmaya zig-zag yaklaşımı kompakt görünüyor

dezavantajı:

Zig-zag tekniğini uygulamaya çalışmanın dezavantajı, forbir dizideki her öğe üzerine iç içe bir döngü kadar basit yazılamayacağı için oluşturma kodunu yazmanın biraz zor olması olabilir :

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    if i is odd:
        offset_x = tile_width / 2
    else:
        offset_x = 0

    for (j = 0; j < tile_map[i].size; j++):
        draw(
            tile_map[i][j],
            x = (j * tile_width) + offset_x,
            y = i * tile_height / 2
        )

Ayrıca, oluşturma sırasının şaşırtıcı doğası nedeniyle bir döşemenin koordinatını anlamaya çalışmak biraz zor olabilir:

Zig-zag sıralaması oluşturmada koordinatlar

Not: Bu cevaba dahil edilen resimler, sunulan karo oluşturma kodunun Java uygulamasıyla intve harita olarak aşağıdaki dizi ile oluşturuldu :

tileMap = new int[][] {
    {0, 1, 2, 3},
    {3, 2, 1, 0},
    {0, 0, 1, 1},
    {2, 2, 3, 3}
};

Döşeme resimleri:

  • tileImage[0] -> İçinde bir kutu olan bir kutu.
  • tileImage[1] -> Bir kara kutu.
  • tileImage[2] -> Beyaz bir kutu.
  • tileImage[3] -> İçinde uzun gri bir nesne olan bir kutu.

Karo Genişlikleri ve Yükseklikleri Üzerine Bir Not

Yukarıdaki kod örneklerinde kullanılan değişkenler tile_widthve tile_heightdöşemeyi temsil eden görüntüdeki zemin döşemesinin genişliğini ve yüksekliğini ifade eder:

Döşeme genişliğini ve yüksekliğini gösteren resim

Görüntü boyutları ve döşeme boyutları eşleştiği sürece görüntünün boyutlarını kullanmak işe yarayacaktır. Aksi takdirde, karo haritası karolar arasındaki boşluklarla oluşturulabilir.


136
resimler bile çizdiniz. Bu çaba.
zaratustra

Şerefe coobird. Geliştirdiğim oyun için elmas yaklaşımını kullanıyorum ve bir tedavi çalışıyor. Tekrar teşekkürler.
Benny Hallett

3
Çoklu yükseklik katmanlarına ne dersiniz? En düşük seviyeden başlayıp en yüksek seviyeye ulaşana kadar devam edebilir miyim?
NagyI

2
@DomenicDatti: Nazik sözleriniz için teşekkür ederim :)
coobird

2
Bu harika. Sadece ekran konumundan ızgara Coords almak için dava birisi ihtiyaçlarını, aynı zamanda elmas yaklaşımı kullanılmış ve ben bunu: j = (2 * x - 4 * y) / tilewidth * 0.5; i = (p.x * 2 / tilewidth) - j;.
Kyr Dunenkoff

10

Her iki durumda da işi yapar. Zikzak ile böyle bir şey demek istediğini varsayıyorum: (sayılar oluşturma sırasıdır)

..  ..  01  ..  ..
  ..  06  02  ..
..  11  07  03  ..
  16  12  08  04
21  17  13  09  05
  22  18  14  10
..  23  19  15  ..
  ..  24  20  ..
..  ..  25  ..  ..

Ve elmasla demek istedin:

..  ..  ..  ..  ..
  01  02  03  04
..  05  06  07  ..
  08  09  10  11
..  12  13  14  ..
  15  16  17  18
..  19  20  21  ..
  22  23  24  25
..  ..  ..  ..  ..

İlk yöntem, tam ekranın çizilmesi için daha fazla döşemeye ihtiyaç duyar, ancak kolayca bir sınır kontrolü yapabilir ve tüm döşemeleri ekran dışında tamamen atlayabilirsiniz. Her iki yöntem de, döşemenin 01 konumunun ne olduğunu bulmak için bir miktar çıtırtı gerektirecektir. Sonunda, her iki yöntem de belirli bir verimlilik seviyesi için gerekli olan matematik açısından kabaca eşittir.


14
Aslında tam tersini kastetmiştim. Elmas şekli (haritanın kenarlarını pürüzsüz bırakır) ve zikzak yöntemi, kenarları dikenli bırakarak
Benny Hallett

1

Elmasınızın sınırlarını aşan bazı fayanslarınız varsa, derinlemesine sıra çizmenizi öneririz:

...1...
..234..
.56789.
..abc..
...d...

1

Coobird'ün cevabı doğru ve eksiksizdir. Ancak, bu tür bir şey arayan buraya gelen herkesle paylaşmak istediğim uygulamamda (iOS / Objective-C) çalışan bir kod oluşturmak için ipuçlarını başka bir siteden olanlarla birleştirdim. Lütfen, bu cevabı beğenirseniz / oylar üstlenirseniz, orijinaller için de aynısını yapın; tek yaptığım "devlerin omuzlarında durmak".

Sıralama düzenine gelince, tekniğim değiştirilmiş bir ressamın algoritmasıdır: her nesnenin (a) tabanın bir yüksekliği ("seviye" diyorum) ve (b) "taban" veya "ayak" için bir X / Y görüntü (örnekler: avatarın tabanı ayakları; ağacın tabanı kökleri; uçağın tabanı orta görüntü, vb.) Sonra sadece en düşük seviyeden en yükseğe, sonra en düşük (ekrandan en yüksek) en yüksek Y, sonra en düşük (en sol) en yüksek X tabanına. Bu, fayansları beklendiği gibi yapar.

Ekranı (nokta) döşemeye (hücre) ve geriye dönüştürmek için kod:

typedef struct ASIntCell {  // like CGPoint, but with int-s vice float-s
    int x;
    int y;
} ASIntCell;

// Cell-math helper here:
//      http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511
// Although we had to rotate the coordinates because...
// X increases NE (not SE)
// Y increases SE (not SW)
+ (ASIntCell) cellForPoint: (CGPoint) point
{
    const float halfHeight = rfcRowHeight / 2.;

    ASIntCell cell;
    cell.x = ((point.x / rfcColWidth) - ((point.y - halfHeight) / rfcRowHeight));
    cell.y = ((point.x / rfcColWidth) + ((point.y + halfHeight) / rfcRowHeight));

    return cell;
}


// Cell-math helper here:
//      http://stackoverflow.com/questions/892811/drawing-isometric-game-worlds/893063
// X increases NE,
// Y increases SE
+ (CGPoint) centerForCell: (ASIntCell) cell
{
    CGPoint result;

    result.x = (cell.x * rfcColWidth  / 2) + (cell.y * rfcColWidth  / 2);
    result.y = (cell.y * rfcRowHeight / 2) - (cell.x * rfcRowHeight / 2);

    return result;
}

1

Görüntüleyicinin en yüksek ve en yakın noktasından öklid mesafesini kullanabilirsiniz, ancak bu doğru değildir. Küresel sıralama düzeniyle sonuçlanır. Daha uzağa bakarak bunu düzeltebilirsiniz. Daha sonra, eğrilik düzleşir. Bu yüzden x ', y' ve z 'vermek için x, y ve z bileşenlerinin her birine 1000 diyelim ekleyin. X '* x' + y '* y' + z '* z' üzerindeki sıralama.


0

Gerçek sorun, iki veya daha fazla fayans kesişen / yayılan bazı kiremit / sprite çizmek gerektiğinde.

Problemin 2 (zor) aylık kişisel analizinden sonra nihayet yeni cocos2d-js oyunum için bir "doğru render çizimi" buldum ve uyguladım. Çözüm, spriteların "ön, arka, üst ve arka" olduğu her bir karo için (duyarlı) haritalamadan oluşur. Bunu yaptıktan sonra "özyinelemeli bir mantık" izleyerek çizebilirsiniz.

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.