Kamis, 15 Agustus 2013

Construct 2 RPG tutorial 5: making AI

Jadi sebelumnya aku sudah mulai bikin AI sederhana (sekali) untuk musuh, walaupun hasil akhirnya adalah musuh baru mengikuti pemain saja. Sebelum aku membuat AI dalam bentuk code yang lebih kompleks, aku ingin merancangnya terlebih dulu. Oh iya, walaupun contoh kasusnya untuk musuh, AI ini nantinya bukan cuma untuk musuh saja, tapi juga party member lain dalam kelompok pemain. Dan juga, AI untuk boss juga akan kusamakan dengan AI untuk musuh biasa, hanya saja nilai parameternya kubuat lebih susah.

Baik musuh maupun anggota party member ada yang berperan sebagai penyerang jarak dekat dan penyerang jarak jauh, ada pula penyerang fisik dan penyerang sihir. Jadi dibagi berdasarkan tipenya, musuh dan party member bisa dikategorikan seperti ini:
  • Penyerang fisik jarak dekat
  • Penyerang fisik jarak jauh
  • Penyerang sihir jarak jauh
  • Penyembuh / support
Aku nggak menuliskan penyerang sihir jarak dekat, karena aku menganggap semua penyerang sihir adalah penyerang jarak jauh, lagipula kalau penyerang sihir menyerang dari jarak dekat dia bisa diserang duluan sebelum sempat mengeluarkan sihirnya. Masing-masing penyerang fisik akan punya radius serangan yang jaraknya beda antara jarak dekat dan jarak jauh, penyerang fisik akan mendekat sampai musuh masuk dalam radius serangan sebelum akhirnya menyerang. Penyerang sihir nggak perlu mendekat buat menyerang musuh, jarak jangkau sihirnya adalah seluruh battle screen, jadi mereka bisa menyerang musuh tanpa peduli jarak. Bedanya adalah sihir perlu casting time untuk bisa dirapalkan dan mereka bisa diserang sewaktu ini.


Jadi bagaimana kita akan membuat AI? Aku akan membedakan antara AI penyerang fisik dan penyerang sihir, inilah kira-kira proses logika AI penyerang fisik:
  1. Kalau belum ada musuh dalam radius serangan, cari musuh
  2. Dekati musuh sampai masuk dalam radius serangan
  3. Serang
  4. Tentukan apakah akan bertahan atau tidak?
  5. Kalau musuh yang tadi diserang masih hidup, kembali ke poin 3
  6. Kalau musuh yang tadi diserang sudah mati, kembali ke poin 1
Ini masih sekedar gambaran kasar, masih bisa dijabarkan lebih banyak lagi, misalnya poin 3. Apakah menyerang itu dengan serangan biasa atau dengan skill? Kalau dengan skill, skill yang mana yang dipakai? Dan juga, kalau aku membuatnya begini, nantinya aku nggak bisa membuat sistem costumize AI untuk party member yang lain (misalnya, utamakan menyerang musuh dengan HP tertinggi, utamakan menyerang penyihir, dsb.). Jadi 6 poin ini akan aku perjelas sambil jalan.

Semua petarung baik musuh maupun party member punya instance variable bernama radius, kalau nggak ada musuh dalam radius ini, petarung akan menentukan akan mendekati musuh yang mana. Untuk melakukan ini, di awal pertarungan semua petarung harus sudah tau tentang siapa saja musuh-musuh yang hadir di battle screen (party member tau tentang musuh, musuh tau tentang party member). Ada dua cara untuk melakukan ini, dengan menambahkan instance variable baru yang mengandung data tentang nama musuh, atau dengan family baru yang bisa dibaca semua petarung. Karena masing-masing kubu mendapatkan data yang sama tentang musuh mereka kapanpun juga sewaktu pertarungan berlangsung, aku akan membuat dua family yang berisi data siapa saja party member dan siapa saja musuh yang masih hidup dalam battle. Buatku, ini lebih efisien daripada meng-update instance variable semua petarung dalam battle.

Ada beberapa logika yang bisa kita pakai untuk mencari musuh mana yang akan jadi target serangan petarung:
  1. Jaraknya terdekat dengan petarung
  2. Jaraknya terjauh dengan petarung
  3. Current HP-nya terbanyak
  4. Current HP-nya paling sedikit
  5. Utamakan menyerang penyihir
Karena setiap petarung akan menyimpan logika pemilihan musuh mana yang akan mereka pakai, maka perlu satu lagi instance variable logika pemilihan musuh, untuk sementara aku namakan instance variable ini enemySelection. Logika-logika pemilihan ini juga akan aku beri nama dengan tipe string.
  1. Jaraknya terdekat = "closest"
  2. Jaraknya terjauh = "farthest"
  3. HP terbanyak = "strongest"
  4. HP paling sedikit = "weakest"
  5. Utamakan penyihir = "mages"
dengan begini akan mudah nantinya sewaktu kita menulis dalam code.

Kemudian setelah menentukan bagaimana petarung memilih musuh, petarung akan mendekati musuh. Tentunya petarung harus tau musuh mana yang harus didekati dan nggak mengganti targetnya sampai targetnya mati. Jadi nanti ada lagi instance variable baru bernama targetName yang berisi nama target yang akan diserang. Tapi di sini ada satu masalah: bagaimana kalau ada lebih dari satu musuh yang punya nama sama, misalnya ada tiga yellow wolf dalam satu battle screen, yang mana yang akan dijadikan target? Penyelesaiannya adalah dengan memberi instance variable baru di family yang menyimpan musuh yang bertarung, kalau kita beri battleName dan membuat battleName ini berisi seperti "yellowWolf1", "yellowWolf2" dan seterusnya, petarung bisa menyerang target yang tepat.

Langkah berikutnya adalah menyerang. Menyerang bisa dengan serangan biasa atau dengan skill. Dan akan ada jeda di antara tiap serangan (tapi kita sudah coding soal jeda serangan ini sebelumnya). Tiap petarung punya persentase serangan sendiri antara serangan biasa dan skill, ada yang cuma menyerang biasa saja, ada yang lebih banyak menyerang skill. Sama seperti logika pemilihan musuh, aku juga akan menamai dan menyimpan logika menyerang ini dalam instance variable bernama attackMethod. Sekarang mari kita list apa saja logika penyerangannya.
  1. Hanya serangan biasa saja = "normals"
  2. Gunakan skill, tapi sedikit saja = "few"
  3. Gunakan cukup banyak skill (50:50) = "balanced"
  4. Lebih sering menggunakan skill daripada serangan biasa = "frontal"
masing-masing logika serangan punya persentase serangan biasa:serangan skill masing-masing, nantinya penentuan apakah petarung akan menyerang dengan skill atau tidak dilihat berdasarkan persentase itu. Tentu saja petarung cuma menyerang dengan skill kalau SP mereka mencukupi, kalau nggak mereka akan menyerang dengan serangan biasa.

Pertanyaan berikutnya adalah: kalau petarung memutuskan untuk menyerang dengan skill, skill apakah yang dipakai? Sebenarnya susah juga menentukan skill apa yang akan dipakai dan tidak dipakai, karena nantinya masing-masing petarung akan punya banyak skill, dan buatku susah untuk membuat AI yang memilih hanya satu dari sekian banyak itu (kecuali memang sudah ditentukan dari code bahwa petarung harus memilih skill itu). Jadi tiap kali petarung akan memilih skill, dia akan memilih secara acak skill apa yang akan dia pakai dari list skill aktif yang mereka miliki (skill bisa diaktifkan dan nonaktif, nanti akan aku desain lebih jelas).

Langkah terakhir adalah menentukan apakah petarung akan bertahan atau tidak. Sebenarnya petarung nggak harus masuk tahap bertahan sesudah menyerang (supaya bisa diserang oleh pemain lebih mudah), tapi aku tambahkan kemampuan bertahan untuk membuat pertarungan lebih seru dan menantang. Sama seperti logika menyerang, aku akan membuat persentase apakah petarung akan bertahan atau nggak lalu menyimpannya dalam instance variable, kali ini bernama defenseMethod. Logika-logika pertahanannya adalah seperti di bawah ini.
  1. Nggak bertahan sama sekali = "defenseless"
  2. Mungkin saja bertahan = "defensive"
  3. Lebih sering bertahan (kemungkinan 50:50) = "defender"
  4. Hampir selalu bertahan = "guardian"
kalau petarung memutuskan untuk bertahan, dia akan memasuki mode bertahan selama 2 detik sebelum melanjutkan ke poin 5 dari alur AI yang kita buat di awal tadi.


Sebenarnya aku juga ingin menulis soal AI para penyihir, tapi karena sepertinya blog post kali ini sudah terlalu panjang, mungkin aku akan menulisnya di blog post berikutnya. Sampai ketemu.