Advertisement
  1. Game Development
  2. Phaser

Panduan Diperbarui untuk Membuat Dunia Isometric, Bagian 1

Scroll to top
Read Time: 15 min
This post is part of a series called Primer for Creating Isometric Worlds.
Updated Primer for Creating Isometric Worlds, Part 2

() translation by (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Kita semua pasti sudah memainkan banyak isometric game, mulai dari Diablo pertama, Age of Empires, atau Commandos. Pertama kali kamu menemui sebuah game isometric, kamu mungkin penasaran apakah game tersebut game 2D, game 3D, atau sesuatu yang sama sekali berbeda. Dunia game isometric juga menarik perhatian game developer. Mari kita ungkap misteri proyeksi isometric dan coba membuat dunia isometric di tutorial ini.

Tutorial ini adalah versi yang diperbarui dari tutorial untuk membuat dunis isometric yang pernah saya buat. Tutorial tersebut menggunakan Flash dan ActionScript dan masih relevan untuk developer Flash atau OpenFL. Dalam tutorial baru ini, saya memutuskan untuk menggunakan Phaser dengan kode JS dan menghasilkan HTML5 interaktif, bukan SWF.

Perlu diketahui ini bukanlah tutorial pengembangan game menggunakan Phaser, kita hanya menggunakan Phaser untuk mengkomunikasikan konsep inti dari membuat tampilan isometric. Lagi pula, ada cara yang lebih baik dan lebih mudah untuk membuat konten isometric menggunakan Phaser, contohnya dengan Phaser Isometric Plugin.

Agar tetap sederhana, kita akan menggunakan pendekatan berbasis petak untuk membuat tampilan isometric di tutorial ini.

1. Game Berbasis Petak

Dalam game 2D dengan pendekatan berbasis petak, setiap elemen visual dipecah menjadi potongan lebih kecil, disebut petak dengan ukuran yang sama. Petak-petak ini akan menyusun dunia game berdasarkan data level yang ditentukan sebelumnya, biasanya array dua dimensi.

Artikel yang Berhubungan

Biasanya game berbasis petak menggunakan sudut pandang top-down atau side view. Kita akan gunakan sudut pandang top-down 2D dengan dua jenis petak, petak rumput dan petak tembok, seperti yang ditampilkan di sini:

Green and Maroon tilesGreen and Maroon tilesGreen and Maroon tiles

Kedua petak ini adalah gambar kotak dengan ukuran yang sama, sehingga tinggi dan lebar petak bernilai sama. Kita buat level dalam game yang berupa dataran rumput dikelilingi tembok di semua sisi. Dalam kasus tersebut, level data diwakili array dua dimensi seperti ini:

1
[
2
 [1,1,1,1,1,1],
3
 [1,0,0,0,0,1],
4
 [1,0,0,0,0,1],
5
 [1,0,0,0,0,1],
6
 [1,0,0,0,0,1],
7
 [1,1,1,1,1,1]
8
 ]

Di sini, 0 artinya petak rumput dan 1 artinya petak tembok. Menyusun petak berdasarkan data level akan menghasilkan dataran rumput yang dikelilingi tembok seperti pada gambar di bawah.

Top view level - grass area surrounded by wallsTop view level - grass area surrounded by wallsTop view level - grass area surrounded by walls

Kita bisa kembangkan lebih jauh dengan menambahkan petak pojok dan memisahkan petak tembok vertikal dan horizontal. Hal ini membutuhkan lima jenis petak tambahan, yang akan membuat data level kita perlu diperbarui.

1
[
2
 [3,1,1,1,1,4],
3
 [2,0,0,0,0,2],
4
 [2,0,0,0,0,2],
5
 [2,0,0,0,0,2],
6
 [2,0,0,0,0,2],
7
 [6,1,1,1,1,5]
8
 ]

Lihatlah gambar di bawah, saya menandai petak dengan nomor petaknya masing-masing sesuai data level:

Better top view level with corner tiles and tile numbersBetter top view level with corner tiles and tile numbersBetter top view level with corner tiles and tile numbers

Sekarang setelah kita mengerti konsep pendekatan berbasis petak, saya akan perlihatkan bagaimana kamu bisa menggunakan pseudo code sederhana grid 2D untuk merender level kita:

1
for (i, loop through rows)
2
    for (j, loop through columns)
3
        x = j * tile width
4
        y = i * tile height
5
        tileType = levelData[i][j]
6
        placetile(tileType, x, y)

Jika kita menggunakan gambar petak di atas, maka lebar dan tinggi petak akan sama untuk semua petak, dan sesuai dengan ukuran gambar yang kita gunakan untuk petak yang bersangkutan. Lebar dan tinggi untuk contoh ini keduanya bernilai 50 piksel, yang membuat ukuran total level menjadi 300 x 300 piksel, karena disusun oleh enam baris dan enam kolom petak yang masing-masing berukuran 50 x 50 piksel.

Seperti yang didiskusikan sebelumnya, dalam pendekatan berbasis petak biasa, kita menerapkan sudut pandang top down atau side view; untuk sudut pandang isometric kita perlu mengimplementasi proyeksi isometric.

2. Proyeksi Isometric

Sejauh yang saya tahu, penjelasan teknis terbaik tentang apa itu proyeksi isometric adalah dari artikel ini oleh Clint Bellanger:

Kita mengatur sudut kamera kita pada dua sumbu (menggeser kamera 45 derajat di satu sisi, lalu 30 derajat ke bawah). Hal ini membuat grid berbentuk belah ketupat, di mana ruang yang diisi oleh grid dua kali lebih lebar dari tingginya. Gaya ini dipopulerkan oleh game strategi dan action RPG. Jika kita lihat sebuah kubus dengan sudut pandang ini, tiga sisi akan terlihat (atas, dan dua sisi depan)

Walaupun terdengar cukup rumit, sebenarnya mengimplementasi sudut pandang ini sangat mudah. Yang perlu kita mengerti adalah hubungan antara ruang 2D dan ruang isometric, yaitu hubungan antara data level dan tampilan; perubahan dari sudut pandang top-down pada koordinat Kartesian menjadi koordinat isometric. Gambar di bawah ini menunjukkan perubahannya secara visual:

Side by side view of top down and isometric gridsSide by side view of top down and isometric gridsSide by side view of top down and isometric grids

Menempatkan Petak Isometric

Saya akan coba menyederhanakan hubungan antara data level yang disimpan sebagai array 2D dan sudut pandang isometric, yaitu bagaimana kita mengubah koordinat Kartesian ke koordinat isometric. Kita akan coba untuk membuat sudut pandang isometric untuk dataran rumput yang dikelilingi tembok. Implementasi sudut pandang 2D dari level ini berupa iterasi sederhana dengan dua buah loop, menempatkan petak dengan jarak yang tetap sesuai dengan tinggi dan lebar petak. Untuk sudut pandang isometric, pseudo codenya tetap sama, tapi fungsi placeTile() berubah.

Fungsi awal hanya menggambar gambar petak di koordinat x dan y yang diminta, tapi untuk sudut pandang isometric kita perlu menghitung koordinat isometric yang bersangkutan. Persamaan yang diperlukan adalah sebagai berikut, di mana isoX dan isoY mewakili koordinat isometric x dan y, cartX dan cartY mewakili koordinat Kartesian x dan y.

1
//Cartesian to isometric:

2
 
3
isoX = cartX - cartY;
4
isoY = (cartX + cartY) / 2;
1
//Isometric to Cartesian:

2
 
3
cartX = (2 * isoY + isoX) / 2;
4
cartY = (2 * isoY - isoX) / 2;

Ya, begitu saja. Persamaan sederhana ini adalah sihir di balik proyeksi isometric. Ini adalah fungsi pembantu dalam Phaser yang bisa digunakan untuk mengubah dari satu sistem ke sistem lainnya menggunakan kelas Point:

1
function cartesianToIsometric(cartPt){
2
    var tempPt=new Phaser.Point();
3
    tempPt.x=cartPt.x-cartPt.y;
4
    tempPt.y=(cartPt.x+cartPt.y)/2;
5
    return (tempPt);
6
}
1
function isometricToCartesian(isoPt){
2
    var tempPt=new Phaser.Point();
3
    tempPt.x=(2*isoPt.y+isoPt.x)/2;
4
    tempPt.y=(2*isoPt.y-isoPt.x)/2;
5
    return (tempPt);
6
}

Kita bisa menggunakan fungsi pembantu cartesianToIsometric untuk mengubah koordinat 2D menjadi koordinat isometric di dalam fungsi placeTile. Di luar fungsi ini, kode untuk merender tetap sama, tapi kita perlu gambar baru untuk petak isometric. Kita tidak bisa menggunakan petak lama yang kita gunakan pada top-down rendering. Gambar di bawah menunjukkan petak isometric rumput dan tembok dan hasil render level isometric.

Isometric level walled grassland along with the isometric tiles usedIsometric level walled grassland along with the isometric tiles usedIsometric level walled grassland along with the isometric tiles used

Tidak bisa dipercaya bukan? Sekarang lihat bagaimana posisi 2D biasa diubah menjadi posisi isometric.

1
2D point = [100, 100];
2
// isometric point will be calculated as below

3
isoX = 100 - 100; // = 0

4
isoY = (100 + 100) / 2;  // = 100

5
Iso point == [0, 100];

Begitu pula, input [0,0] akan menghasilkan [0,0] dan [10,5] akan menghasilkan [5, 7.5].

Untuk dataran rumput bertembok kita, kita bisa menentukan area yang bisa digunakan berjalan dengan memeriksa apakah elemen array pada koordinat tersebut adalah 0, yang mewakili petak rumput. Untuk ini kita perlu menentukan koordinat array. Kita bisa menemukan koordinat petak dalam data level berdasarkan koordinat Kartesian menggunakan fungsi berikut:

1
function getTileCoordinates(cartPt, tileHeight){
2
    var tempPt=new Phaser.Point();
3
    tempPt.x=Math.floor(cartPt.x/tileHeight);
4
    tempPt.y=Math.floor(cartPt.y/tileHeight);
5
    return(tempPt);
6
}

(Di sini, kita mengasumsikan tinggi dan lebar petak bernilai sama, seperti pada umumnya.)

Karena itu, dari pasangan koordinat layar (isometric), kita bisa mendapatkan koordinat petak dengan memanggil:

1
getTileCoordinates(isometricToCartesian(screen point), tile height);

Titik layar (screen point) bisa berisi misalnya posisi klik mouse atau posisi objek yang bisa diambil pemain.

Titik-titik penting

Dalam Flash, kita bisa mengatur titik untuk sebuah gambar untuk menentukan titik pusatnya atau [0,0]. Fitur tersebut pada Phaser disebut Pivot. Jika kamu menyimpan gambar misalnya di [10,20], maka titik Pivot akan sejajar dengan [10,20]. Pada awalnya, pojok kiri atas sebuah gambar dianggap titik [0,0] atau Pivot. Jika kamu coba membuat level di atas menggunakan kode yang disediakan, maka kamu tidak akan mendapatkan hasil yang ditampilkan. Melainkan kamu akan mendapat dataran rata tanpa tembok, seperti gambar di bawah:

The issue with wall tiles when rendered normallyThe issue with wall tiles when rendered normallyThe issue with wall tiles when rendered normally

Hal ini disebabkan karena gambar petak berbeda ukuran dan kita tidak menangani atribut tinggi dari petak tembok. Gambar di bawah menunjukkan gambar petak yang berbeda yang kita gunakan disertai dengan bounding box masing-masing dan lingkaran putih di mana posisi awal [0,0] berada:

How to properly align the different tiles along with their registration pointsHow to properly align the different tiles along with their registration pointsHow to properly align the different tiles along with their registration points

Lihat bagaimana karakter tidak sejajar saat kita menggambar sesuai pivot awal. Perhatikan pula bahwa kita kehilangan tinggi petak tembok jika menggunakan pivot awal. Gambar di kanan menujukkan bagaimana mereka seharusnya diluruskan agar petak tembok mendapat tinggi yang benar dan gambar ditempatkan di tengah petak rumput. Masalah ini bisa diselesaikan dengan berbagai cara.

  1. Buat semua petak dengan ukuran yang sama, dengan gambar disejajarkan dengan benar. Cara ini membuat banyak area kosong di dalam gambar petak.
  2. Mengatur titik pivot secara manual untuk setiap petak agar mereka semua sejajar.
  3. Gambar petak dengan jarak tertentu agar berjajar dengan benar.

Untuk tutorial ini, saya memilih menggunakan cara ketiga agar bisa tetap bekerja walau dengan framework yang memiliki kemampuan untuk mengatur titik pivot.

Bergerak dalam Koordinat Isometric

Kita tidak akan menggerakkan karakter atau proyektil secara langsung dalam koordinat isometric. Tapi kita akan memanipulasi data dunia game dalam koordinat Kartesian dan hanya menggunakan fungsi di atas untuk memperbarui posisi pada layar. Contohnya, jika kamu ingin memindahkan karakter maju di sumbu y, kamu bisa menambah nilai y di koordinat 3D lalu mengubahnya menjadi posisi pada koordinat isometric:

1
y = y + speed;
2
placetile(cartesianToIsometric(new Phaser.Point(x, y)))

Sekarang adalah waktu yang baik untuk meninjau kembali semua konsep yang sudah kita pelajari sejauh ini dan coba membuat contoh benda yang bergerak di dunia isometric. Kamu bisa menemukan gambar yang dibutuhkan di folder assets pada git repository.

Depth Sorting (mengurutkan kedalaman)

Jika kamu mencoba menggerakkan gambar bola di taman bertembok maka kamu akan menemui masalah dengan depth sorting. Selain penempatan normal, kita perlu menangani depth sorting untuk menggambar dunia isometric, jika ada elemen yang bergerak.

Cara mengurut kedalaman yang paling sederhana adalah menggunakan nilai koordinat y, seperti disebut pada Quick Tip berikut: semakin atas objek pada layar, semakin awal harus digambar. Cara ini akan bekerja dengan baik untuk adegan isometric sederhana, tapi cara yang lebih baik adalah untuk menggambar ulang adegan isometric begitu ada gerakan terjadi, sesuai dengan koordinat array petak. Saya akan jelaskan konsep ini secara rindi dengan pseudo code untuk menggambar level:

1
for (i, loop through rows)
2
    for (j, loop through columns)
3
        x = j * tile width
4
        y = i * tile height
5
        tileType = levelData[i][j]
6
        placetile(tileType, x, y)

Bayangkan item atau karakter kita di petak [1,1], yaitu petak hijau paling atas pada sudut pandang isometric. Untuk menggambar level dengan benar, karakter perlu digambar setelah menggambar petak tembok pojok, petak tembok kanan dan kiri, dan petak lantai, seperti di bawah ini:

Hero standing on the corner tileHero standing on the corner tileHero standing on the corner tile

Jika kita mengikuti loop draw seperti pseudo dode di atas, kita akan menggambar tembok pojok tengah pertama, dan lanjut menggambar semua tembok di bagian kanan atas sampai mencapai pojok kanan.

Lalu, pada loop berikutnya, kita akan gambar tembok di sebelah kiri karakter, lalu petak lantai sempat karakter berdiri. Setelah kita menentukan bahwa ini adalah petak posisi karkter, kita akan gambar karakter setelah menggambar petak rumput. Dengan begini, jika ada tembok di tiga petak rumput yang berhubungan dengan petak tempat karakter berdiri, tembok tersebut akan tumpang tindih dengan karakter, menghasilkan rendering dengan urutan kedalaman yang tepat.

Membuat Art

Art untuk isometric bisa pixel art, tapi tidak harus. Saat berurusan dengan pixel art isometric, panduan dari RhysD akan memberi tahu semua hal yang kamu perlu tahu. Beberapa teori juga bisa ditemukan di Wikipedia.

Saat membuat art isometric, aturan dasarnya adalah:

  • Mulai dengan grid isometric kosong dan terapkan presisi sampai setiap piksel benar.
  • Coba untuk memecah art menjadi gambar petak isometric terpisah.
  • Pastikan semua petak ditentukan apakah bisa dilewati atau tidak. Akan sangat sulit jika kita harus membuat satu petak memiliki area yang bisa dilewati yang tidak.
  • Sebagian besar petak akan perlu tersambung dengan baik ke petak lain di satu arah atau lebih.
  • Bayangan akan sulit untuk dibuat, kecuali kamu menggunakan pendekatan layer di mana kita menggambar bayangan di layer lantai lalu menggambar karakter (atau pohon, atau objek lain) di layer atas. Jika pendekatanmu bukan multi-layered, pastikan bayangan jatuh ke depan agar tidak jauh ke objek yang salah, misalnya karakter saat di belakang pohon.
  • Jika kamu perlu gambar petak lebih besar dari ukuran petak isometric yang standar, coba gunakan dimensi yang merupakan kelipatan dari ukuran petak isometric. Lebih baik menggunakan pendekatan layer pada kasus tersebut, di mana kita bisa memecah art menjadi potongan yang berbeda berdasarkan ketinggiannya. Contohnya, sebuah pohon bisa dipecah menjadi tiga bagian, akar, batang, dan daun. Hal ini membuat lebih mudah mengurutkan kedalaman karea kita bisa menggambar potongan di layer sesuai dengan ketinggian mereka.

Petak isometric yang lebih besar dari ukuran satu petak akan membuat masalah saat mengurut kedalaman. Sebagian masalah tersebut dibahas di beberapa tautan berikut:

Artikel Terkait

Karakter Isometric

Pertama kita perlu memperbaiki berapa banyak arah gerak yang kita perbolehkan dalam game ini, biasanya game akan menyediakan gerakan empat arah atau delapan arah. Lihat gambar di bawah untuk mengerti hubungan antara ruang 2D dan ruang isometric:

The directions of motion in top view and isometric viewThe directions of motion in top view and isometric viewThe directions of motion in top view and isometric view

Perhatikan bahwa seorang karakter akan bergerak ke atas secara vertikal saat kita tekan tombol panah atas pada game top-down, tapi untuk game isometric karakter akan bergerak 45 derajat ke arah pojok kanan atas.

Untuk sudut pandang top-down, kita bisa buat satu set animasi karakter menghadap satu arah, dan memutarnya untuk arah lainnya. Untuk gambar karakter isometric, kita perlu merender ulang semua animasi untuk semua arah yang diperbolehkan, jadi untuk gerakan delapan arah, kita perlu buat delapan animasi untuk setiap aksi.

Agar lebih mudah dimengerti, kita biasanya menyebut arah sebagai Utara, Barat Laut, Barat, Barat Daya, Selatan, Tenggara, Timur, dan Timur Laut. Frame karakter di bawah menunjukkan frame idle dari Tenggara dan seterusnya sesuai arah jarum jam:

The different frames of the character facing the different directionsThe different frames of the character facing the different directionsThe different frames of the character facing the different directions

Kita akan tempatkan karakter dengan cara yang sama dengan menempatkan petak. Pergerakan karakter dicapai dengan memperhitungkan gerakan di koordinat Kartesian lalu mengubahnya ke koordinat isometric. Kita asumsikan kita akan menggunakan keyboard untuk mengontrol karakter.

Kita akan atur dua variabel, dX dan dY, berdasarkan tombol arah yang ditekan. Awalnya, variabel ini akan bernilai 0 dan akan diperbarui sesuai tabel di bawah, di mana U, D, R, dan L mewakili tombol Up, Down, Right, dan Left. Nilai 1 pada tombol menunjukkan bahwa tombol sedang ditekan; 0 menunjukkan bahwa tombol tidak sedang ditekan.

1
  Key       Pos
2
U D R L    dX dY
3
================
4
0 0 0 0     0  0
5
1 0 0 0     0  1
6
0 1 0 0     0 -1
7
0 0 1 0     1  0
8
0 0 0 1    -1  0
9
1 0 1 0     1  1
10
1 0 0 1    -1  1
11
0 1 1 0     1 -1
12
0 1 0 1    -1 -1

Berikutnya, dengan nilai dX dan dY, kita bisa memperbarui koordinat Kartesian sebagai berikut;

1
newX = currentX + (dX * speed);
2
newY = currentY + (dY * speed);

Jadi dX dan dY menunjukkan perubahan posisi karakter pada x dan y, sesuai tombol yang ditekan. Kita bisa menghitung koordinat isometric baru, seperti yang sudah kita bahas sebelumnya:

1
Iso = cartesianToIsometric(new Phaser.Point(newX, newY))

Setelah kita mendapatkan posisi isometric baru, kita perlu memindahkan karakter ke posisi tersebut. Berdasarkan nilai dX dan dY yang kita miliki, kita bisa menentunkan arah hadap karakter dan menggunakan gambar karakter yang tepat. Begitu karakter dipindahkan, jangan lupa untuk menggambar ulang level dengan kedalaman yang tepat karena koordinat petak karakter mungkin berubah.

Deteksi Tabrakan

Deteksi tabrakan dilakukan dengan memeriksa apakah petak di posisi baru adalah petak yang tidak bisa dilewati. Jadi, saat kita menemukan posisi baru, kita tidak langsung memindahkan karakter ke sana, tapi terlebih dahulu memeriksa petak apa yang ada pada posisi tersebut.

1
tile coordinate = getTileCoordinates(isometricToCartesian(current position), tile height);
2
if (isWalkable(tile coordinate)) {
3
  moveCharacter();
4
} else {
5
  //do nothing;

6
}

Pada fungsi isWalkable(), kita memeriksa apakah nilai array data level pada koordinat yang bersangkutan adalah petak yang bisa dilewati atau tidak. Kita harus memperbarui arah hadap karakter, walaupun dia tidak bergerak, misalnya saat menabrak petak yang tidak bisa dilewati.

Sekarang hal ini mungkin terdengar seperti sollusi yang baik, tapi hanya bisa bekerja untuk benda tanpa volume. Hal ini karena kita hanya mempertimbangkan satu titik, yaitu titik tengah karakter, untuk menentukan tabrakan. Yang kita butuhkan sekarang adalah untuk menemukan empat sudut karakter dari koordinat titik tengah 2D dan memperhitungkan tabrakan untuk semuanya. Jika salah satu sudut masuk ke petak yang tidak bisa dilewati, kita tidak boleh menggerakkan karakter.

Mengurutkan Kedalaman dengan Karakter

Bayangkan karakter dan sebuah petak pohon pada dunia isometric, dan keduanya memiliki tinggi gambar yang sama, walaupun terdengar tidak realistis.

Untuk memahami depth sorting dengan baik, kita harus mengerti bahwa saat koordinat x dan y dari karakter kurang dari tinggi pohon, pohon tumpang tindih dengan karakter. Saat koordinat x dan y karakter lebih besar dari pohon, karakter tumpang tindih dengan pohon. Saat mereka memiliki koordinat x yang sama, maka kita tentukan hanya berdasarkan koordinat y: manapun yang memiliki nilai koordinat y yang lebih tinggi menimpa yang lainnya. Saat keduanya memiliki nilai koordinat y yang sama, maka kita tentukan hanya berdasarkan koordinat x: manapun yang memiliki koordinat x lebih besar menimpa yang lainnya.

Seperti dijelaskan sebelumnya, versi sederhana dari ini adalah dengan hanya menggambar level berurutan mulai dari petak terjauh, yaitu tile[0][0], lalu menggambar semua petak di setiap baris satu per satu. Jika seorang karakter menempati petak, kita menggambar petak tanah terlebih dahulu dan merender petak karakter. Ini akan bekerja dengan baik, karena karakter tidak bisa menempati petak tembok.

Waktunya Demo!

Ini adalah demo dalam Phaser. Klik untuk fokus ke area interaktif dan gunakan tombol panah untuk menggerakkan karakter. Kamu bisa menggunakan dua tombol arah untuk bergerak secara diagonal.

Kamu bisa menemukan kode lengkap untuk demo ini di repository kode tutorial ini. Kode JS  utama disediakan di bawah ini:

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.