Advertisement
  1. Game Development
  2. Game Development
Gamedevelopment

Panduan Diperbarui untuk Membuat Dunia Isometric, Bagian 2

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Primer for Creating Isometric Worlds.
An Updated Primer for Creating Isometric Worlds, Part 1

Indonesian (Bahasa Indonesia) translation by Aditia Dwiperdana (you can also view the original English article)

Final product image
What You'll Be Creating

Pada bagian akhir dari seri tutorial ini, kita kan melanjutkan dari tutorial pertama dan mempelajari tentang membuat barang yang bisa diambil, trigger, mengganti level, path finding, path following, menggeser level, ketinggian isometric, dan proyektil isometric.

1. Barang Pickup

Pickup adalah barang yang bisa dikumpulkan dalam sebuah level, biasanya cukup dengan melewatinya, contohnya koin, permata, uang, peluru, dan lain-lain.

Data pickup bisa dimasukkan langsung ke data level kita seperti di bawah ini:

Dalam data level ini, kita gunakan 8 untuk menunjukkan pickup pada petak rumput (1 dan 0 mewakili tembok dan petak yang bisa dilewati seperti sebelumnya). Ini bisa berarti sebuah gambar petak rumput ditimpa dengan gambar barang pickup. Dengan logika ini, kita akan membutuhkan dua kondisi petak yang berbeda untuk setiap petak yang memiliki pickup, misalnya yang memiliki pickup, dan yang sudah tidak memiliki pickup jika sudah diambil pemain.

Art isometric pada umumnya memiliki beberapa jenis petak yang bisa dilewati, misalnya kita punya 30. Pendekatan di atas berarti jika kita punya sejumlah N pickup, kita akan membutuhkan N x 30 petak ditambah 30 petak asli, karena setiap petak akan perlu satu versi dengan pickup dan satu versi tanpa pickup. Cara ini tidak terlalu efisien; kita perlu coba membuat kombinasi ini secara dinamis.

Untuk memecahkan ini, kita bisa gunakan cara yang sama dengan menempatkan karakter di tutorial pertama. Kapanpun kita menemui petak dengan pickup, kita akan tempatkan petak rumput dan tempatkan pickup di atas petak rumput tersebut. Dengan ini, kita hanya perlu sejumlah N petak pickup ditambah 30 petak yang bisa dilewati, tapi kita perlu nilai angka untuk melambangkan kombinasi pada data level. Untuk menghasilkan sejumlah N x 30 nilai representasi, kita bisa simpan pickupArray khusus untuk menyimpan data pickup terpisah dari levelData. Level yang selesai dengan pickup akan terlihat seperti berikut:

Isometric level with coin pickup

Pada contoh kita, saya mempertahankan berbagai hal sederhana dan tidak menggunakan array tambahan untuk pickup.

Mengambil Pickup

Mendeteksi pickup dilakukan dengan cara yang sama dengan mendeteksi tabrakan, tapi dilakukan setelah menggerakkan karakter.

Pada fungsi onPickupTile() kita periksa apakah nilai array levelData di koordinat heroMapTile adalah petak pickup atau tidak. Angka di array levelData pada petak di suatu koordinat menunjukkan jenis pickup. Kita perlu periksa tabrakan sebelum gerakan karakter tapi periksa pickup setelahnya, karena pada kasus tabrakan, karakter tidak seharusnya mengisi titik tersebut jika sudah terisi oleh petak yang tidak bisa dilewati, tapi dalam kasus pickup, karakter bebas untuk bergerak melewatinya.

Satu hal yang perlu diketahui adalah data  tabrakan biasanya tidak pernah berubah, tapi data pickup berubah setiap kita mengambil sebuah barang. (Hal ini biasanya cukup dengan merubah nilai di array levelData dari misalnya 8 menjadi 0.)

Hal ini membawa sebuah masalah: apa yang terjadi jika kita perlu mengulang level, dan harus mengembalikan semua pickup kembali ke posisi awalnya? Kita tidak memiliki informasi untuk melakukan hal ini, karena array levelData sudah diubah saat pemain pengambil barang. Solusinya adalah menggunakan array salinan untuk level saat sedang dimainkan dan menyimpan array levelData aslinya utuh. Contohnya, kita gunakan levelData dan levelDataLive[], salin array pertama ke array kedua di awal level, dan hanya ubah levelDataLive[] saat permainan.

Untuk contoh ini, saya memunculkan pickup acak di petak rumput yang kosong setiap pemain mengambil barang dan menambahkan nilai pickupCount. Fungsi pickupItem akan terlihat seperti ini.

Kamu perlu memperhatikan bahwa kita memeriksa pickup saat karakter ada pada petak tersebut. Ini bisa terjadi beberapa kali dalam satu detik (kita hanya memeriksa saat pemain bergerak, tapi kita bisa bergerak berputar di suatu petak), tapi logika tersebut tidak akan gagal; karena kita mengatur data pada array levelData menjadi 0 saat pertama kali kita mendeteksi benda pickup, semua pemeriksaan onPickupTile() akan bernilai false untuk petak tersebut. Lihatlah contoh interaktif di bawah:

2. Petak Trigger

Sesuai namanya, petak trigger menyebabkan sesuatu terjadi saat pemain menginjaknya atau menekan kunci di atasnya. Mereka bisa memindahkan pemain ke lokasi yang berbeda, membuka gerbang, memunculkan musuh, dan lain-lain. Sebenarnya, pickup adalah bentuk khusus dari petak trigger: saat pemain menginjak petak berisi koin, koin hilang, dan penghitung koin bertambah.

Kita lihat bagaimana kita bisa mengimplementasi pintu yang membawa pemain ke level lain. Petak di sebelah pintu akan menjadi petak trigger; saat pemain menekan tombol x, mereka akan melanjutkan ke level berikutnya.

Isometric level with doors trigger tiles

Untuk mengubah level, kita hanya perlu menukar array levelData saat ini dengan milik level baru, dan mengatur posisi heroMapTile dan arah dari karakter pemain. Anggaplah ada dua level dengan pintu yang menghubungkan keduanya. Karena petak tanah di sebelah pintu akan menjadi petak trigger di kedua level, kita bisa menggunakannya sebagai posisi baru saat karakter saat muncul di level baru.

Logika untuk mengimplementasi fitur ini sama dengan pickup, dan kita akan menggunakan array levelData untuk menyimpan nilai trigger. Dalam contoh kita, 2 berarti petak pintu, dan nilai di sebelahnya adalah triggernya. Saya menggunakan 101 dan 102 dengan ketentuan dasar bahwa sebuah petak dengan nilai lebih dari 100 adalah petak trigger dan nilai tersebut dikurangi 100 adalah level yang dituju:

Kode untuk memeriksa event trigger adalah sebagai berikut:

Fungsi triggerListener() memeriksa jika nilai di array data trigger pada suatu koordinat lebih besar dari 100. Jika iya, kita cari level yang perlu kita munculkan dengan mengurangi nilai petak dengan 100. Fungsi tersebut mencari petak trigger di levelData yang baru sebagai posisi baru untuk karakter pemain. Saya buat trigger aktif saat x dilepas; jika kita hanya mendengarkan tombol ditekan, maka kita akan masuk dalam loop di mana kita menukar antar level selama tombol ditekan, karena karakter selalu muncul di atas petak trigger.

Berikut adalah demo yang bisa dicoba. Cobalah mengambil barang dengan berjalan melewatinya dan berganti level dengan berdiri di sebelah pintu dan menekan x.

3. Proyektil

Proyektil adalah sesuatu yang bergerak dengan arah dan kecepatan tertentu, seperti peluru, sihir, bola, dan lain-lain. Proyektil serupa dengan karakter pemain, kecuali tingginya: proyektil umumnya melayang di atas tanah, tidak berdiri di atasnya. Sebuah peluru akan bergerak di atas ketinggian pinggang karakter, dan sebuah bola mungkin akan memantul kesana kemari.

Satu hal yang menarik adalah tinggi isometric sama dengan tinggi pada sudut pandang 2D dari samping, walau nilainya lebih kecil. Tidak ada perubahan rumit yang perlu dilakukan. Jika sebuah bola berada 10 piksel di atas tanah pada koordinat Kartesian, bola tersebut bisa 10 atau 6 piksel di atas tanah pada koordinat isometric. (dalam kasus kita, sumbu yang relevan adalah sumbu y.)

Kita coba implementasi bola memantul pada dataran rumput kita. Agar lebih nyata, kita tambahkan bayangan untuk bola. Kita hanya perlu menambahkan nilai ketinggian pantulan pada sumbu Y untuk bola kita. Tinggi pantulan akan berubah dari frame ke frame tergantung dari gravitasi, dan begitu bola menyentuh lantai, kita akan ubah kecepatan sepanjang sumbu y.

Sebelum kita menangani pantulan dalam dunia isometric, kita lihat bagaimana untuk mengimplementasinya pada sistem Kartesian 2D. Kita wakili kekuatan pantulan bola dengan  variabel zValue. Bayangkan bahwa nilai awal bola bernilai 100, jadi zValue = 100.

Kita akan gunakan dua variabel: incrementValue, yang akan mulai dari 0, dan gravity, yang akan memiliki nilai -1. Setiap frame, kita kurangi incrementValue dengan zValue, dan kurangi gravity dengan incrementValue untuk membuat perlambatan. Saat zValue bernilai 0 artinya bola mencapai tanah; sekarang kita mengubah tanda incrementValue dengan mengalikannya dengan -1, mengubahnya menjadi nilai positif. Ini membuat bola bergerak ke atas pada frame berikutnya, seakan memantul.

Kodenya akan terlihat sebagai berikut:

Kodenya akan sama untuk sudut pandang isometric, dengan perbedaan bahwa kamu bisa menggunakan nilai zValue yang lebih kecil. Lihat di bawah bagaimana zValue ditambahkan pada nilai y isometric bola saat rendering.

Lihatlah contoh interaktif di bawah:

Ketahuilah bahwa peran bayangan sangat penting untuk menambahkan ilusi agar terlihat lebih nyata. Ketahui pula kita sekarang menggunakan dua koordinat (x dan y) untuk mewakili posisi tiga dimensi dalam koordinat isometric, sumbu Y pada koordiant layar adalah sumbu z pada koorinat isometric. Hal ini bisa membingungkan!

4. Mencari dan Mengikuti Jalur

Pencarian jalur dan mengikuti jalur adalah proses yang rumit. Ada beberapa pendekatan menggunakan berbagai algoritma untuk menemukan jalur antara dua titik, tapi karena levelData adalah array 2D, proses ini akan lebih mudah. Kita punya titik-titik unik yang bisa dilewati pemain, dan mudah diperiksa apakah bisa dilewati atau tidak.

Artikel Terkait

Penjelasan detail dari algoritma pathfinding di luar cakupan artikel ini, tapi saya akan coba menjelaskan cara umumnya bekerja: algoritma jalur terpendek, dengan A* dan Dijkstra adalah contoh implementasi yang paling terkenal.

Kita ingin mencari titik yang menghubungkan titik awal dan titik akhir. Dari titik awal, kita kunjungi delapan titik tetangga dan tandai mereka bahwa sudah dikunjungi; proses inti ini diulangi untuk setiap titik baru yang dikunjungi, secara rekursif.

Setiap thread mencatat titik yang sudah dikunjungi. Saat melompat ke titik tetangga, titik yang sudah dikunjungi dilewati (rekursi berhenti); jika tidak, proses dilanjutkan sampai mencapai titik akhir, di mana rekursi akan berhenti dan jalur akhir dikembalikan sebagai array berisi titik. Kadang titik akhir tidak pernah dicapai, dalam hal ini pencarian jalur dianggap gagal. Kita biasanya mendapat lebih dari satu jalur diantara dua titik, dalam hal ini kita perlu mengambil jalur dengan jumlah titik paling sedikit.

Mencari Jalur

Tidak bijak untuk berusaha menciptakan ulang algoritma yang sudah umum, kita sebaiknya menggunakan solusi yang sudah ada untuk keperluan pencarian jalur kita. Untuk menggunakan Phaser, kita membutuhkan solusi JavaScript, dan yang saya pilih adalah EasyStarJS. Kita menginisialisasi engine pencarian jalur sebagai berikut.

Karena levelData hanya memiliki 0 dan 1, kita bisa langsung melemparnya sebagai array titik. Kita mengatur nilai 0 sebagai titik yang bisa dilewati. Kita membolehkan berjalan diagonal tapi mencegahnya saat berjalan dekat sudut atau petak yang tidak bisa dilewati.

Hal ini karena jika diperbolehkan, pemain bisa memotong jalan ke petak yang tidak bisa dilewati saat berjalan diagonal. Dalam kasus tersebut, deteksi tabrakan tidak akan memperbolehkan pemain untuk menembus. Perlu diperhatikan juga bahawa pada contoh tersebut saya menghilangkan deteksi tabrakan karena tidak diperlukan untuk contoh jalan berbasis AI.

Kita akan mendeteksi ketukan pemain di petak kosong di dalam level dan mengkalkulasi jalur menggunakan fungsi findPath. Pemanggilan fungsi plotAndMove menghasilkan array titik dari jalur yang didapat. Kita menandai minimap dengan jalur yang ditemukan.

Isometric level with the newly found path highlighted in minimap

Mengikuti Jalur

Begitu kita dapat jalur sebagai array titik, kita perlu membuat karakter kita mengikutinya.

Misalnya kita mau membuat karakter bergerak ke petak yang kita klik. Kita perlu mencari jalur antara titik posisi karakter dan titik yang kita klik. Jika jalur yang sukses ditemukan, kita perlu menggerakkan karakter ke titik pertama di array titik dengan mengaturnya sebagai tujuan. Begitu kita mencapai titik tujuan, kita periksa apakah ada titik lain di array titik dan jika iya, atur titik berikutnya sebagai tujuan, dan seterusnya sampai kita mencapai titik akhir.

Kita juga akan mengubah arah hadap pemain berdasarkan titik saat ini dan titik tujuan setiap kali kita mencapai sebuah titik. Antara titik kita hanya berjalan ke arah yang dibutuhkan sampai mencapai titik tujuan. Ini adalah AI yang sangat sederhana, dan pada contoh dilakukan dalam fungsi aiWalk ditujukkan sebagian di bawah ini.

Kita perlu menyeleksi klik yang sah dengan menentukan apakah kita menekan titik yang bisa dilewati, atau titik yang tidak bisa dilewati seperti tembok.

Hal lain yang menarik saat menulis kode untuk AI: kita tidak ingin karakter berputar ke arah petak beirkutnyan di array titik begitu dia sampai di titik saat ini, hal itu akan membuat karakter bergerak di batas ujung petak. Kita sebaiknya menunggu sampai karakter beberapa langkah di dalam petak sebelum kita melihat titik berikutnya. Lebih baik lagi untuk menempatkan karakter di tengah petak saat ini sebelum kita berputar, untuk membuatnya terasa sempurna.

Lihatlah demo di bawah ini:

5. Isometric Scrolling

Saat area level lebih besar dari area layar, kita perlu membuatnya bergulir.

Isometric level with 12x12 visible area

Area layar yang terlihat bisa dianggap sebagai persegi kecil di dalam persegi yang lebih besar yang berisi area level yang lengkap. Menggulir adalah aksi menggeser persegi kecil di dalam persegi besar. Biasanya saat peta bergulir terjadi, posisi pemain tetap sama terhadap persegi di layar, biasanya ada di tengah layar. Menariknya, yang perlu kita lakukan untuk membuat peta bergulir adalah memperhatikakn posisi sudut dari persegi dalam.

Titik sudut ini, yang ditulis dalam koordinat Kartesian, akan berada dalam sebuah petak dalam data level. Untuk menggulir peta, kita tambahkan posisi x dan y dari titik sudut dalam koordinat Kartesian. Sekarang kita bisa mengubah titik tersebut menjadi koordinat isometric dan menggunakannya untuk menggambar layar.

Nilai yang baru diubah dalam ruang isometric ini perlu menjadi sudut pada layar kita, yang artinya mereka adalah titilk (0,0) yang baru. Jadi saat mengurai dan menggambar data level, kita kurangi nilai ini dari posisi isometric semua petak, dan tentukan jika posisi barunya ada di dalam layar.

Selain itu, kita bisa menentukan untuk hanya menggambar sejumlah X x Y petak di layar agar loop render kita efisien untuk level yang lebih besar.

Kita bisa menulis cara tersebut dalam langkah-langkah sebagai berikut:

  • Perbarui nilai x dan y koordinat Kartesian titik sudut.
  • Ubah menjadi titik pada ruang isometric.
  • Kurangi nilai ini dari posisi isometric setiap petak.
  • Gambar sejumlah petak dalam jumlah tertentu di layar mulai dari sudut yang baru.
  • Opsional: Gambar petak hanya jika posisi isometric ada di dalam layar.

Perlu diingat bahwa titik sudut ditingkatkan dengan arah yang berlawanan dari posisi pemain saat dia bergerak. Hal ini memastikan pemain tetap berada di posisi yang sama di layar. Lihatlah contoh berikut (gunakank panah untuk bergulir, ketuk untuk menambahkan area yang terlihat).

Beberapa catatan:

  • Saat menggulirkan peta, kita perlu menggambar petak tambahan di batas layar, atau kita bisa melihat petak menghilang dan muncul di batas layar.
  • Jika kamu punya petak yang lebih besar dari satu petak, kamu perlu menggambar petak di perbatasan. Contohnya, jika petak terbesar di level berukuran X dan Y, kamu perlu menggambar sejumlah X petak ke kiri dan kanan, dan sejumlah Y ke atas dan bawah. Hal ini memastikan bahwa sudut dari petak yang lebih besar akan tetap terlihat saat bergulir masuk atau keluar dari layar.
  • Kita perlu memastikan kita tidak memiliki area kosong di layar saat menggambar dekat perbatasan level.
  • Level hanya boleh bergulir sampai petak ujung digambar pada ujung layar, setelah ini, karakter seharusnya berjalan di layar tanpa peta bergulir. Untuk ini, kita perlu mencatat empat sudut dari persegi dalam layar, dan mengatur peta bergulir dan logika pergerakan pemain dengan sesuai. Apakah kamu menerima tantangan untuk mengimplementasinya sendiri?

Kesimpulan

Seri ini ditujukan pada pemula yang mencoba eksplorasi dunia game isometric. Banyak konsep yang dijelaskan memiliki pendekatan lain yang lebih rumit, dan saya sengaja memilih pendekatan yang lebih sederhana.

Pendekatan tersebut mungkin tidak memenuhi semua skenario yang akan kamu hadapi, tapi pengetahuan yang didapat bisa dikembangkan untuk membuat solusi lain yang lebih rumit. Contohnya, fitur depth sorting yang kita implementasi akan gagal saat kita memiliki level dengan banyak tingkat, atau petak platform yang bergerak satu sama lain.

Tapi itu tutorial untuk kesempatan lain.

Advertisement
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.