Tautan ke artikel sebelumnya
Untuk memulainya, saya memutuskan untuk mengumpulkan semua kode sebelumnya sehingga mereka yang menggunakan informasi dari artikel sebelumnya akan mengerti bagaimana saya membayangkan program ini secara keseluruhan:
extends KinematicBody2D
#
const GRAVITY: int = 40
const MOVE_SPEED: int = 120 #
const JUMP_POWER: int = 80 #
#
var velocity: Vector2 = Vector2.ZERO
func _physics_process(_delta: float) -> void:
#
move_character() #
jump()
#
self.velocity.y += GRAVITY
self.velocity = self.move_and_slide(self.velocity, Vector2(0, -1))
func move_character() -> void:
var direction: float = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
self.velocity.x = direction * MOVE_SPEED
func jump() -> void:
if self.is_on_floor():
if Input.is_action_pressed("ui_accept"): # ui_accept
#
self.velocity.y -= JUMP_POWER
Saya harap mereka yang telah membaca artikel sebelumnya kurang lebih memahami bagaimana semuanya bekerja. Sekarang mari kembali ke pengembangan.
Mesin negara
Mesin keadaan (dalam pengertian saya) adalah bagian dari program yang menentukan keadaan sesuatu: di udara, di lantai, di langit-langit, atau di dinding, dan juga menentukan apa yang harus terjadi pada karakter di tempat ini atau itu. GodotEngine memiliki hal seperti enum, yang membuat enumerasi, di mana setiap elemen adalah konstanta yang ditentukan dalam kode. Saya pikir lebih baik saya menunjukkan ini dengan contoh:
enum States { # States, States.IN_AIR, States.ON_FLOOR...
IN_AIR, #
ON_FLOOR, #
ON_WALL #
}
Kode ini dapat ditempatkan dengan aman di awal skrip karakter permainan dan perlu diingat bahwa kode itu ada. Selanjutnya, kita menginisialisasi variabel di tempat yang benar var current_state: int = States.IN_AIR, yang sama dengan nol jika kita menggunakan print. Selanjutnya, Anda perlu menentukan apa yang akan dilakukan pemain dalam keadaan saat ini. Saya rasa banyak pengembang berpengalaman yang berasal dari C ++ yang akrab dengan konstruksi switch () {case:}. GDScript memiliki konstruksi adaptasi yang serupa, meskipun switch juga ada dalam agenda. Konstruksinya disebut korek api.
Saya pikir akan lebih tepat untuk menunjukkan konstruksi ini dalam praktiknya, karena akan lebih sulit untuk mengatakannya daripada menunjukkan:
func _physics_process(_delta: float) -> void:
#
match (self.current_state):
States.IN_AIR:
# .
self.move_character()
States.ON_FLOOR:
# , .
self.move_character()
self.jump()
States.ON_WALL:
# , , . .
self.move_character()
#
Tapi kami tetap tidak mengubah keadaan. Kita perlu membuat fungsi terpisah, yang akan kita panggil sebelum pertandingan, untuk mengubah variabel current_state, yang harus ditambahkan ke kode ke variabel lainnya. Dan kami akan memanggil fungsi update_state ().
func update_state() -> void:
# .
if self.is_on_floor():
self.current_state = self.States.ON_FLOOR
elif self.is_on_wall() and !self.is_on_floor():
# .
self.current_state = self.States.ON_WALL
elif self.is_on_wall() and self.is_on_floor():
# . .
self.current_state = self.States.ON_WALL
else: #
self.current_state = self.states.IN_AIR
Sekarang mesin negara sudah siap, kita bisa menambahkan banyak fungsi. Termasuk menambahkan animasi ke karakter ... Bahkan tidak ... Kita bisa menambahkan banyak animasi ke karakter. Sistem telah menjadi modular. Tapi kami belum selesai dengan kode di sini. Saya berkata di awal bahwa saya akan menunjukkan kepada Anda bagaimana melakukan lompatan ekstra di udara, memanjat, dan melompat dari tembok. Mari kita mulai secara berurutan.
Lompatan tambahan di udara
Pertama, tambahkan jump call di status States.IN_AIR ke kecocokan kita, yang akan kita ubah sedikit.
Berikut adalah kode lompatan kami yang saya perbaiki:
func jump() -> void:
# . .
if Input.is_action_pressed("ui_accept"): #
if self.current_state == self.States.ON_FLOOR:
# , _
self.velocity.y -= JUMP_POWER
elif (self.current_state == self.States.IN_AIR or self.current_state == self.States.ON_WALL)
and self.second_jump == true:
#
self.velocity.y = -JUMP_POWER
#
self.second_jump = false
# var second_jump: bool = true . update_state()
# if self.is_on_floor(): self.second_jump = true # .
Komentar pada kode pada dasarnya mengatakan bagaimana saya mengubah program, saya harap Anda memahami kata-kata saya di sana. Namun nyatanya, perbaikan ini cukup untuk mengubah mekanisme lompatan dan meningkatkannya menjadi dua kali lipat. Saya membutuhkan beberapa bulan untuk menemukan metode berikut. Sebenarnya saya menulisnya sehari sebelum kemarin, 1 Oktober 2020.
Memanjat tembok
Sayangnya bagi kami, GodotEngine Wall Normal tidak mengizinkan kami untuk mengetahuinya, yang berarti kami harus membuat kruk kecil. Untuk memulainya, saya akan membuat catatan kaki dari variabel yang tersedia saat ini sehingga Anda dapat dengan mudah mengetahui apa yang telah berubah.
extends KinematicBody2D
#
signal timer_ended # yield wall_jump, .
#
const GRAVITY: int = 40
const MOVE_SPEED: int = 120 #
const JUMP_POWER: int = 80 #
const WALL_JUMP_POWER: int = 60 # .
const CLIMB_SPEED: int = 30 #
#
var velocity: Vector2 = Vector2.ZERO
var second_jump: bool = true
var climbing: bool = false # , , .
var timer_working: bool = false
var is_wall_jump: bool = false # , ,
var left_pressed: bool = false #
var right_pressed: bool = false #
var current_state: int = States.IN_AIR
var timer: float = 0 # , _process(delta: float)
var walls = [false, false, false] # . - . - .
#
#
enum States {
IN_AIR, #
ON_FLOOR, #
ON_WALL #
}
# , _process()
func _process(delta: float):
if timer_working:
timer -= delta
if timer <= 0:
emit_signal("timer_ended")
timer = 0
Sekarang Anda perlu menentukan dinding mana yang sedang dipanjat pemain.
Berikut adalah pohon adegan yang harus Anda persiapkan untuk mengimplementasikan kualifikasi sisi dinding.
Tempatkan 2 Area2D di sisi karakter dan CollisionShape2D keduanya tidak boleh tumpang tindih dengan karakter. Tanda tangani objek WallLeft / WallRight dengan benar dan lampirkan sinyal _on_body_endered dan _on_body_exited ke satu skrip karakter. Berikut adalah kode yang diperlukan untuk menentukan dinding (Tambahkan ke bagian paling akhir skrip):
#
# ,
func _on_WallRight_body_entered(_body):
if (_body.name != self.name):
self.walls[0] = true # , -
func _on_WallRight_body_exited(_body):
self.walls[0] = false # -
func _on_WallLeft_body_entered(_body):
if (_body.name != self.name):
self.walls[2] = true # , -
func _on_WallLeft_body_exited(_body):
self.walls[2] = false # -
Mari kita mulai dengan metode pendakian. Kode akan mengatakan segalanya untuk saya
func climbing() -> void:
if (self.walls[0] or self.walls[2]): #
# action ui_climb. .
self.climbing = Input.is_action_pressed("ui_climb")
else:
self.climbing = false
Dan kita perlu menulis ulang kontrol move_character () sehingga kita tidak bisa hanya bertahan, tetapi naik dan turun, karena kita memiliki arah:
func move_character() -> void:
var direction: float = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
if !self.climbing:
self.velocity.x = direction * MOVE_SPEED
else:
self.velocity.y = direction * CLIMB_SPEED
Dan kami memperbaiki _physics_process () kami:
func _physics_process(_delta: float) -> void:
#
match (self.current_state):
States.IN_AIR:
self.move_character()
States.ON_FLOOR:
self.move_character()
self.jump()
States.ON_WALL:
self.move_character()
#
if !self.climbing:
self.velocity.y += GRAVITY
self.velocity = self.move_and_slide(self.velocity, Vector2(0, -1))
Karakter sekarang harus bisa memanjat dinding.
Lompat dari tembok
Sekarang mari kita terapkan lompatan dari dinding.
func wall_jump() -> void:
if Input.is_action_just_pressed("ui_accept") and Input.is_action_pressed("ui_climb"):
# 1
self.is_wall_jump = true # =
self.velocity.y = -JUMP_POWER # -JUMP_POWER
if walls[0]: #
self.timer = 0.5 # self.timer 0.5
self.timer_enabled = true #
self.left_pressed = true # left_pressed
yield(self, "timer_ended") # timer_ended
self.left_pressed = false # left_pressed
if walls[2]: #
self.timer = 0.5 # self.timer 0.5
self.timer_enabled = true #
self.right_pressed = true # right_pressed
yield(self, "timer_ended") # timer_ended
self.right_pressed = false # right_pressed
self.is_wall_jump = false # .
Kami menambahkan panggilan ke metode ini ke pertandingan kami -> States.ON_WALL dan kami melampirkan metode kami ke sisa _physics_process ().
Kesimpulan
Pada artikel ini, saya telah menunjukkan implementasi mekanika yang relatif kompleks (untuk pemula) di GodotEngine. Tapi ini bukan bagian terakhir dari rangkaian artikel, jadi saya meminta mereka yang tahu bagaimana menerapkan metode yang saya tunjukkan di artikel ini lebih baik menulis tentang mereka di komentar. Saya, dan banyak pembaca, akan berterima kasih atas solusi cepat dan berkualitas tinggi.