Menjadikan Tower Defense sebagai Game Persatuan - Bagian 1

gambar

Game menara pertahanan semakin populer, dan ini tidak mengherankan - sedikit yang bisa dibandingkan dengan kesenangan mengamati garis pertahanan Anda sendiri yang menghancurkan musuh-musuh jahat! Dalam tutorial dua bagian ini, kita akan membuat game menara pertahanan di mesin Unity !

Anda akan belajar bagaimana melakukan hal berikut:

  • Buat gelombang musuh
  • Buat mereka mengikuti titik rute
  • Bangun dan tingkatkan menara, dan juga ajarkan mereka cara memecah musuh menjadi piksel kecil

Pada akhirnya, kita mendapatkan kerangka permainan, yang dapat dikembangkan lebih lanjut!

Catatan : Anda memerlukan pengetahuan Unity dasar (misalnya, Anda perlu tahu bagaimana aset dan komponen ditambahkan, apa cetakan) dan dasar-dasar C # . Untuk mempelajari semua ini, saya sarankan Anda membaca tutorial tentang Unity oleh Sean Duffy atau seri Beginning C # with Unity oleh Brian Mockley.

Saya akan bekerja di Unity untuk OS X, tetapi tutorial ini juga cocok untuk Windows.

Melalui jendela menara gading


Dalam tutorial ini, kami akan membuat game menara pertahanan tempat musuh (bug kecil) merangkak ke cookie milik Anda dan antek Anda (tentu saja, ini adalah monster!). Pemain dapat menempatkan monster di titik-titik strategis dan meningkatkannya untuk emas.

Pemain harus membunuh semua bug sampai mereka mendapatkan cookie. Setiap gelombang musuh baru menjadi semakin sulit dikalahkan. Permainan berakhir ketika Anda selamat dari semua gelombang (kemenangan!) Atau ketika lima musuh merangkak ke cookie (kalah!).

Ini adalah screenshot dari game yang sudah selesai:


Monster, satukan! Lindungi cookie!

Mulai bekerja


Unduh proyek ini kosong , buka zipnya , dan buka proyek TowerDefense-Part1-Starter di Unity.

Proyek draft memiliki aset grafik dan suara, animasi siap pakai dan beberapa skrip yang berguna. Skrip tidak terkait langsung dengan game menara pertahanan, jadi saya tidak akan membicarakannya di sini. Namun, jika Anda ingin mempelajari lebih lanjut tentang membuat animasi 2D di Unity, lihat tutorial Unity 2D ini .

Proyek ini juga mengandung cetakan, yang akan kami tambahkan nanti untuk membuat karakter. Akhirnya, ada adegan dalam proyek dengan latar belakang dan antarmuka pengguna yang disesuaikan.

Buka GameScene yang terletak di folder Adegan dan atur mode Game ke rasio aspek 4: 3 sehingga semua label cocok dengan latar belakang dengan benar. Dalam mode Game, Anda akan melihat yang berikut:


Karangan:

  • Grafik untuk proyek ini diambil dari paket Wiki Wenderlich gratis! Karya grafis lainnya dapat ditemukan di situs web gameartguppy- nya.
  • Musik yang hebat diambil dari BenSound , yang memiliki soundtrack keren lainnya!
  • Saya juga berterima kasih kepada Michael Jesper untuk fungsi goyangan kamera yang sangat berguna .
.

Tempat itu ditandai dengan tanda silang: lokasi monster


Monster hanya bisa ditempatkan pada titik yang ditandai dengan x .

Untuk menambahkannya ke adegan, seret Images \ Objects \ openspot dari Project Browser ke jendela Scene . Sementara posisi itu tidak penting bagi kami.

Setelah Anda memilih Openspot dalam hierarki, klik Tambahkan Komponen di Inspektur dan pilih Box Collider 2D . Di jendela Scene, Unity akan menampilkan collider persegi panjang dengan garis hijau. Kami akan menggunakan collider ini untuk mengenali klik mouse di lokasi ini.


Tambahkan komponen Audio \ Audio Source ke Openspot dengan cara yang sama. Untuk parameter AudioClip komponen Sumber Audio, pilih file tower_place yang terletak di folder Audio dan nonaktifkan Play On Sedarlah .

Kita perlu membuat 11 poin lagi. Meskipun ada godaan untuk mengulangi semua langkah ini, Unity memiliki solusi yang lebih baik: Prefab !

Seret Openspot dari Hierarki ke folder Rak itan di dalam Browser Proyek . Namanya akan berubah menjadi biru dalam Hierarki, yang artinya melekat pada cetakan. Sesuatu seperti ini:


Sekarang kami memiliki cetakan prefab, kami dapat membuat salinan sebanyak yang kami suka. Cukup seret dan lepas Openspot dari folder Rak itan di dalam Browser Proyek ke dalam jendela Adegan . Ulangi ini 11 kali dan 12 objek openspot akan muncul di layar.

Sekarang gunakan Inspektur untuk mengatur 12 objek OpenSpot ini dengan koordinat berikut:

  • (X: -5.2, Y: 3.5, Z: 0)
  • (X: -2.2, Y: 3.5, Z: 0)
  • (X: 0,8, Y: 3,5, Z: 0)
  • (X: 3,8, Y: 3,5, Z: 0)
  • (X: -3,8, Y: 0,4, Z: 0)
  • (X: -0,8, Y: 0,4, Z: 0)
  • (X: 2.2, Y: 0.4, Z: 0)
  • (X: 5.2, Y: 0.4, Z: 0)
  • (X: -5.2, Y: -3.0, Z: 0)
  • (X: -2.2, Y: -3.0, Z: 0)
  • (X: 0,8, Y: -3,0, Z: 0)
  • (X: 3,8, Y: -3,0, Z: 0)

Ketika Anda melakukan ini, pemandangannya akan terlihat seperti ini:


Kami menempatkan monster


Untuk mempermudah penempatan, ada Rakasa Rak Rak di folder Rak Gudang proyek.


Monster Rakasa Siap Pakai

Saat ini, itu terdiri dari objek permainan kosong dengan tiga sprite yang berbeda dan animasi penembakan sebagai anak-anak.

Setiap sprite adalah monster dengan tingkat kekuatan yang berbeda. Rak pabrikan juga berisi komponen Sumber Audio , yang akan diluncurkan untuk memutar suara ketika monster menembakkan laser.

Sekarang kita akan membuat skrip yang akan meng - host Monster di Openspot .

Di Browser Proyek, pilih objek Openspot di folder Rak itan . Di Inspektur, klik Tambah Komponen , lalu pilih Script Baru dan beri nama script PlaceMonster . Pilih C Sharp sebagai bahasa dan klik Buat dan Tambah . Karena kami menambahkan skrip ke prefab Openspot , semua objek Openpot dalam adegan sekarang akan memiliki skrip ini. Hebat!

Klik dua kali pada skrip untuk membukanya di IDE. Kemudian tambahkan dua variabel:

public GameObject monsterPrefab; private GameObject monster; 

Kami akan membuat instance objek yang disimpan di monsterPrefab untuk membuat monster, dan menyimpannya di monster sehingga dapat dimanipulasi selama permainan.

Satu monster per poin


Agar hanya satu monster yang dapat diletakkan pada satu titik, tambahkan metode berikut:

 private bool CanPlaceMonster() { return monster == null; } 

Di CanPlaceMonster() kita dapat memeriksa apakah variabel monster masih null . Jika demikian, maka tidak ada monster di titik itu, dan kita bisa menempatkannya.

Sekarang tambahkan kode berikut untuk menempatkan monster ketika pemain mengklik GameObject ini:

 //1 void OnMouseUp() { //2 if (CanPlaceMonster()) { //3 monster = (GameObject) Instantiate(monsterPrefab, transform.position, Quaternion.identity); //4 AudioSource audioSource = gameObject.GetComponent<AudioSource>(); audioSource.PlayOneShot(audioSource.clip); // TODO:   } } 

Kode ini menemukan monster ketika Anda mengklik mouse atau menyentuh layar. Bagaimana cara kerjanya?

  1. Unity secara otomatis memanggil OnMouseUp ketika seorang pemain menyentuh GameObject fisik collider.
  2. Ketika dipanggil, metode ini menempatkan monster jika CanPlaceMonster() mengembalikan true .
  3. Kami membuat monster menggunakan metode Instantiate , yang membuat instance dari prefab yang diberikan dengan posisi dan rotasi yang ditentukan. Dalam hal ini, kami menyalin monsterPrefab , memberikannya posisi GameObject saat ini dan tidak ada rotasi, mentransfer hasilnya ke GameObject dan menyimpannya ke monster
  4. Pada akhirnya, kami memanggil PlayOneShot untuk memainkan efek suara yang melekat pada komponen AudioSource objek.

Sekarang skrip PlaceMonster kami dapat memiliki monster baru, tetapi kami masih perlu menentukan cetakan.

Menggunakan Prefab Kanan


Simpan file dan kembali ke Unity.

Untuk mengatur variabel monsterPrefab , pertama-tama pilih objek Openspot dari folder Rak itan di browser proyek.

Di Inspektur, klik pada lingkaran di sebelah kanan bidang Rakasa Rakasa komponen PlaceMonster (Script) dan pilih Rakasa di kotak dialog yang muncul.


Itu saja. Luncurkan adegan dan buat monster di tempat yang berbeda dengan mengklik mouse atau menyentuh layar.


Hebat! Sekarang kita bisa membuat monster. Namun, mereka terlihat seperti kekacauan aneh, karena semua sprite anak monster itu ditarik. Sekarang kita akan memperbaikinya.

Naikkan level monster


Gambar di bawah ini menunjukkan bahwa dengan peningkatan level, monster terlihat semakin menakutkan.


Manis sekali! Tetapi jika Anda mencoba mencuri cookie-nya, monster ini akan berubah menjadi seorang pembunuh.

Script digunakan sebagai dasar untuk implementasi sistem level monster. Ini melacak kekuatan monster di setiap level dan, tentu saja, level monster saat ini.

Tambahkan skrip ini.

Pilih Rak Pabrikan / Rakasa di Browser Proyek . Tambahkan skrip C # baru yang disebut MonsterData . Buka skrip di IDE dan tambahkan kode berikut di atas kelas MonsterData .

 [System.Serializable] public class MonsterLevel { public int cost; public GameObject visualization; } 

Jadi kami membuat MonsterLevel . Ini mengelompokkan harga (dalam emas, yang akan kami dukung di bawah) dan representasi visual dari tingkat monster.

Kami menambahkan di atas [System.Serializable] sehingga instance kelas dapat dimodifikasi di inspektur. Ini memungkinkan kita untuk dengan cepat mengubah semua nilai kelas Level, bahkan ketika game sedang berjalan. Ini sangat berguna untuk menyeimbangkan permainan.

Mengatur Level Monster


Dalam kasus kami, kami akan menyimpan MonsterLevel ditentukan di List<T> .

Mengapa tidak menggunakan MonsterLevel[] ? Kita perlu indeks objek MonsterLevel tertentu beberapa kali. Meskipun mudah untuk menulis kode untuk ini, kita masih harus menggunakan IndexOf() , yang mengimplementasikan fungsionalitas Lists . Tidak masuk akal untuk menemukan kembali roda.


Menciptakan kembali sepeda motor biasanya merupakan ide yang buruk.

Di bagian atas MonsterData.cs, tambahkan konstruksi berikut using :

 using System.Collections.Generic; 

Ini memberi kita akses ke struktur data umum sehingga kita bisa menggunakan kelas List<T> dalam skrip.

Catatan : generalisasi adalah konsep C # yang kuat. Mereka memungkinkan Anda untuk menentukan struktur data tipe aman tanpa harus mematuhi tipe tersebut. Ini berguna untuk kelas kontainer seperti daftar dan set. Untuk mempelajari lebih lanjut tentang struktur generik, baca buku Pengantar C # Generik .

Sekarang tambahkan variabel berikut ke MonsterData untuk menahan daftar MonsterLevel :

 public List<MonsterLevel> levels; 

Berkat generalisasi, kami dapat menjamin bahwa List dari level hanya akan berisi objek MonsterLevel .

Simpan file dan beralih ke Unity untuk mengkonfigurasi setiap level.

Pilih Rak itan / Rakasa di Browser Proyek . Inspektur sekarang menampilkan bidang Levels dari komponen MonsterData (Script) . Atur ukuran ke 3 .


Selanjutnya, tetapkan biaya untuk setiap level:

  • Elemen 0 : 200
  • Elemen 1 : 110
  • Elemen 2 : 120

Sekarang kami menetapkan nilai bidang tampilan visual.

Perluas Rakasa / Rakasa di peramban Proyek untuk melihat anak-anaknya. Tarik anak Monster0 ke dalam bidang Elemen 0 visualisasi .

Selanjutnya, atur Elemen 1 ke Monster1 , dan Elemen 2 ke Monster2 . GIF menunjukkan proses ini:


Saat Anda memilih Rak itan / Rakasa , Rak itan akan terlihat seperti ini:


Setel level saat ini


Kembali ke MonsterData.cs di IDE dan tambahkan variabel lain ke MonsterData .

 private MonsterLevel currentLevel; 

Dalam variabel private currentLevel kita akan menyimpan level monster saat ini.

Sekarang atur currentLevel dan buat itu terlihat oleh skrip lain. Tambahkan baris berikut ke MonsterData bersama dengan deklarasi variabel instan:

 //1 public MonsterLevel CurrentLevel { //2 get { return currentLevel; } //3 set { currentLevel = value; int currentLevelIndex = levels.IndexOf(currentLevel); GameObject levelVisualization = levels[currentLevelIndex].visualization; for (int i = 0; i < levels.Count; i++) { if (levelVisualization != null) { if (i == currentLevelIndex) { levels[i].visualization.SetActive(true); } else { levels[i].visualization.SetActive(false); } } } } } 

Potongan kode C # yang cukup besar, bukan? Mari kita lakukan secara berurutan:

  1. Atur properti variabel currentLevel variabel. Dengan mengatur properti, kita dapat menyebutnya seperti variabel lain: baik sebagai CurrentLevel (di dalam kelas) atau sebagai monster.CurrentLevel (di luar). Kita dapat mendefinisikan perilaku apa pun dalam metode pengambil atau penyetel properti, dan dengan hanya membuat pengambil, penyetel, atau keduanya, kita dapat mengontrol properti properti: baca-saja, hanya-tulis, dan tulis / baca.
  2. Di pengambil, kami mengembalikan nilai currentLevel .
  3. Di setter, kami menetapkan currentLevel nilai baru. Kemudian kita mendapatkan indeks level saat ini. Akhirnya, kami menggilir semua tingkatan dan mengaktifkan / menonaktifkan tampilan visual tergantung pada currentLevelIndex . Ini bagus karena ketika currentLevel berubah, sprite memperbarui secara otomatis. Properti adalah hal yang sangat nyaman!

Tambahkan implementasi OnEnable berikut:

 void OnEnable() { CurrentLevel = levels[0]; } 

Di sini kita mengatur CurrentLevel saat menempatkan. Ini memastikan bahwa hanya sprite yang diinginkan yang ditampilkan.

Catatan : penting untuk menginisialisasi properti di OnEnable , dan bukan di OnStart , karena kami memanggil metode ordinal saat membuat instance prefab.

OnEnable akan dipanggil segera ketika cetakan dibuat (jika cetakan disimpan dalam keadaan diaktifkan), tetapi OnStart tidak dipanggil sampai objek mulai berjalan sebagai bagian dari adegan.

Kami perlu memverifikasi data ini sebelum menempatkan monster, jadi kami inisialisasi ke OnEnable .

Simpan file dan kembali ke Unity. Jalankan proyek dan tempatkan monster; mereka sekarang menampilkan sprite yang benar dari level terendah.


Peningkatan Monster


Kembali ke IDE dan tambahkan metode berikut ke MonsterData :

 public MonsterLevel GetNextLevel() { int currentLevelIndex = levels.IndexOf (currentLevel); int maxLevelIndex = levels.Count - 1; if (currentLevelIndex < maxLevelIndex) { return levels[currentLevelIndex+1]; } else { return null; } } 

Di GetNextLevel kita mendapatkan indeks currentLevel dan indeks level tertinggi; jika monster belum mencapai level maksimum, maka level berikutnya kembali. Jika tidak, null dikembalikan.

Anda dapat menggunakan metode ini untuk mengetahui apakah upgrade monster dimungkinkan.

Untuk menaikkan level monster, tambahkan metode berikut:

 public void IncreaseLevel() { int currentLevelIndex = levels.IndexOf(currentLevel); if (currentLevelIndex < levels.Count - 1) { CurrentLevel = levels[currentLevelIndex + 1]; } } 

Di sini kita mendapatkan indeks level saat ini, dan kemudian memastikan bahwa ini bukan level maksimum, memeriksa bahwa itu kurang dari level. levels.Count - 1 . Jika demikian, maka CurrentLevel ke level berikutnya.

Memeriksa Fungsi Peningkatan


Simpan file dan kembali ke PlaceMonster.cs di IDE. Tambahkan metode baru:

 private bool CanUpgradeMonster() { if (monster != null) { MonsterData monsterData = monster.GetComponent<MonsterData>(); MonsterLevel nextLevel = monsterData.GetNextLevel(); if (nextLevel != null) { return true; } } return false; } 

Pertama kita periksa apakah ada monster yang bisa diperbaiki dengan membandingkan variabel monster dengan null . Jika ini benar, maka kita mendapatkan level monster saat ini dari MonsterData -nya.

Kemudian kami memeriksa apakah level berikutnya tersedia, yaitu, apakah GetNextLevel() tidak mengembalikan null . Jika peningkatan level dimungkinkan, maka kami mengembalikan true ; jika tidak, kembalikan false .

Kami menerapkan peningkatan untuk emas


Untuk mengaktifkan opsi pemutakhiran, tambahkan cabang else if ke OnMouseUp :

 if (CanPlaceMonster()) { //      } else if (CanUpgradeMonster()) { monster.GetComponent<MonsterData>().IncreaseLevel(); AudioSource audioSource = gameObject.GetComponent<AudioSource>(); audioSource.PlayOneShot(audioSource.clip); // TODO:   } 

Kami memeriksa kemungkinan peningkatan menggunakan CanUpgradeMonster() . Jika memungkinkan, kita mengakses komponen GetComponent() menggunakan GetComponent() dan memanggil IncreaseLevel() , yang meningkatkan level monster. Akhirnya, kami meluncurkan Monster AudioSource .

Simpan file dan kembali ke Unity. Jalankan game, tempatkan dan tingkatkan sejumlah monster (tetapi untuk sekarang).


Membayar Emas - Manajer Game


Meskipun kita bisa segera membangun dan meningkatkan monster apa pun, tetapi apakah itu akan menarik dalam game?

Mari kita lihat masalah emas. Masalah dengan pelacakan itu adalah kita harus mentransfer informasi antara objek permainan yang berbeda.

Gambar di bawah ini menunjukkan semua objek yang harus mengambil bagian dalam hal ini.


Semua objek permainan yang dipilih harus tahu berapa banyak emas yang dimiliki pemain.

Untuk menyimpan data ini, kami akan menggunakan objek umum yang dapat diakses oleh objek lain.

Klik kanan pada Hierarki dan pilih Buat Kosong . Beri nama objek GameManager baru.

Tambahkan skrip C # baru yang disebut GameManagerBehavior ke GameManager , lalu buka di IDE. Kami akan menampilkan jumlah total emas pemain di label, jadi di bagian atas file tambahkan baris berikut:

 using UnityEngine.UI; 

Ini akan memungkinkan kita untuk mengakses kelas UI seperti Text , yang digunakan untuk label. Sekarang tambahkan variabel berikut ke kelas:

 public Text goldLabel; 

Ini akan menyimpan tautan ke komponen Text digunakan untuk menampilkan jumlah emas yang dimiliki pemain.

Sekarang GameManager tahu tentang label, bagaimana kita menyinkronkan jumlah emas yang disimpan dalam variabel dan nilai yang ditampilkan dalam label? Kami akan membuat properti.

Tambahkan kode berikut ke GameManagerBehavior :

 private int gold; public int Gold { get { return gold; } set { gold = value; goldLabel.GetComponent<Text>().text = "GOLD: " + gold; } } 

Apakah dia tampak akrab? Kode ini mirip dengan CurrentLevel , yang kami atur di Monster . Pertama kita membuat variabel gold pribadi untuk menampung jumlah emas saat ini. Lalu kita atur properti Gold (tiba-tiba, kan?) Dan terapkan pengambil dan penyetel.

Sang pengambil hanya mengembalikan nilai gold . Setter lebih menarik. Selain mengatur nilai variabel, itu juga menetapkan bidang text untuk goldLabel untuk menampilkan nilai emas baru.

Bagaimana kita akan bermurah hati? Tambahkan baris berikut ke Start() untuk memberi pemain 1000 emas, atau kurang jika Anda menyesal atas uang itu:

 Gold = 1000; 

Menetapkan objek label ke skrip


Simpan file dan kembali ke Unity. Di Hirarki, pilih GameManager . Di Inspektur, klik pada lingkaran di sebelah kanan Label Emas . Di kotak dialog Select Text , pilih tab Scene dan pilih GoldLabel .


Jalankan adegan dan label akan menampilkan Emas: 1000 .


Memeriksa "dompet" pemain


Buka skrip PlaceMonster.cs di IDE dan tambahkan variabel instance berikut:

 private GameManagerBehavior gameManager; 

Kami akan menggunakan gameManager untuk mengakses komponen GameManagerBehavior dari objek GameManagerBehavior di layar. Untuk menentukannya, tambahkan yang berikut ke Start() :

 gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>(); 

Kami mendapatkan GameObject bernama GameManager menggunakan fungsi GameObject.Find() , yang mengembalikan objek game pertama yang ditemukan dengan nama itu. Kemudian kita mendapatkan komponen GameManagerBehavior dan menyimpannya untuk masa depan.

Catatan : Anda bisa melakukan ini dengan menyetel bidang di editor Unity atau dengan menambahkan metode statis ke GameManager yang mengembalikan instance singleton dari mana kita bisa mendapatkan GameManagerBehavior .

Namun, dalam blok kode yang ditunjukkan di atas ada kuda hitam: metode Find , yang bekerja lebih lambat selama eksekusi aplikasi; tetapi nyaman dan dapat digunakan dalam jumlah sedang.

Ambil uangku!


Kami belum mengurangi emas, jadi kami akan menambahkan baris ini dua kali ke OnMouseUp() , menggantikan masing-masing komentar // TODO: :

 gameManager.Gold -= monster.GetComponent<MonsterData>().CurrentLevel.cost; 

Simpan file dan kembali ke Unity, perbarui beberapa monster dan lihat pembaruan nilai Gold. Sekarang kami mengurangi emas, tetapi pemain dapat membangun monster selama mereka memiliki cukup ruang; mereka hanya meminjam uang.


Kredit tak terbatas? Hebat! Tapi kita tidak bisa membiarkannya. Pemain harus bisa bertaruh monster sementara dia memiliki cukup emas.

Cek Emas untuk Monster


Beralih di IDE ke PlaceMonster.cs dan ganti konten CanPlaceMonster() berikut:

 int cost = monsterPrefab.GetComponent<MonsterData>().levels[0].cost; return monster == null && gameManager.Gold >= cost; 

Kami MonsterData penempatan monster dari levels di MonsterData -nya. Lalu kita periksa monster bukan null , dan itu gameManager.Gold lebih dari harga ini.

Tugas untuk Anda: tambahkan secara independen ke CanUpgradeMonster() centang apakah pemain memiliki cukup emas.

Solusi di dalam
Ganti baris:

 return true; 

tentang ini:

 return gameManager.Gold >= nextLevel.cost; 

Ini akan memeriksa apakah pemain memiliki lebih banyak Emas daripada harga upgrade.

Simpan dan jalankan adegan di Unity. Sekarang cobalah cara menambahkan monster tanpa batas!


Sekarang kita hanya dapat membangun sejumlah monster.

Kebijakan menara: musuh, gelombang, dan titik arah


Inilah saatnya untuk "membuka jalan" bagi musuh-musuh kita. Musuh muncul di titik pertama rute, pindah ke yang berikutnya dan ulangi prosesnya hingga mencapai cookie.

Anda dapat membuat musuh bergerak seperti ini:

  1. Atur jalan yang akan diikuti musuh
  2. Pindahkan musuh di sepanjang jalan
  3. Putar musuh sehingga dia melihat ke depan

Membuat jalan dari titik arah


Klik kanan pada Hierarchy dan pilih Create Empty untuk membuat objek game baru yang kosong. Beri nama Jalan dan posisikan di (0, 0, 0) .

Sekarang klik kanan pada Road in the Hierarchy dan buat objek game kosong lainnya sebagai anak Road. Beri nama Waypoint0 dan letakkan di titik (-12, 2, 0) - dari sini musuh akan memulai gerakan mereka.


Demikian pula, buat lima titik rute lagi dengan nama dan posisi berikut:

  • Waypoint1: (X: 7, Y: 2, Z: 0)
  • Waypoint2: (X: 7, Y: -1, Z: 0)
  • Waypoint3: (X: -7.3, Y: -1, Z: 0)
  • Waypoint4: (X: -7.3, Y: -4.5, Z: 0)
  • Waypoint5: (X: 7, Y: -4.5, Z: 0)

Tangkapan layar di bawah ini menunjukkan titik rute dan jalur yang dihasilkan.


Membuat musuh


Sekarang buat beberapa musuh sehingga mereka bisa bergerak di sepanjang jalan. Ada prefab Musuh di folder Rak itan . Posisinya adalah (-20, 0, 0) , jadi instance baru akan dibuat di luar layar.

Dalam semua hal lain, ini dikonfigurasi hampir dengan cara yang sama seperti Rakasa Rakasa, memiliki AudioSource dan anak Sprite , dan kita dapat memutar sprite ini di masa depan tanpa memutar bar kesehatan.


Kami menggerakkan musuh di sepanjang jalan


Tambahkan skrip C # baru yang disebut MoveEnemy ke prefab \ prefab Musuh . Buka skrip di IDE dan tambahkan variabel berikut:

 [HideInInspector] public GameObject[] waypoints; private int currentWaypoint = 0; private float lastWaypointSwitchTime; public float speed = 1.0f; 

Di waypoints , salinan titik rute disimpan dalam array, dan baris [HideIn inspector ] atas waypoints memastikan bahwa kami tidak dapat secara tidak sengaja mengubah bidang ini di Inspektur , tetapi masih akan memiliki akses ke sana dari skrip lain.

currentWaypoint melacak dari mana rute musuh dari pada saat ini, dan lastWaypointSwitchTime menyimpan waktu ketika musuh melewatinya. Selain itu, kami menyimpan speed musuh.

Tambahkan baris ini ke Start() :

 lastWaypointSwitchTime = Time.time; 

Jadi kami menginisialisasi lastWaypointSwitchTime dengan nilai waktu saat ini.

Agar musuh bergerak di sepanjang rute, tambahkan kode berikut ke Update() :

 // 1 Vector3 startPosition = waypoints [currentWaypoint].transform.position; Vector3 endPosition = waypoints [currentWaypoint + 1].transform.position; // 2 float pathLength = Vector3.Distance (startPosition, endPosition); float totalTimeForPath = pathLength / speed; float currentTimeOnPath = Time.time - lastWaypointSwitchTime; gameObject.transform.position = Vector2.Lerp (startPosition, endPosition, currentTimeOnPath / totalTimeForPath); // 3 if (gameObject.transform.position.Equals(endPosition)) { if (currentWaypoint < waypoints.Length - 2) { // 3.a currentWaypoint++; lastWaypointSwitchTime = Time.time; // TODO:     } else { // 3.b Destroy(gameObject); AudioSource audioSource = gameObject.GetComponent<AudioSource>(); AudioSource.PlayClipAtPoint(audioSource.clip, transform.position); // TODO:   } } 

Mari kita menganalisis kode langkah demi langkah:

  1. Dari berbagai titik rute, kami mendapatkan posisi awal dan akhir dari segmen rute saat ini.
  2. Kami menghitung waktu yang diperlukan untuk mencakup seluruh jarak menggunakan rumus waktu = jarak / kecepatan , dan kemudian menentukan waktu saat ini pada rute. Menggunakan Vector2.Lerp , kami menginterpolasi posisi musuh saat ini antara segmen awal dan akhir yang tepat.
  3. Periksa apakah musuh telah mencapai endPosition . Jika ya, maka kami memproses dua skenario yang mungkin:
    1. Musuh belum mencapai titik terakhir rute, jadi tambah nilai currentWaypoint dan perbarui lastWaypointSwitchTime . Nanti kita akan menambahkan kode untuk mengubah musuh sehingga dia melihat ke arah gerakannya.
    2. Musuh telah mencapai titik terakhir dari rute, lalu kita hancurkan dan mulai efek suara. Nanti kita akan menambahkan kode yang mengurangi health pemain.

Simpan file dan kembali ke Unity.

Kami memberi tahu musuh tentang arah gerakan


Dalam kondisi saat ini, musuh tidak mengetahui urutan titik rute.

Pilih Jalan di Hirarki dan tambahkan skrip C # baru yang disebut SpawnEnemy . Buka di IDE dan tambahkan variabel berikut:

 public GameObject[] waypoints; 

Kami akan menggunakan waypoints untuk menyimpan referensi ke titik lewat dalam adegan dalam urutan yang diinginkan.

Simpan file dan kembali ke Unity. Pilih Road di Hierarchy dan atur Size of the Waypoints array menjadi 6 .

Seret setiap anak Jalan ke dalam bidang dengan menempelkan Waypoint0 di Elemen 0 , Waypoint1 di Elemen 1, dan seterusnya.


Sekarang kami memiliki array yang berisi titik rute dalam urutan yang benar - ingat, musuh tidak pernah mundur, mereka terus-menerus berjuang untuk mendapatkan hadiah yang manis.

Periksa cara kerjanya


Buka SpawnEnemy di IDE dan tambahkan variabel berikut:

 public GameObject testEnemyPrefab; 

Ini akan menyimpan referensi ke testEnemyPrefab Musuh di testEnemyPrefab .

Untuk membuat musuh saat menjalankan skrip, tambahkan kode berikut ke Start() :

 Instantiate(testEnemyPrefab).GetComponent<MoveEnemy>().waypoints = waypoints; 

Jadi kami akan membuat salinan cetakan baru yang disimpan di testEnemy dan memberinya rute.

Simpan file dan kembali ke Unity. Pilih objek Jalan di Hirarki dan pilih prefab Musuh untuk parameter Uji Musuh .

Luncurkan proyek dan lihat bagaimana musuh bergerak di sepanjang jalan (di GIF, untuk kejelasan, kecepatannya meningkat 20 kali).


Melihat bahwa dia tidak selalu melihat ke mana dia pergi? Ini lucu, tapi kami mencoba membuat game profesional. Oleh karena itu, di bagian kedua tutorial kami akan mengajarkan musuh untuk berharap.

Ke mana harus pergi selanjutnya?


Kami telah melakukan banyak hal dan dengan cepat bergerak menuju pembuatan game pertahanan menara kami sendiri.

Pemain dapat membuat sejumlah monster, dan musuh berlari di sepanjang jalan, menuju cookie kami. Pemain memiliki emas dan mereka dapat meningkatkan monster.

Unduh hasil akhirnya dari sini .

Pada bagian kedua, kita akan mempertimbangkan penciptaan gelombang besar musuh dan kehancuran mereka. Sampai ketemu lagi!

Source: https://habr.com/ru/post/id413837/


All Articles