Sabtu, 14 September 2013

Construct 2 RPG tutorial part 7: implementing the AI

Selamat datang di bagian ke 7 dari seri tutorial membuat RPG di Construct 2. Kali ini aku akan mencoba mengimplementasikan AI seperti yang sudah aku desain sebelumnya. Aku akan mencoba membuatnya dalam 1 blog post, tapi kalau nggak bisa, berarti blog post ini cuma mencakup AI penyerang fisik.

Pertama-tama aku ingin memastikan bahwa semua petarung di battle scene tahu tentang musuh-musuhnya, karena AI kedua tipe musuh juga akan bergantung dari data ini. Awalnya aku berencana memakai family buat keperluan ini. Tapi ternyata aku nggak bisa menambah / mengurangi member family ini lewat event, padahal buat kebutuhan ini aku harus bisa mengganti member family secara dinamis lewat event (karena tiap battle, musuhnya beda-beda, dan party member pun bisa diganti secara bebas oleh pemain). Jadi untuk mengatasi ini, aku akan membuat array yang berisi data petarung dari party member dan juga dari musuh, array ini bernama enemyPartyArr dan playerPartyArr. Kedua array ini hanya berisi maksimal 4 elemen, karena kita memang membatasi jumlah petarung di sisi player dan musuh dalam battle. Ini juga mengubah fungsi family Friendly, dulunya family ini berisi semua party member yang bertarung, tapi sekarang mungkin lebih cocok berisi semua character dalam game yang bisa bergabung bersama pemain.

Aku mengubah apa yang terjadi di event on start of layout, sekarang kita memberi nama pada battleName musuh dan juga mengisi kedua array petarung, saat ini isinya hanya ada satu tapi nanti isinya akan berbeda-beda.

Kemudian aku membuat instance variable enemySelection di kedua family. Untuk memudahkan kita untuk membedakan nantinya, di family Enemies akan aku namai playerSelection sedangkan di family Friendly aku namai enemySelection. Keduanya memiliki nilai awal "closest". Di bagian bawah, aku membuat function baru bernama "enemySelTarget" yang akan digunakan oleh musuh untuk mencari target pemain. Fungsi ini menerima dua parameter, yaitu nama musuh yang akan mengganti target, dan AI pemilihan target si musuh.


Di awal function ini, kita langsung menyimpan posisi musuh sekarang ke dalam dua local variable, kita mendapat instance musuh dari battleName musuh yang sama dengan parameter pertama (index 0), ini nanti akan kita gunakan untuk menghitung jarak dengan karakter pemain. Kemudian kita melihat isi parameter kedua dan menjalankan kode mencari target sesuai dengan nama AI. Pertama-tama kita melihat apakah nama AI adalah "closest", jika benar maka kita mulai mencari karakter pemain yang paling dekat dengan musuh ini. Kita me-loop dari 1 sampai 4 (karena jumlah party member maksimal adalah 4), kemudian jika ada member family Friendly yang bernama sama dengan elemen playerPartyArr, berarti itu adalah objek karakter pemain atau party member yang ikut dalam battle, kita akan langsung hitung jaraknya dengan musuh dengan expression distance(), hasilnya akan kita buat positif dengan expression abs() untuk kemudian kita masukkan dalam array playerDistance. Kenapa hasilnya harus kita buat positif dengan abs() lebih dulu? Karena sewaktu fungsi ini dipanggil, posisi karakter pemain bisa saja di kiri atau kanan pemain, sehingga nilai distance bisa negatif atau positif. Untuk memastikan musuh selalu memilih karakter terdekat tidak peduli apakah karakter itu ada di kiri atau kanan musuh, nilainya harus aku buat absolut.

Sesudah itu ada satu event lagi sewaktu loopindex bernilai 4, ini berarti loop sudah berakhir dan sudah waktunya untuk mencari nilai terendah dari jarak pemain. Untuk melakukannya mudah saja, kita masukkan nilai-nilai jarak tadi ke dalam expression min(), yang akan mengembalikan nilai terkecil dari nilai-nilai yang dimasukkan ke dalamnya, hasilnya akan kita simpan dalam local variable closestDistance.

Sesudah kita mendapat jarak terdekat, kita perlu tahu karakter pemain mana yang berjarak terdekat ini. Caranya mudah, karena kita sudah mengisi playerDistance dengan jarak pemain di index yang sama dengan index nama pemainnya, kita tinggal me-loop array playerDistance untuk mencari nilai closestDistance. Kalau ketemu, berarti kita dapat indexnya karakter pemain, isi local variable selectedTarget dengan elemen array playerPartyArr di index yang sama. Lalu kita kembalikan selectedTarget untuk digunakan oleh si musuh, dan akhiri loop-nya.



Setelah itu ada event untuk mencari karakter pemain yang terjauh dari musuh. Ini sebenarnya sama saja seperti fungsi untuk mencari musuh terdekat, hanya bedanya di akhir loop playerPartyLoop (sewaktu loopindex bernilai 4), kita mencari nilai terbesar untuk menemukan karakter terjauh, jadi kita mengganti expression min() dengan max(). Sesudah itu, kita mengembalikan nilai selectedTarget untuk digunakan oleh si musuh.

Dua event berikutnya adalah untuk mencari karakter dengan current HP terbanyak dan tersedikit. Logika yang dipakai di dua event ini sama saja dengan yang dipakai di logika pencarian pemain berdasarkan jarak, hanya variabelnya saja yang berbeda. Kalian pasti bisa mengerti dengan mudah. Terakhir untuk pencarian karakter penyihir ("mages"), aku rasa aku perlu semacam tag untuk menandai karakter mana yang penyihir atau bukan, mungkin akan aku tambahkan nanti.

Sesudah kita mendapatkan nama targetnya musuh, kita akan mulai menggunakan variabel ini. Pertama-tama tambahkan dulu instance variable targetName di family Enemies. Lalu kita harus menentukan di mana kita menggunakan function enemySelTarget, mungkin di awal layout adalah pilihan yang bagus, jadi panggil enemySelTarget dengan dua parameter, battleName dan juga playerSelection si yellow wolf.

Berikutnya kita akan menambahkan event untuk mendekati target (pemain) seperti sebelumnya, tapi kali ini dengan bantuan sistem AI baru. Buatlah event baru yang mengecek jikalau targetName dari Enemies tidak sama dengan kosong, kalau nggak kosong cari member family Friendly yang memiliki nama yang sama, lalu dekati dia. Kita belum mengatur soal cara musuh menyerang karena kita belum menyentuh AI penyerangan. Kalau begitu, mari kita mulai menyentuh AI menyerang.



Pertama-tama kita buat dulu instance variable di family Enemies bernama attackMethod, untuk menyimpan AI untuk menyerang, nilai default-nya adalah "normals". Di event yang membuat musuh mendekati target (pemain), sesudah musuh cukup dekat dengan targetnya tambahkan event-event baru yang mengecek nilai attackMethod musuh.

Sesudah in ikita akan mencoba membuat musuh menyerang, tapi sebelum itu kita harus menentukan di mana attack effect buat serangan musuh akan muncul. Jadi, double click di objek yellowWolf (bukan di sprite-nya) dan tambahkan dua lagi image point bernama Attack1 dan Attack2, masing-masing untuk menyerang di kanan dan kiri. Lalu kita juga harus membuat family baru bernama EnemyAttackFX yang berisi efek serangan musuh, ini sama seperti yang dimiliki pemain.



Sekarang tambahkan action di event Enemies | attackMethod = "normals". Saat ini kita baru bisa menambahkan serangan fisik karena kita belum merumuskan daftar skill musuh dan bagaimana menggunakannya. Cara musuh menyerang sama seperti pemain, dan nantinya kita juga masih harus memikirkan bagaimana cara musuh memilih attack effect, karena ga mungkin semua musuh menyerang dengan cara menggigit.



Terakhir adalah memberikan kemampuan bertahan pada musuh. Pertama-tama kita akan membuat family instance variable bernama defenseMethod dengan nilai default "defenseless". Sesudah itu kita akan membuat function baru bernama on enemyDefenseMethod. Function ini akan menerima satu parameter, yaitu nama AI pertahanan musuh, nanti function ini akan mengembalikan entah nilai 0 atau 1, kalau nilainya 0 musuh tidak akan bertahan tapi kalau 1 maka musuh akan bertahan.

Untuk membuat musuh bisa bertahan, berarti harus ada state-nya, buat satu lagi family instance variable di Enemies bernama state, nanti nilai state ini tergantung dari nilai kembalian dari on enemyDefenseMethod, kita juga perlu mengganti animasinya nanti. Kita juga harus mengatur berapa lama pemain bertahan (atau diam saja, seperti state rest karakter pemain), jadi tambahkan satu family behaviour, yaitu timer.

Sekarang, kita panggil function tadi sesudah musuh menyerang, lalu kita lihat apa return value-nya dan menentukan apa yang akan dilakukan oleh musuh sesudah itu.



Dan sama seperti karakter pemain, kita juga harus menghilangkan state musuh ketika waktunya berakhir.



Kemudian kita harus menulis code dengan cara sedemikian rupa supaya musuh nggak menyerang sewaktu mereka lagi rest atau defending. Karena itu, kita akan ubah sedikit code di bagian sewaktu pemain mulai menyerang.




Yak, seharusnya kode-kode dasar untuk AI musuh sudah selesai, tapi karena aku lagi capek dan sudah lama nggak update blog, untuk sementara aku tinggalkan seperti ini dan akan lanjut di lain waktu. Silahkan mencoba dan kalau ada bug kamu bisa menulisnya di komentar, atau mencoba mengatasi bug-nya sendiri, atau menunggu blog post-ku yang menulis soal blog itu. Sampai ketemu di post berikutnya.

(by the way, capx terbaru bisa diambil di https://dl.dropboxusercontent.com/u/22420086/My%20RPG%20project.capx )