ADS

Melompat dan Menghancurkan Monster

 

Melompat dan menghancurkan monster

Di bagian ini, kita akan menambahkan kemampuan untuk melompat dan menghancurkan monster. Di pelajaran berikutnya, kita akan membuat pemain mati saat monster menghantam mereka di tanah.

Pertama, kita harus mengubah beberapa pengaturan yang terkait dengan interaksi fisika. Masuk ke dunia lapisan fisika .

Mengendalikan interaksi fisika

Benda-benda fisika memiliki akses ke dua properti yang saling melengkapi: lapisan dan topeng. Lapisan menentukan lapisan fisika mana yang ditempati suatu objek.

Masker mengendalikan lapisan yang akan didengar dan dideteksi oleh suatu benda. Ini memengaruhi deteksi tabrakan. Bila Anda ingin dua benda berinteraksi, Anda memerlukan setidaknya satu untuk memiliki masker yang sesuai dengan yang lain.

Jika itu membingungkan, jangan khawatir, kita akan melihat tiga contoh sebentar lagi.

Poin pentingnya adalah Anda dapat menggunakan lapisan dan masker untuk memfilter interaksi fisika, mengontrol kinerja, dan menghilangkan kebutuhan akan kondisi tambahan dalam kode Anda.

Secara default, semua benda dan area fisika diatur ke layer dan mask 1. Ini berarti semuanya saling bertabrakan.

Lapisan fisika direpresentasikan dengan angka, tetapi kita dapat memberinya nama untuk melacak apa itu.

Mengatur nama lapisan

Mari beri nama pada lapisan fisika kita. Buka Project -> Project Settings .

gambar0

Di menu sebelah kiri, navigasikan ke bawah ke Nama Lapisan -> Fisika 3D . Anda dapat melihat daftar lapisan dengan bidang di samping masing-masing lapisan di sebelah kanan. Anda dapat menetapkan nama lapisan di sana. Beri nama tiga lapisan pertama sebagai pemain , musuh , dan dunia .

gambar1

Sekarang, kita dapat menetapkannya ke simpul fisika kita.

Menetapkan lapisan dan masker

Di adegan Utama , pilih Groundnode. Di Inspektur , perluas bagian Tabrakan . Di sana, Anda dapat melihat lapisan dan topeng node sebagai kisi tombol.

gambar2

Tanah adalah bagian dari dunia, jadi kita ingin tanah menjadi bagian dari lapisan ketiga. Klik tombol yang menyala untuk mematikan Lapisan pertama dan mengaktifkan lapisan ketiga . Kemudian, matikan Masker dengan mengkliknya.

gambar3

Seperti disebutkan sebelumnya, properti Mask memungkinkan sebuah node mendengarkan interaksi dengan objek fisika lainnya, tetapi kita tidak memerlukannya untuk mengalami tabrakan. Groundtidak perlu mendengarkan apa pun; ia hanya ada untuk mencegah makhluk jatuh.

Perhatikan bahwa Anda dapat mengeklik tombol "..." di sisi kanan properti untuk melihat daftar kotak centang bernama.

gambar4

Berikutnya adalah Playerdan Mob. Buka player.tscndengan mengklik dua kali berkas di dok FileSystem .

Pilih simpul Pemain dan atur Tabrakan -> Topeng ke "musuh" dan "dunia". Anda dapat membiarkan properti Lapisan default apa adanya, karena lapisan pertama adalah lapisan "pemain".

gambar5

Lalu, buka adegan Mob dengan mengklik dua kali mob.tscndan pilih Mobnode tersebut.

Atur Collision -> Layer ke "enemies" dan hapus Collision -> Mask , biarkan mask kosong.

gambar6

Pengaturan ini berarti monster akan bergerak melalui satu sama lain. Jika Anda ingin monster saling bertabrakan dan meluncur, aktifkan topeng "musuh".

Catatan

Massa tidak perlu menutupi lapisan "dunia" karena mereka hanya bergerak di bidang XZ. Kami tidak menerapkan gravitasi apa pun kepada mereka berdasarkan rancangan.

Melompat

Mekanik lompatan itu sendiri hanya memerlukan dua baris kode. Buka skrip Player . Kita memerlukan nilai untuk mengontrol kekuatan lompatan dan memperbarui _physics_process()kode lompatan.

Setelah baris yang mendefinisikan fall_acceleration, di bagian atas skrip, tambahkan jump_impulse.

#...
# Vertical impulse applied to the character upon jumping in meters per second.
@export var jump_impulse = 20

// Don't forget to rebuild the project so the editor knows about the new export variable.

// ...
// Vertical impulse applied to the character upon jumping in meters per second.
[Export]
public int JumpImpulse { get; set; } = 20;

Di dalam _physics_process(), tambahkan kode berikut sebelum move_and_slide()codeblock.

func _physics_process(delta):
#...

# Jumping.
if is_on_floor() and Input.is_action_just_pressed("jump"):
target_velocity.y = jump_impulse

#...

public override void _PhysicsProcess(double delta)
{
// ...

// Jumping.
if (IsOnFloor() && Input.IsActionJustPressed("jump"))
{
_targetVelocity.Y = JumpImpulse;
}

// ...
}

Hanya itu yang Anda butuhkan untuk melompat!

Metode ini is_on_floor()adalah alat dari CharacterBody3Dkelas. Metode ini mengembalikan truejika tubuh bertabrakan dengan lantai dalam bingkai ini. Itulah sebabnya kami menerapkan gravitasi ke Pemain : jadi kami bertabrakan dengan lantai alih-alih melayang di atasnya seperti monster.

Jika karakter berada di lantai dan pemain menekan "lompat", kami langsung memberi mereka banyak kecepatan vertikal. Dalam permainan, Anda benar-benar ingin kontrolnya responsif dan memberikan peningkatan kecepatan instan seperti ini, meskipun tidak realistis, terasa hebat.

Perhatikan bahwa sumbu Y bernilai positif ke atas. Tidak seperti 2D, di mana sumbu Y bernilai positif ke bawah.

Menghancurkan monster

Selanjutnya, mari tambahkan mekanisme squash. Kita akan membuat karakter memantul di atas monster dan membunuh mereka pada saat yang bersamaan.

Kita perlu mendeteksi tabrakan dengan monster dan membedakannya dari tabrakan dengan lantai. Untuk melakukannya, kita dapat menggunakan fitur penandaan grup Godot .

Buka kembali scene mob.tscndan pilih node Mob . Buka dock Node di sebelah kanan untuk melihat daftar sinyal. Dock Node memiliki dua tab: Signals , yang sudah Anda gunakan, dan Groups , yang memungkinkan Anda menetapkan tag ke node.

Klik di atasnya untuk menampilkan kolom tempat Anda dapat menulis nama tag. Masukkan "mob" di kolom tersebut dan klik tombol Tambah .

gambar7

Ikon muncul di dok Adegan untuk menunjukkan simpul tersebut merupakan bagian dari setidaknya satu grup.

gambar8

Kita sekarang dapat menggunakan grup dari kode untuk membedakan tabrakan dengan monster dari tabrakan dengan lantai.

Mengkodekan mekanik squash

Kembali ke skrip Pemain untuk mengodekan squash dan bounce.

Di bagian atas skrip, kita memerlukan properti lain, bounce_impulse. Saat menghancurkan musuh, kita tidak selalu ingin karakter tersebut terbang setinggi saat melompat.

# Vertical impulse applied to the character upon bouncing over a mob in
# meters per second.
@export var bounce_impulse = 16

// Don't forget to rebuild the project so the editor knows about the new export variable.

// Vertical impulse applied to the character upon bouncing over a mob in meters per second.
[Export]
public int BounceImpulse { get; set; } = 16;

Kemudian, setelah blok kode Jumping yang kami tambahkan di atas dalam _physics_process(), tambahkan loop berikut. Dengan move_and_slide(), Godot membuat tubuh bergerak terkadang beberapa kali berturut-turut untuk memperhalus gerakan karakter. Jadi, kita harus mengulang semua tabrakan yang mungkin terjadi.

Dalam setiap iterasi loop, kami memeriksa apakah kami mendarat di gerombolan. Jika ya, kami membunuhnya dan bangkit kembali.

Dengan kode ini, jika tidak ada tabrakan yang terjadi pada frame tertentu, loop tidak akan berjalan.

func _physics_process(delta):
#...

# Iterate through all collisions that occurred this frame
for index in range(get_slide_collision_count()):
# We get one of the collisions with the player
var collision = get_slide_collision(index)

# If the collision is with ground
if collision.get_collider() == null:
continue

# If the collider is with a mob
if collision.get_collider().is_in_group("mob"):
var mob = collision.get_collider()
# we check that we are hitting it from above.
if Vector3.UP.dot(collision.get_normal()) > 0.1:
# If so, we squash it and bounce.
mob.squash()
target_velocity.y = bounce_impulse
# Prevent further duplicate calls.
break

public override void _PhysicsProcess(double delta)
{
// ...

// Iterate through all collisions that occurred this frame.
for (int index = 0; index < GetSlideCollisionCount(); index++)
{
// We get one of the collisions with the player.
KinematicCollision3D collision = GetSlideCollision(index);

// If the collision is with a mob.
// With C# we leverage typing and pattern-matching
// instead of checking for the group we created.
if (collision.GetCollider() is Mob mob)
{
// We check that we are hitting it from above.
if (Vector3.Up.Dot(collision.GetNormal()) > 0.1f)
{
// If so, we squash it and bounce.
mob.Squash();
_targetVelocity.Y = BounceImpulse;
// Prevent further duplicate calls.
break;
}
}
}
}

Itu banyak sekali fungsi baru. Berikut beberapa informasi lebih lanjut tentangnya.

Fungsi get_slide_collision_count()dan get_slide_collision()keduanya berasal dari kelas CharacterBody3D dan terkait dengan move_and_slide().

get_slide_collision()mengembalikan objek KinematicCollision3D yang menyimpan informasi tentang di mana dan bagaimana tabrakan terjadi. Misalnya, kita menggunakan propertinya get_collideruntuk memeriksa apakah kita bertabrakan dengan "gerombolan" dengan memanggilnya is_in_group()collision.get_collider().is_in_group("mob").

Catatan

Metode ini is_in_group()tersedia pada setiap Node .

Untuk memeriksa apakah kita mendarat di monster, kita menggunakan perkalian titik vektor: . Normal tumbukan adalah vektor 3D yang tegak lurus terhadap bidang tempat tumbukan terjadi. Perkalian titik memungkinkan kita membandingkannya dengan arah atas.Vector3.UP.dot(collision.get_normal()) > 0.1

Dengan perkalian titik, jika hasilnya lebih besar dari 0, kedua vektor berada pada sudut kurang dari 90 derajat. Nilai yang lebih besar dari 0.1memberi tahu kita bahwa kita kira-kira berada di atas monster.

Setelah menangani logika squash dan bounce, kami mengakhiri loop lebih awal melalui breakpernyataan untuk mencegah panggilan duplikat lebih lanjut ke mob.squash(), yang mungkin mengakibatkan bug yang tidak diinginkan seperti menghitung skor beberapa kali untuk satu kill.

Kami memanggil satu fungsi yang tidak terdefinisi, mob.squash(), jadi kami harus menambahkannya ke kelas Mob.

Buka skrip mob.gddengan mengklik dua kali pada dok FileSystem . Di bagian atas skrip, kita ingin menentukan sinyal baru bernama squashed. Dan di bagian bawah, Anda dapat menambahkan fungsi squash, tempat kita memancarkan sinyal dan menghancurkan gerombolan.

# Emitted when the player jumped on the mob.
signal squashed

# ...

func squash():
squashed.emit()
queue_free()
// Don't forget to rebuild the project so the editor knows about the new signal.

// Emitted when the player jumped on the mob. [Signal] public delegate void SquashedEventHandler(); // ... public void Squash() { EmitSignal(SignalName.Squashed); QueueFree(); }

Catatan

Saat menggunakan C#, Godot akan membuat peristiwa yang sesuai secara otomatis untuk semua Sinyal yang diakhiri dengan EventHandler , lihat Sinyal C# .

Kita akan menggunakan sinyal tersebut untuk menambahkan poin pada skor di pelajaran berikutnya.

Dengan demikian, Anda seharusnya dapat membunuh monster dengan melompat ke atasnya. Anda dapat menekan F5untuk mencoba permainan dan menetapkannya main.tscnsebagai adegan utama proyek Anda.

Namun, pemain tersebut belum akan mati. Kita akan bahas hal itu di bagian selanjutnya.


Tidak ada komentar:

Posting Komentar