Advertisement
  1. Game Development
  2. Platformer
Gamedevelopment

Dasar Physics Platformer 2D, Bagian 8: Bidang Miring

by
Difficulty:IntermediateLength:LongLanguages:

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

Final product image
What You'll Be Creating

Demo

Demo itu menunjukkan hasil akhir dari implementasi bidang miring. Gunakan WASD untuk menggerakkan karakter. Tombol mouse kanan membuat sebuah petak. Kamu bisa menggunakan rola ulir atau tombol panah untuk memilih petak yang ingin kamu tempatkan. Slider mengubah ukuran karakter pemain.

Demo ini dirilis pada Unity 5.5.2f1, dan source code juga kompatibel untuk versi Unity tersebut.

Sebelum kita mulai...

Seperti bagian sebelumnya pada seri ini, kita akan melanjutkan pekerjaan dari bagian sebelumnya. Sebelumnya kita menghitung dan menyimpan data yang kita butuhkan untuk menggerakkan objek keluar dari tabrakan dengan bidang miring dan mengubah bagaimana pengecekan tabrakan dijalankan terhadap denah petak. Pada bagian ini kita membutuhkan pengaturan yang sama dengan bagian akhir dari seri sebelumnya.

Kamu bisa mengunduh file proyek bagian sebelumnya dan menulis kode didampingi tutorial ini.

Pada bagian ini kita akan mengimplementasi tabrakan dengan bidang miring atau petak khusus lain, menambahkan bidang miring satu arah, dan membuat objek game bisa berjalan di  sepanjang bidang miring dengan mulus.

Implementasi bidang miring

Pengecekan vertikal terhadap bidang miring

Akhirnya kita mencapai implementasi bidang miring! Pertama, kita coba untuk menangani kondisi saat sisi bawah objek ada di dalam petak bidang miring.

Kita lihat fungsi CollidesWithTileBottom, terutama bagian di mana kita menangani petak.

Untuk bisa melihat apakah objek kita bertabrakan dengan bidang miring, kita perlu offset dari fungsi yang kita buat sebelumnya, yang menjalankan sebagian besar dari tugas kita.

Karena kita hanya memeriksa satu piksel di bawah karakter kita, kita perlu mengatur offset.

Kondisi untuk tabrakan adalah offset freeUp lebih besar atau sama dengan nol, yang berarti antara kita menggerakkan karakter ke atas, atau karakter berdiri di bidang miring.

Jangan lupa dengan kasus saat kita ingin karakter menempel pada bidang miring. Ini berarti walau karakter bergerak keluar bidang miring, kita ingin karakter berlaku seperti ada di atas bidang miring tersebut. Untuk ini, kita perlu menambahkan sebuah nilai konstan yang akan menyimpan nilai seberapa miring sebuah bidan guntuk dianggal tembok vertikal, bukan bidang miring.

Jika offset di bawah ini sama dengan nilai konstanta, seharusnya mungkin untuk sebuah objek bergerak dengan mulus pada bidang miring. Jika sama atau lebih besar, bidang ini perlu dianggap tembok, dan aksi lompat akan dibutuhkan untuk bisa memanjatnya.

Sekarang kita perlu menambah sebuah kondisi untuk pernyataan kita. Kondisi ini akan memeriksa apakah karakter seharusnya menempel pada bidang miring, apakah pada frame sebelumnya karakter berada di atas bidang miring, dan apakah karakter perlu didorong ke bawah atau atas sejumlah piksel yang lebih sedikit dari konstanta cSlopeWallHeight.

Jika kondisi tersebut benar, kita perlu menyimpan petak ini sebagai kandidat yang bisa menabrak  objek. Kita masih perlu mengiterasi semua petak sepanjang sumbu X. Pertama, buat variabel yang akan menyimpan koordinat X dan nilai offset untuk petak yang tabrakan.

Sekarang simpan nilai-nilainya, jika kondisi tersebut bernilai benar. Jika kita sudah menemukan petak yang tabrakan, kita perlu membandingkan offset, dan petak terakhir yang tabrakan adalah kita gunakan untuk mengatur offset karakter.

Akhirnya, setelah kita mengiterasi seluruh petak dan menemukan petak yang tabrakan dengan objek, kita perlu mengatur offset objek tersebut.

Sekian untuk pemeriksaan bagian bawah, sekarang kita buat untuk bagian atas. Bagian ini akan lebih sederhana, kita bahkan tidak perlu menangani kondisi menempel.

Sekian.

Pengecekan horizontal terhadap bidang miring

Pengecekan horizontal akan lebih rumit, karena di sini kita akan menangani berbagai kasus yang menyulitkan.

Kita mulai dengan menangani bidang miring di sebelah kanan. Ada beberapa hal yang kita perlu ketahui, terutama terkait bergerak ke atas bidang miring. Kita pertimbangkan kondisi berikut.

Different shaped slopes

Kita perlu menangani kasus-kasus tersebut dengan hati-hati karena di satu titik ketika kita begerak di bidang miring kita akan menabrak langit-langit. Untuk mencegahnya, kita perlu melakukan pemeriksaan jika karakter bergerak secara horizontal.

Untuk pengecekan vertikal, kita menggerakkan objek ke atas sebuah petak, tapi pada umumnya kita tidak akan menggunakan fungsionalitas itu di sini. Karena kita selalu memeriksa piksel yang tepat di luar batas objek, kita tidak pernah benar-benar tumpang tindih dengan sebuah rintangan. Untuk pemeriksaan horizontal akan sedikit berbeda, karena ini adalah tempat di mana kita menangani gerakan sepanjang bidang miring, jadi pengaturan ketinggian akan dilakukan pada fungsi ini.

Untuk membuat respon tabrakan yang benar untuk kasus pada gambar di atas, akan lebih mudah untuk memeriksa apakah kita bisa masuk sebuah ruang secara horizontal, dan jika memungkinkan lalu periksa apakah objek tidak tumpang tindih dengan piksel solid jika harus digerakkan secara vertikal karena bergerak pada bidang miring. Jika kita gagal menemukan ruang tersebut, kita tahu bahwa tidak mungkin untuk bergerak ke arah tersebut, dan kita ibisa atur penanda tembok horizontal.

Kita lanjutkan ke fungsi CollidesWithTileRight, ke bagian di mana kita menangani bidang miring.

kita dapatkan offset dengan cara yang sama pada pengecekank vertikal, tapi offset yang kita butuhkan adalah yang lebih besar.

Sekarang, kita lihat apakah karakter kita perlu memperlakukan petak yang diperiksa sebagai tembok. Kita lakukan ini jika offset bidang miring lebih besar atau sama dengan konstanta cSlopeWallHeight atau untuk keluar dari tabrakan kita perlu menggeser karakter ke atas atau bawah saat kita sudah menabrak petak di arah yang sama, artinya objek kita berada di tengah-tengah petak atas dan bawah.

Jika bukan itu dan offset lebih besar dari nol, artinya kita menabrak bidang miring. Satu masalah di sini adalah kita tidak tahu apakah kita menabrak tembok di petak lain yang belum kita periksa, jadi saat ini kita hanya simpan offset bidang miring dan jenis tabrakan siapa tahu akan kita butuhkan nanti.

Sekarang kita tidak memeriksa offset bidang miring lebih besar dari nol, melainkan kita bandingkan dengan offset bidang miring pada petak lain, jika kita sudah menemukan tabrakan dengan bidang miring pada iterasi sebelumnya.

Menangani kasus terjepit di antara petak

Setelah kita selesai memproses semua petak yang diperlukan, kita lihat apakah kita perlu menggerakkan objek. Kita tangani kasus di mana offset bidang miring bernilai bukan nol.

Kita perlu menangani dua kasus di sini, dan kita perlu melakukan hal yang sedikit berbeda tergantung apakah kita perlu offset objek ke atas atau ke bawah.

Pertama, kita perlu memeriksa apakah kita bisa menempati ruang setelah menggeser objek. Jika iya, kita menangani satu dari kasus yang digambarkan di atas. Karakter mencoba untuk bergerak ke kanan, offset positif, namun jika kita menggeser objek maka akan terdorong ke tembok atas, jadi kita tandai bahwa objek bertabrakan dengan tembok di kanan dan mencegah pergerakkan ke arah tersebut.

Jika kita cukup dalam ruang tersebut, kita tandai bahwa kita tabrakan dengan petak bawah dan menggeser posisi objek sesuai kebutuhan.

Kita tangani kasus di mana objek perlu digeser ke bawah dengan cara yang sama.

Objek yang bergerak saat pengecekan tabrakan

Fungsi ini akan menggeser objek ke atas atau bawah sesuai kebutuhan jika kita ingin melangkah ke petak di kanan, tapi bagaimana jika kita ingin menggunakan fungsi ini hanya untuk pengecekan, dan kita tidak ingin menggerakkan karakter saat memanggil fungsi ini? Untuk memecahkan masalah ini, kita tambahkan variabel bernama 'move' untuk menandai apakah fungsi bisa menggerakkan objek atau tidak.

Dan gerakkan objek hanya jika penanda ini bernilai true.

Menangani kondisi menempel pada bidang miring

Sekarang kita tangani kondisi menempel pada bidang miring. Cukup sederhana, tapi kita perlu menangani semua kasus sudut dengan benar, agar karakter menempel pada bidang miring tanpa gangguan sepanjang jalan.

Sebelum kita tangani kasus sudut, kita bisa menangani kondisi menempel pada bidang miring pada suatu petak dalam pengecekan tabrakan vertikal. Hal ini cukup dengan menambahkan kondisi berikut pada fungsi CollidesWithTileBottom.

Kondisi ini membuat karakter terdorong ke bawah jika jarak antara posisi objek dan tanah terdekat di antara nol dan cSlopeWallHeight. Sayangnya hal ini hanya berlaku dalam satu petak saja, gambar berikut menunjukkan masalah yang perlu kita pecahkan.

Slope with three squares marked on it

Kasus sudut yang kita bicarakan adalah sebagai berikut: karakter bergerak ke bawah dan kiri dari petak nomor satu ke petak nomor dua. Petak nomor dua kosong, jadi kita perlu cek petak di bawahnya dan lihat apakah offset karakter ke petak nomor tiga sudah cukup untuk terus berjalan sepanjang bidang miring di situ.

Menangani kasus sudut

Akan lebih mudah untuk menangani kasus sudut ini pada pemeriksaan tabrakan horizontal, jadi kita kembali ke fungsi CollidesWithTileRight. Kita lihat akhir fungsi dan tangani kasus tersebut di sana.

Pertama, untuk menangani kondisi menempel pada bidang miring, penanda mSticksToSlope perlu diatur, dan objek harus ada di tanah pada frame sebelumnya, dan penanda gerakkan harus menyala.

Sekarang kita perlu temukan petak yang harus kita tempel. Karena fungsi ini memeriksa tabrakan pada sisi kanan objek, kita akan menangani kondisi menempel untuk sisi kiri bawah karakter.

Sekarang kita perlu cari cara untuk membandingkan tinggi objek saat ini dengan objek yang akan dilewati. Jika tinggi berikutnya lebih rendah dari yang sekarang, tapi lebih tinggi dari konstanta cSlopeWallHeight, kita dorong objek kita ke tanah.

Mendapatkan ketinggian bidang miring

Kita kembali ke kelas Slope untuk membuat fungsi yang akan mengembalikan tinggi dari bidang miring di suatu posisi tertentu.

Parameter untuk fungsi ini adalah nilai x pada bidang miring dan jenis bidang miring. Jika bidang miring kosong kita langsung kembalikan nol dan jika penuh maka kita kembalikan ukuran petak.

Kita bisa mendapatkan tinggi bidang miring dengan menggunakan offset yang kita simpan. Jika petak tidak ditransformasikan, kita cukup mengambil offset untuk sebuah objek dengan lebar satu piksel pada posisi x, dan tingginya sama dengan tinggi petak.

Kita tangani kasus ini untuk berbagai transformasi yang berbeda. Jika bidang miring dicerminkan pada sumbu X, kita cukup mencerminkan parameter x.

Jika bidang miring dicerminkan pada sumbu Y, kita perlu mengembalikan offset collidingTop, bukan collidingBottom. Karena collidingTop dalam kasus ini akan negatif, kita juga perlu mengubah tandanya.

Akhirnya, jika petak diputar 90 derajat, kita perlu mengembalikan offset collidingLeft atau collidingRight. Selain itu, untuk mendapatkan offset yang sudah tersimpan, kita perlu menukar posisi x dan y dan ukurannya.

Itu adalah fungsi akhirnya.

Kembali ke kasus sudut

Kita kembali ke fungsi CollidesWithTileRight, tepat di mana kita menentukan jenis bidang miring untuk petak yang dilewati karakter.

Untuk menggunakan fungsi yang baru kita buat, kita perlu menentukan posisi di mana kita ingin mendapatkan tinggi petak.

Sekarang kita hitung tinggi antara dua titik tersebut.

Jika offset bernilai antara nol dan konstanta cSlopeWallHeight, maka kita perlu mendorong objek ke bawah, namun kita perlu memeriksa apakah kita bisa mendorong objek ke bawah. Ini adalah aksi yang sama dengan sebelumnya.

Lengkapnya, fungsi ini akan terlhat sebagai berikut.

Sekarang kita perlu melakukan semuanya lagi untuk fungsi CollidesWithTileLeft. Versi akhir fungsi tersebut akan seperti berikut ini.

Sekitan. Kode tersebut seharusnya bisa menangani berbagai jenis bidang miring yang tidak ditranslasi.

Animation of character moving on slope

Menangani jenis translasi

Sebelum kita menangani petak yang ditransformasi, kita b uat beberapa fungsi yang akan mengembalikan apakah sebuah TileCollisionType ditranslasikan. Enum jenis tabrakan kita struktur sebagai berikut.

Kita bisa menggunakan pola ini untuk mengetahui dari nilai enum apakah sebuah tabrakan ditranslasi. Kita mulai dengan mengidentifikasi pencerminan di sumbu X.

Pertama, kita ambil id bidang miring. Kita lakukan itu dengan menghitung offset dari petak bidang miring pertama ke petak yang ingin kita periksa.

Kita punya delapan jenis translasi, sekarang yang perlu kita lakukan adalah mendapatkan sisa pembagian typeId dengan 8.

Sekarang sebuah angka sudah ditunjuk untuk setiap translasi.

Percerminan pada sumbu X terlihat pada tipe 1, 3, 5, dan 7, jadi fungsi perlu mengembalikan nilai true, jika tidak, kembalikan false.

Dengan cara yang sama, kita buat fungsi yang memberitahu apakah sebuah tipe dicerminkan pada sumbu Y.

Dan akhirnya, jika tipe tabrakan dirotasi.

Itu saja yang kita butuhkan.

Transformasi offset

Kita kembali ke kelas Slopes dan buat fungsi GetOffset mendukung petak yang ditranslasi.

Seperti biasa, karena kita tidak punya data yang tersimpan untuk bidang miring yang ditranslasi, kita akan melakukan translasi terhadahp posisi dan ukuran objek jadi  hasilnya sama dengan seperti petak ditranslasikan. Kita mulai dengan pencerminan pada sumbu X. Kita perlu mencerminkan objek terhadap tengah petak.

Begitu pula untuk pencerminan pada sumbu Y.

Sekarang jika kita cerminkan petak pada sumbu Y, offset yang kita terima sebenarnya tertukar. Kita translasikan agar bekerja seperti offset pada petak yang tidak ditranslasi, artinya atas berarti atas dan bawah berarti bawah.

Sekarang kita tangani rotasi 90 derajat.

Di sini semua seharusnya dirotasi 90 derajat, jadi kita tidak akan menggunakan sisi kiri dan kanan objek untuk menentukan posX dan sizeX, kita akan menggunakan sisi atas dan bawah.

Sekarang kita perlu melakukan hal yang sama seperti sebelumnya jika petak dicerminkan pada sumbu Y, tapi kali ini kita perlu melakukannya untuk rotasi 90 derajat dan pencerminan sumbu Y.

Cukup sekian. Karena offset akhir untuk atas dan bawah diatur untuk ruang dunia, pengaturan batas petak kita akan tetap berjalan dengan benar.

Sekarang kita bisa menggunakan bidang miring yang sudah ditranslasi.

Animation of character moving on slope

Pada animasi di atas, terdapat bidang miring 45, 22, 15, dan 11 derajat. Dengan bantuan rotasi 90 derajat, kita juga bisa mendapatkan bidang 79, 75, dan 68 derajat tanpa mendefinisikan petak bidang miring tambahan. Kamu juga bisa lihat bidang miring 79 derajat terlalu curam untuk bergerak dengan mulkus karena nilai cSlopeWallHeight.

Menangani pijakan satu arah

Karena semua perubahan ini, kita membuat pijakan satu arah kita tidak bekerja. Kita perlu memperbaikinya, dan menambahkan fungsinya untuk bidang miring. Pijakan satu arah sama pentingnya dengan petak solid, jadi kita tidak bisa kehilangan fitur itu.

Menambahkan tipe satu arah

Hal pertama yang perlu kita lakukan adalah menambahkan jenis tabrakan baru untuk pijakan satu arah. Kita tambahkan mereka setelah tipe tabrakan biasa dan tandai di mana tipe baru ini dimulai, jadi nantinya kita akan lebih mudah mengetahui apakah suatu tabrakan adalah satu arah atau bukan.

Sekarang semua pijakan satu arah ada di antara enum OneWayStart dan OneWayEnd, jadi kita bisa dengan mudah membuat fungsi yang akan mengembalikan informasi tersebut.

Varian satu arah dari bidang miring akan menggunakan data yang digunakan pijakan biasa, jadi tidak perlu takut kita akan menggunakan memori tambahan.

Menangani data tambahan

Sekarang kita tambahkan variabel yang membuat sebuah objek mengabaikan pijakan satu arah. Yang pertama adalah penanda objek yang menandai objek secara permanen untuk mengabaikan pijakan satu arah (yang berguna untuk monster terbang dan objek yang tidak perlu menggunakan pijakan), dan penanda lain untuk mengabaikan tabrakan dengan pijakan satu arah untuk sementara, hanya untuk membuat kita bisa jatuh menembusnya.

Variabel pertama akan ada di dalam kelas MovingObject.

Variabel kedua ada di dalam struktur PositionState.

Kita juga akan menambahkan variabel yang akan menyimpan koordinat Y dari pijakan yang ingin kita lewati.

Untuk membuat pijakan satu arah bekerja, kita cukup mengabaikan satu lapis pijakan horizontal. Saat kita memasukan lapisan lain, itu adalah saat posisi Y karakter kita sudah berubah di koordinat peta, lalu kita atur karakter tersebut untuk kembali bertabrakan dengan pijakan satu arah lagi.

Ubah pengecekan tabrakan

Kita kembali ke fungsi CollidesWithTileBottom. Pertama, saat kita mengiterasi petak, kita periksa apakah petak tersebut adalah pijakan satu arah, dan jika iya, apakah kita harus menabrak petak ini atau tidak.

Kita perlu bertabrakan dengan platform satu arah hanya jika jarak dari bagian atas pijakan kurang dari cSlopeWallHeightConstant, agar kita bisa berada di atasnya. Kita tambahkan kondisi tersebut ke kondisi yang sudah ada, dan kita perlu mengisi nilai yang benar untuk state.onOneWay dan state.oneWayY.

Untuk fungsi CollidesWithTileTop, kita cukup abaikan pijakan satu arah.

Untuk pengecekan tabrakan horizontal, akan ada pekerjaan tambahan. Pertama, kita buat dua boolen tambahan di awal, yang akan menyediakan informasi apakah petak saat ini adalah satu arah, dan apakah petak sebelumnya adalah satu arah.

Sekarang kita tertarik untuk mengiterasi pijakan satu arah jika kita sedang bergerak di atasnya. Kita tidak bisa benar-benar bertabrakan dengan pijakan satu arah dari kanan atau kiri, tapi jika karakter begerak sepanjang bidang miring yang merupakan pijakan satu arah, maka perlu ditangani dengan cara yang sama seperti bidang miring biasa.

Sekarang pastikan kita tidak bisa bertabrakan dengan bidang miring seperti tembok.

Jika tidak seperti itu, dan offset cukup kecil untuk dipanjat, ingatlah bahwa kita bergerak pada pijakan satu arah.

Sekarang yang tersisa adalah memastikan setiap kita mengubah status posisi kita juga perlu memperbarui variabel onOneWay.

Melompat ke bawah

Kita perlu berhenti mengabaikan pijakan satu arah ketika kita mengubah posisi Y pada koordinat peta. Kita akan mengatur kondisi setelah pergerakan pada sumbu Y dalam fungsi Move. Kita perlu menambahkannya di akhir kasus kedua.

Dan juga di akhir kasus ketiga.

Sekian. Sekarang yang perlu kita lakukan adalah mengatur tmpIgnoresOneWay menjadi true agar karakter bisa melompat turun dari pijakan satu arah.

Kita lihat hasil jadinya.

New animation of character moving on slope

Kesimpulan

Fiuh, banyak sekali yang sudah kita kerjakan, tapi semuanya setimpal dengan hasilnya yang sangat fleksibel dan kuat. Kita bisa mendefinisikan bidang miring apa saja karena penanganan bitmap tabrakan, translasi petak, dan mengubahnya menjadi platform satu arah.

Implementasi ini belum optimal, dan saya yakin saya melewatkan banyak kesempatan optimasi menggunakan metode integrasi satu piksel kita. Saya juga cukup yakin banyak pengecekan tabrakan yang bisa dilewatkan, jadi jika kamu mengembangkan implementasi ini maka beri tahu saya di bagian komentar!

Terima kasih sudah bertahan sejauh ini dengan saya, saya harap tutorial ini berguna untuk kamu!

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.