MENAMBAHKAN MUSUH DAN MENGEMBANGKAN GRAFIS GAME 3D
4.6 MEMBUAT SKRIP TARGET REAKTIF
dikendalikan oleh nilai yang dikembalikan pada hasil. Beberapa jenis nilai pengembalian yang berbeda berfungsi di coroutine, tetapi yang paling mudah adalah mengembalikan jangka waktu tertentu untuk menunggu. Mengembalikan WaitForSeconds(1) menyebabkan coroutine berhenti sejenak selama satu detik. Buat bola, jeda selama satu detik, lalu hancurkan bola: urutan itu menyiapkan indikator visual sementara.
Gambar 4.17 Daftar Indikator visual untuk membidik
Metode baru lainnya telah ditambahkan ke kelas RayShooter, yang disebut OnGUI().
Unity hadir dengan sistem antarmuka pengguna (UI) dasar dan yang lebih canggih; karena sistem dasar memiliki banyak keterbatasan, kami akan membangun UI lanjutan yang lebih fleksibel di bab mendatang, tetapi untuk saat ini menampilkan titik di tengah layar menggunakan UI dasar jauh lebih mudah. Sama seperti Start() dan Update(), setiap MonoBehaviour secara otomatis merespons metode OnGUI(). Fungsi itu menjalankan setiap bingkai tepat setelah scene 3D dirender, menghasilkan semua yang digambar selama OnGUI() muncul di atas scene 3D (bayangkan stiker diterapkan pada lukisan lanskap).
Definisi Render adalah tindakan komputer menggambar piksel scene 3D. Meskipun scene ditentukan menggunakan koordinat XYZ, tampilan sebenarnya pada monitor Anda adalah kisi 2D piksel berwarna. Jadi untuk menampilkan scene 3D, komputer perlu menghitung warna semua piksel dalam kisi 2D; menjalankan algoritma itu disebut sebagai rendering.
Di dalam OnGUI() kode mendefinisikan koordinat 2D untuk tampilan (digeser sedikit untuk memperhitungkan ukuran label) dan kemudian memanggil GUI.Label(). Metode itu menampilkan label teks; karena string yang diteruskan ke label adalah tanda bintang (*), Anda berakhir dengan karakter yang ditampilkan di tengah layar. Sekarang jauh lebih mudah untuk membidik di game FPS kami yang baru lahir! Selalu ingat bahwa Anda dapat menekan Esc untuk membuka kunci kursor mouse. Saat kursor mouse terkunci, tidak mungkin mengklik tombol Play dan menghentikan permainan.
menjadi 2 dan biarkan X dan Z pada 1. Posisikan objek baru pada 0, 1, 0 untuk meletakkannya di lantai di tengah ruangan, dan beri nama objek Musuh. Buat skrip baru bernama ReactiveTarget dan lampirkan ke kotak yang baru dibuat. Anda akan segera menulis kode untuk skrip ini, tetapi biarkan default untuk saat ini; Anda hanya membuat file skrip karena daftar kode berikutnya mengharuskannya ada untuk dikompilasi. Kembali ke RayShooter.cs dan ubah kode raycasting sesuai dengan daftar berikut. Jalankan kode baru dan tembak target baru; pesan debug muncul di konsol alih-alih indikator bola di scene.
Gambar 4.18 Daftar Mendeteksi apakah objek target terkena.
Perhatikan bahwa Anda mengambil objek dari RaycastHit, seperti koordinat yang diambil untuk indikator bola. Secara teknis, informasi hit tidak mengembalikan hit objek game;
ini menunjukkan hit komponen Transform. Anda kemudian dapat mengakses gameObject sebagai properti transformasi.
Kemudian, Anda menggunakan metode GetComponent() pada objek untuk memeriksa apakah itu target reaktif (yaitu, jika skrip ReactiveTarget terpasang). Seperti yang Anda lihat sebelumnya, metode itu mengembalikan komponen dari tipe tertentu yang dilampirkan ke GameObject. Jika tidak ada komponen jenis itu yang dilampirkan ke objek, maka GetComponent() tidak akan mengembalikan apa pun. Anda memeriksa apakah null dikembalikan dan menjalankan kode yang berbeda dalam setiap kasus. Jika objek yang terkena adalah target reaktif, kode memancarkan pesan debug alih-alih memulai coroutine untuk indikator bola. Sekarang mari kita beri tahu objek target tentang pukulan sehingga dapat bereaksi.
Peringatkan target yang terkena
Semua yang diperlukan dalam kode adalah perubahan satu baris, seperti yang ditunjukkan pada daftar berikut.
Gambar 4.19 Daftar Mengirim pesan ke objek target
Sekarang kode pemotretan memanggil metode target, jadi mari kita tulis metode target itu. Dalam skrip ReactiveTarget, tulis kode dari daftar berikutnya. Objek target akan jatuh dan menghilang saat Anda menembaknya; lihat gambar 4.20.
Gambar 4.20 Objek target jatuh saat dipukul
Gambar 4.21 Daftar Skrip ReactiveTarget yang mati saat dipukul
Sebagian besar kode ini seharusnya sudah Anda kenal dari skrip sebelumnya, jadi kami hanya akan membahasnya secara singkat. Pertama, Anda mendefinisikan metode ReactToHit(), karena itulah nama metode yang dipanggil dalam skrip pemotretan. Metode ini memulai coroutine yang mirip dengan kode indikator bola dari sebelumnya; perbedaan utamanya adalah ia beroperasi pada objek skrip ini daripada membuat objek terpisah.
Ekspresi seperti this.gameObject merujuk ke GameObject tempat skrip ini dilampirkan (dan kata kunci this adalah opsional, sehingga kode dapat merujuk ke gameObject tanpa apa pun di depannya).
Transformasi diterapkan secara instan, tetapi Anda mungkin lebih suka melihat gerakan saat objek terguling. Setelah Anda mulai mencari topik yang lebih lanjut di luar buku ini, Anda mungkin ingin mencari tween, sistem yang digunakan untuk membuat objek bergerak dengan lancar seiring waktu. Baris kedua dari metode ini menggunakan kata kunci hasil yang sangat penting bagi coroutine, menjeda fungsi di sana dan mengembalikan jumlah detik untuk menunggu sebelum melanjutkan. Akhirnya, objek game menghancurkan dirinya sendiri di baris terakhir fungsi. Destroy(this.gameObject) dipanggil setelah waktu tunggu, seperti kode yang disebut Destroy(sphere) sebelumnya.
Pastikan untuk memanggil Destroy() di this.gameObject dan bukan hanya ini! Jangan bingung antara keduanya; this hanya merujuk ke komponen skrip ini, sedangkan this.gameObject merujuk ke objek yang dilampirkan skrip. Target sekarang bereaksi saat ditembak; Bagus! Tapi itu tidak melakukan hal lain sendiri, jadi mari tambahkan lebih banyak perilaku untuk membuat target ini menjadi karakter musuh yang tepat.
AI pengembara dasar
Target statis tidak terlalu menarik, jadi mari kita menulis kode yang akan membuat musuh berkeliaran. Kode untuk berkeliaran adalah contoh AI yang paling sederhana;
kecerdasan buatan (AI) mengacu pada entitas yang dikendalikan komputer. Dalam hal ini entitas adalah musuh dalam sebuah game, tetapi bisa juga berupa robot di dunia nyata, atau suara yang bermain catur, misalnya.
Membuat diagram cara kerja AI dasar
Ada sejumlah pendekatan berbeda untuk AI (serius, kecerdasan buatan adalah bidang penelitian utama bagi ilmuwan komputer), tetapi untuk tujuan kami, kami akan tetap menggunakan pendekatan sederhana. Saat Anda menjadi lebih berpengalaman dan permainan Anda menjadi lebih canggih, Anda mungkin ingin menjelajahi berbagai pendekatan untuk AI.
Gambar 4.22 Kecerdasan buatan
Kode sebenarnya akan terlihat cukup familiar, karena itu menggerakkan musuh ke depan menggunakan perintah yang sama seperti menggerakkan player ke depan. Kode AI juga akan menggunakan raycasting, mirip dengan tetapi dalam konteks yang berbeda dari pemotretan.
"Melihat" rintangan dengan raycast
Seperti yang Anda lihat di pengantar bab ini, raycasting adalah teknik yang berguna untuk sejumlah tugas dalam simulasi 3D. Salah satu tugas yang mudah dipahami adalah memotret, tetapi raycasting tempat lain yang berguna adalah untuk memindai di sekitar scene.
Mengingat bahwa pemindaian di sekitar scene adalah langkah dalam kode AI, itu berarti raycasting digunakan dalam kode AI.
Sebelumnya Anda membuat sinar yang berasal dari kamera, karena dari sanalah player melihat; kali ini Anda akan membuat sinar yang berasal dari musuh. Sinar pertama ditembakkan melalui bagian tengah layar, tetapi kali ini sinar akan menembak ke depan di depan karakter; Gambar 3.6 mengilustrasikan hal ini. Kemudian seperti kode penembakan yang menggunakan informasi RaycastHit untuk menentukan apakah sesuatu terkena dan di mana, kode AI akan menggunakan informasi RaycastHit untuk menentukan apakah ada sesuatu di depan musuh dan, jika demikian, seberapa jauh.
Gambar 4.23 Menggunakan raycasting untuk "melihat" rintangan
Satu perbedaan antara raycasting untuk pemotretan dan raycasting untuk AI adalah radius sinar yang terdeteksi. Untuk pemotretan sinar diperlakukan sebagai sangat tipis, tetapi untuk AI sinar akan diperlakukan sebagai memiliki penampang yang besar; dalam hal kode, ini berarti menggunakan metode SphereCast() bukan Raycast(). Alasan perbedaan ini adalah pelurunya kecil, sedangkan untuk memeriksa rintangan di depan karakter kita perlu memperhitungkan lebar karakter. Buat skrip baru bernama WanderingAI, lampirkan itu ke objek target (bersama skrip ReactiveTarget), dan tulis kode dari daftar berikutnya. Mainkan scene sekarang dan Anda akan melihat musuh berkeliaran di sekitar ruangan; Anda masih bisa menembak target dan bereaksi dengan cara yang sama seperti sebelumnya.
Gambar 4.24 Skrip dasar WanderingAI
Daftar tersebut menambahkan beberapa variabel untuk mewakili kecepatan gerakan dan dari seberapa jauh untuk bereaksi terhadap rintangan. Kemudian metode Translate() ditambahkan dalam metode Update() untuk bergerak maju terus menerus (termasuk penggunaan deltaTime untuk pergerakan frame rate-independen). Di Update() Anda juga akan melihat kode raycasting yang sangat mirip dengan skrip pemotretan sebelumnya; sekali lagi, teknik raycasting yang sama digunakan di sini untuk melihat alih-alih memotret. Sinar dibuat menggunakan posisi dan arah musuh, bukan menggunakan kamera.
Seperti yang telah dijelaskan sebelumnya, perhitungan raycasting dilakukan dengan menggunakan metode Physics.SphereCast(). Metode ini mengambil parameter radius untuk menentukan seberapa jauh di sekitar sinar untuk mendeteksi persimpangan, tetapi dalam segala hal itu persis sama dengan Physics.Raycast(). Kesamaan ini termasuk bagaimana perintah mengisi informasi hit, memeriksa persimpangan seperti sebelumnya, dan menggunakan properti jarak untuk memastikan untuk bereaksi hanya ketika musuh mendekati rintangan (sebagai lawan dari dinding di seberang ruangan).
Ketika musuh memiliki rintangan terdekat tepat di depannya, kode memutar karakter dalam jumlah semi-acak ke arah yang baru. Saya katakan "semi-acak" karena nilainya dibatasi pada nilai minimum dan maksimum yang masuk akal untuk situasi ini. Secara khusus, kami menggunakan metode Random.Range() yang disediakan Unity untuk memperoleh nilai acak di antara batasan. Dalam hal ini batasannya hanya sedikit di luar belokan kiri atau kanan yang tepat, memungkinkan karakter untuk berbelok secukupnya untuk menghindari rintangan.
Melacak keadaan karakter
Satu keanehan dari perilaku saat ini adalah musuh terus bergerak maju setelah jatuh karena dipukul. Itu karena sekarang metode Translate() menjalankan setiap frame apa pun yang terjadi. Mari kita buat sedikit penyesuaian pada kode untuk melacak apakah karakter itu hidup atau tidak—atau dengan cara lain (lebih teknis), kita ingin melacak status karakter
"hidup". Membuat kode melacak dan merespons secara berbeda terhadap keadaan objek saat ini adalah pola kode umum di banyak bidang pemrograman, bukan hanya AI. Implementasi yang lebih canggih dari pendekatan ini disebut sebagai mesin negara, atau bahkan mungkin mesin negara yang terbatas.
Definisi Finite state machine (FSM) adalah struktur kode di mana status objek saat ini dilacak, transisi yang terdefinisi dengan baik ada di antara status, dan kode berperilaku berbeda berdasarkan status.
Kami tidak akan menerapkan FSM penuh, tetapi bukan kebetulan bahwa tempat umum untuk melihat inisial FSM adalah dalam diskusi tentang AI. FSM penuh akan memiliki banyak status untuk semua perilaku berbeda dari AI yang canggih, tetapi dalam AI dasar ini kita hanya perlu melacak apakah karakter itu hidup atau tidak. Daftar berikutnya menambahkan nilai Boolean, _alive, ke bagian atas skrip, dan kode memerlukan pemeriksaan kondisional sesekali dari nilai itu. Dengan pemeriksaan itu, kode gerakan hanya berjalan saat musuh masih hidup.
Gambar 4.25 Daftar Skrip WanderingAI dengan status "hidup" ditambahkan
Skrip ReactiveTarget sekarang dapat memberi tahu skrip WanderingAI saat musuh hidup atau tidak (lihat daftar berikut).
Gambar 4.26 Daftar ReactiveTarget memberi tahu WanderingAI saat mati