Pembuatan game "Like Coins" di Godot Engine. Bagian 2

Saya harap Anda bosan menunggu bagian kedua artikel, yang menyentuh pengembangan game menggunakan Godot Engine, menggunakan contoh game Like Coins? Banyak dari semuanya yang “enak” dan “sehat” telah disiapkan dalam agenda. Saya akan segera melakukan reservasi bahwa dalam artikel ini kami akan menyelesaikan game yang sudah dimulai sebelumnya, yang permulaannya bisa Anda baca di sini - Membuat game “Like Coins” di Godot Engine. Bagian 1 , tetapi seri artikel akan berlanjut, karena ada begitu banyak bahan yang membuat saya menyisihkan sebagian darinya, tetapi kami pasti akan kembali lagi nanti. Biarkan "gamedev" dimulai!


Adegan "Utama"


Di bagian sebelumnya artikel, kami berhenti di panggung utama ( Main ), dan dari situ, mungkin, kami akan melanjutkan. Kami menghapus semua yang ditambahkan sebelumnya (jika Anda menambahkan sesuatu tentu saja untuk memeriksa bagaimana semuanya bekerja), jika tidak ada yang dimuat ke dalam adegan, maka kita harus menambahkan Node , yang akan menjadi induk untuk node yang tercantum di bawah, yang pada gilirannya juga harus ditambahkan ke tempat kejadian:


ColorRect ("Background") - mengisi warna latar belakang;
Player - objek "Player" (Saya harap Anda tidak bingung, karena saya menyebut adegan Player sebagai objek?);
Node ("Wadah") - "wadah" untuk penyimpanan sementara koin;
Position2D ("PlayerStart") - di awal permainan menentukan posisi awal objek "Player";
Timer ("GameTimer") - penghitung batas waktu;


Pilih ColorRect dan pada bilah alat pilih: Layout -> Full Rect untuk merentangkannya ke seluruh area layar (di masa depan kita akan sering menggunakan fungsi ini, jadi saya menyarankan Anda untuk mempelajari operasi lain yang ditentukan dalam daftar Layout ) untuk node ini, di properti "Warna" tentukan warna isian yang diinginkan. Anda dapat melakukan hal yang sama dengan TextureRect , hanya alih-alih mengisi Anda harus memuat gambar melalui properti "Tekstur". Untuk Position2D , di properti "position", tentukan nilai "x" dan "y" - ini akan berfungsi sebagai posisi awal untuk Player . Tentu saja, dengan bantuan skrip, Anda dapat mengatur nilai penentuan posisi langsung di Player itu sendiri, tetapi kami tidak hanya belajar mengembangkan game, tetapi juga mempelajari "Godot", oleh karena itu, pertimbangan berbagai opsi untuk menyelesaikan satu masalah tidak akan berlebihan.


Script untuk "Main"


Tambahkan skrip untuk Node dan cetak berikut ini:


 extends Node #PackedScene        export (PackedScene) var Coin export (int) var playtime var level #  var score # var left_time #     var window_size #   var playing = false #    

Properti "Coin" dan "playtime" akan ditampilkan di Inspector . Seret adegan "Coin.tscn" ke properti "Coin", dan atur nilai "waktu bermain" menjadi "40" (durasi permainan dalam hitungan detik).


Ketika permainan dimulai, setiap kali harus ada inisialisasi - persiapan untuk bekerja, penentuan parameter yang diperlukan untuk operasi aplikasi yang berkualitas tinggi dan bebas kesalahan. Ini adalah langkah wajib, jadi Anda harus mengurus ini terlebih dahulu.


 func _ready(): randomize() #        window_size = get_viewport().get_visible_rect().size #    $Player.window_size = window_size #    "" $Player.hide() #   

Perhatikan bahwa ketika menentukan nama objek Player , simbol "$" digunakan - ini adalah "gula sintaksis" yang memungkinkan Anda untuk langsung mengakses node dalam adegan saat ini, alternatif yang baik untuk metode get_node("Node1") (walaupun penggunaan yang terakhir tidak dilarang). Jika "Node1" memiliki turunan "Node2", Anda juga dapat menggunakan metode ini - $Node1/Node2 . Perhatikan bahwa pengisian otomatis bekerja sangat baik di Godot, jadi jangan abaikan. Menggunakan spasi pada nama node tidak diinginkan, tetapi masih valid, dalam hal ini gunakan tanda kutip - $"My best Node1" .


Game baru


Untuk memulai permainan baru, kami akan menentukan fungsi yang sesuai untuk ini, yang kemudian dapat kita panggil, misalnya, dengan menekan tombol.


 func new_game(): playing = true #   level = 1 score = 0 time_left = playtime $Player.start($PlayerStart.position) $Player.show() $GameTimer.start() #    spawn_coins() #  

Fungsi "start ()" yang argumennya adalah $PlayerStart.position akan memindahkan pemain ke lokasi awal, dan fungsi "spawn_coins ()" bertanggung jawab, seperti yang Anda duga, untuk spawn coin di lapangan permainan.


 func spawn_coins(): for i in range(4 + level): var c = Coin.instance() $CoinContainer.add_child(c) c.window_size = window_size c.position = Vector2(rand_range(0, window_size.x), rand_range(0, window_size.y)) 

Fungsi range(4 + level) akan mengembalikan array dengan range yang diberikan yang nilainya sama dengan jumlah jumlah koin dan nilai level saat ini. Rentang dapat berisi satu argumen, seperti dalam kasus kami, baik dua argumen atau tiga argumen (argumen ketiga akan menjadi langkah array). Dalam fungsi ini, kami membuat beberapa contoh objek "Coin" dan menambahkan CoinContainer sebagai elemen anak (Saya harap Anda tidak lupa bahwa kami sudah memiliki akses ke objek, terima kasih kepada PackedScene ). Ingat bahwa setiap kali Anda membuat instance dari simpul baru (metode instance() ), itu harus ditambahkan ke pohon menggunakan add_child() . Selanjutnya, kami mengatur area untuk kemungkinan spawn koin sehingga mereka tidak muncul secara tidak sengaja di belakang layar, dan kemudian secara acak menetapkan suatu posisi. Baris terakhir tidak terlihat sedikit estetis, jadi saya sarankan menyederhanakannya dengan beralih ke Singleton.


Singleton


Nama tengah Singleton adalah "Startup." Sudah sugestif, bukan? Saya beri tahu Anda, singleton berfungsi sebagai berikut: sebuah skrip di mana kita dapat menulis apa pun yang kita inginkan (mulai dari mendeklarasikan variabel dan diakhiri dengan "sakelar" adegan, termasuk memuat dan menurunkannya) dimuat terlebih dahulu, dengan aplikasi berjalan, dan semua kontennya dapat diakses dari sembarang poin proyek. Di satu sisi, ini adalah semacam repositori global kustom "apa pun" yang tersedia pada waktu tertentu.


Catatan, proyek memiliki repositori globalnya sendiri, konten yang juga dapat kita gunakan, dan Anda dapat mengaksesnya menggunakan ProjectSettings.get_setting(name) , di mana name adalah nama parameter yang diperlukan.

Sekarang, untuk menggunakan sesuatu dari repositori "_G", cukup menyebutnya dengan nama, dan kemudian tentukan metode yang akan dipanggil, atau apa pun yang kita miliki di sana. Jadi, buat skrip kosong dan tulis di dalamnya fungsi yang ditunjukkan di bawah ini:


 extends Node func rand(): var rrand = Vector2(rand_range(40, 760), rand_range(40, 540)) return rrand #   


Selanjutnya, simpan dan pergi ke pengaturan proyek: Project -> Project Settings -> AutoLoad . Kami memilih skrip kami yang baru dibuat, menetapkan nama untuknya, misalnya, "_G", dan kembali ke fungsi "spawn_coins ()" untuk sedikit menyesuaikan tenggat waktu, menggantinya dengan kode berikut:


  ... c.position = _G.rand() 

Sekarang perlu memeriksa apa yang terjadi dengan menempatkan "spawn_coins ()" di blok "_ready ()" dan menjalankan aplikasi pada F5. Dan jangan lupa untuk memilih Main.tscn sebagai adegan utama, jika karena alasan tertentu Anda melakukan kesalahan dalam memilih, Anda dapat mengubah adegan utama secara manual, untuk ini Anda perlu pergi ke pengaturan proyek: General -> Run -> MainScene . Apakah ini berhasil? Kemudian lanjutkan.


Berapa banyak koin yang tersisa?


Mari kita lanjutkan. Selanjutnya, Anda perlu memeriksa berapa banyak koin yang tersisa untuk mentransfer pemain ke tingkat berikutnya, memberinya "bonus" kecil dalam bentuk peningkatan waktu 5 detik, dan kemudian memunculkan kembali koin.


 func _process(delta): #   ?     ? if playing and $CoinContainer.get_child_count() == 0: #    level += 1 # ""   time_left += 5 #  spawn_coins() 

Antarmuka pengguna


Seluruh antarmuka kami akan terdiri dari elemen-elemen berikut: indikator skor, level saat ini, waktu, nama permainan, dan tombol yang akan memicu peluncuran game. Buat adegan ( HUD.tscn ) dengan orangtua CanvasLayer (memungkinkan Anda untuk menggambar antarmuka pengguna di atas lapangan bermain). Ke depan, saya akan mengatakan bahwa tidak terlalu nyaman untuk mengelola elemen antarmuka pengguna, setidaknya bagi saya, tetapi daftar elemen yang cukup luas dan pengembangan aktif menanamkan suasana hati yang positif di masa depan yang cerah untuk pengembangan aspek mesin ini.



Di Godot ada yang disebut "node kontrol" yang memungkinkan Anda untuk memformat elemen anak secara otomatis sehubungan dengan parameter yang ditentukan dari induk. Setiap jenis "node kontrol" memiliki properti khusus yang mengontrol bagaimana mereka mengontrol lokasi turunannya. Perwakilan yang jelas dari tipe ini adalah MarginContainer , yang harus ditambahkan ke lokasi. Dengan bantuan Layout -> Top Wide merentangkannya di bagian atas jendela, dan pada properti objek ini, di bagian Margin , tentukan indentasi dari tepi: kiri, atas dan kanan. MarginContainer harus memiliki tiga Label anak dengan nama-nama berikut: ScoreLabel , LevelLabel dan TimeLabel . Tambahkan mereka ke tempat kejadian. Menggunakan properti Align , buat mereka sejajar ke kiri, tengah, dan kanan. Tetap menambahkan Label lain ( Messagelabel ), menempatkannya di tengah, semua juga dengan bantuan Layout , dan sedikit lebih rendah menempatkan tombol ( StartButton ).



Sekarang mari kita buat antarmuka responsif, kita perlu memperbarui waktu, jumlah koin yang dikumpulkan dan menyoroti level saat ini. Tambahkan skrip untuk simpul HUD .


 extends CanvasLayer signal start_game func update_score(value): $MarginContainer/ScoreLabel.text = str(value) func update_level(value): if len(str(value)) == 1: $MarginContainer/TimeLabel.text = "0: 0" + str(value) else: $MarginContainer/TimeLabel.text = "0: " + str(value) func update_timer(value): $MarginContainer/TimeLabel.txt = str(value) 

Untuk MessageLabel kita memerlukan timer untuk mengubah teks pesan untuk waktu yang singkat. Tambahkan simpul Timer dan ganti namanya dengan MessageTimer . Di inspektur, atur waktu tunggu menjadi 2 detik dan pilih kotak centang di bidang One Shot . Ini memastikan timer hanya berjalan satu kali saat startup.


 func show_message(text): $MessageLabel.text = text $MessageLabel.show() $MessageTimer.start() 

Hubungkan sinyal timeout() ke "MessageTimer" dan tambahkan yang berikut:


 func _on_MessageTimer_timeout(): $MessageLabel.hide() 

Pada tab "Node" untuk StartButton , hubungkan sinyal yang pressed() . Ketika Anda mengklik tombol StartButton itu harus menghilang bersama dengan MessageLabel , kemudian mengirim sinyal ke adegan utama, di mana kami kemudian akan berhasil mencegatnya pada saat yang sama dengan menyelipkan fungsi eksekusi - "new_game ()". Kami menerapkan ini menggunakan kode di bawah ini. Jangan lupa tombol di properti Text untuk mengatur panggilan teks apa pun untuk memulai permainan.


 func _on_StartButton_pressed(): $StartButton.hide() $MessageLabel.hide() emit_signal("start_game") 

Untuk akhirnya selesai dengan antarmuka, kami akan menulis fungsi terakhir, terakhir - fungsi menampilkan pesan tentang akhir permainan. Dalam fungsi ini, kita perlu tulisan "Game Over" untuk ditampilkan tidak lebih dari dua detik, dan kemudian menghilang, yang dimungkinkan berkat fungsi "show_message ()". Namun, Anda harus kembali menunjukkan tombol mulai dari game baru segera setelah sebuah pesan yang memberitahukan bahwa game telah berakhir menghilang. yield() menghentikan sementara pelaksanaan fungsi sampai sinyal dari MessageTimer diterima, dan menerima sinyal dari MessageTimer tentang pelaksanaannya, fungsi tersebut akan terus dieksekusi, mengembalikan kami ke keadaan semula sehingga kami dapat memulai permainan baru lagi.


 func show_game_over(): show_message("Game Over") yield($MessageTimer, "timeout") $StartButton.show() $MessageLabel.text = "LIKE COINS!" $MessageLabel.show() 

Berakhir?


Mari kita mengatur umpan balik antara HUD dan Main . Tambahkan adegan HUD ke adegan utama dan sambungkan sinyal GameTimer melalui timeout() pada adegan utama dengan menambahkan yang berikut ini:


 func _on_GameTimer_timeout(): time_left -= 1 #  $HUD.update_timer(time_left) #   if time_left <= 0: game_over() #     

Kemudian hubungkan sinyal pickup() dan die() dari pemain.


 func _on_Player_pickup(): score += 1 $HUD.update_score(score) func _on_Player_die(): game_over() 

Di akhir permainan, beberapa hal lagi harus terjadi yang tidak boleh diabaikan. Tulis kode berikut, dan saya akan jelaskan.


 func game_over(): playing = false $GameTimer.stop() for coin in $CoinContainer.get_children(): coin.queue_free() $HUD.show_game_over() $Player.die() 

Fungsi ini akan menghentikan permainan, dan kemudian koin yang tersisa akan dihitung ulang dan koin yang tersisa akan show_game_over() , kemudian show_game_over() akan dipanggil untuk HUD . Langkah selanjutnya adalah memulai animasi dan menghentikan proses mengeksekusi node Player .


Terakhir, Anda harus mengaktifkan StartButton , yang harus terhubung ke fungsi new_game() . Klik pada simpul HUD dan dalam dialog koneksi Anda harus mengklik Make Function to Off (ini akan mencegah pembuatan fungsi baru) dan di bidang Method In Node , tentukan nama fungsi yang terhubung - new_game . Ini akan menghubungkan sinyal ke fungsi yang sudah ada, daripada membuat yang baru.


Sentuhan terakhir adalah untuk menghapus new_game() dari fungsi _ready() dan menambahkan dua baris berikut ke fungsi new_game() :


 ... $HUD.update_score(score) $HUD.update_timer(time_left) 

Sekarang kita dapat mengatakan dengan yakin bahwa permainan sudah siap, sekarang sudah cukup "dimainkan", tetapi tanpa efek. Kami akan mempertimbangkan yang terakhir di artikel berikutnya, mencurahkan perhatian besar pada berbagai "dekorasi" untuk mendiversifikasi gameplay dan lebih jauh mengeksplorasi kemungkinan "Godot". Karena itu, jangan lupa ikuti rilis artikel. Semoga beruntung

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


All Articles