Links para artigos anteriores
Para começar, decidi coletar todo o código anterior para que aqueles que usaram as informações do artigo anterior entendessem como imaginei o programa em sua totalidade:
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
Espero que aqueles que leram o artigo anterior tenham entendido aproximadamente como tudo funciona. Agora vamos voltar ao desenvolvimento.
Máquina de estado
A máquina de estados (no meu entendimento) é uma parte do programa que determina o estado de algo: no ar, no chão, no teto ou na parede, e também determina o que deve acontecer com o personagem em um lugar ou outro. GodotEngine tem algo como enum, que cria uma enumeração, onde cada elemento é uma constante especificada no código. Acho melhor mostrar isso com um exemplo:
enum States { # States, States.IN_AIR, States.ON_FLOOR...
IN_AIR, #
ON_FLOOR, #
ON_WALL #
}
Este código pode ser colocado com segurança bem no início do script do personagem do jogo e tenha em mente que ele existe. Em seguida, inicializamos a variável no lugar certo var current_state: int = States.IN_AIR, que é igual a zero se usarmos print. Em seguida, você precisa determinar de alguma forma o que o player fará no estado atual. Acho que muitos desenvolvedores experientes que vieram do C ++ estão familiarizados com a construção switch () {case:}. GDScript tem uma construção adaptada semelhante, embora switch também esteja na agenda. A construção é chamada de jogo.
Acho que seria mais correto mostrar essa construção na prática, pois será mais difícil dizer do que mostrar:
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()
#
Mas ainda não mudamos o estado. Precisamos criar uma função separada, que chamaremos antes da partida, para alterar a variável current_state, que deve ser adicionada ao código para o resto das variáveis. E vamos chamar a função 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
Agora que a máquina de estado está pronta, podemos adicionar uma tonelada de funções. Incluindo adicionar animações ao personagem ... Nem isso ... Podemos adicionar uma tonelada de animações ao personagem. O sistema tornou-se modular. Mas ainda não terminamos com o código aqui. Eu disse no início que iria mostrar como dar um salto extra no ar, subir e pular da parede. Vamos começar em ordem.
Salto adicional no ar
Primeiro, adicione a chamada de salto no estado States.IN_AIR à nossa correspondência, que iremos ajustar um pouco.
Aqui está o código do nosso salto que consertei:
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 # .
Os comentários ao código basicamente dizem como mudei o programa, espero que você entenda minhas palavras aqui. Mas, na verdade, essas correções são suficientes para mudar a mecânica do salto e melhorar para dobrar. Levei alguns meses para inventar os métodos a seguir. Na verdade, eu os escrevi anteontem, 1º de outubro de 2020.
Escalando as paredes
Infelizmente para nós, o GodotEngine Wall Normal não nos permite saber, o que significa que teremos que criar uma pequena muleta. Para começar, farei uma nota de rodapé das variáveis atualmente disponíveis para que você possa saber facilmente o que mudou.
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
Agora você precisa determinar em qual parede o jogador está escalando.
Aqui está a árvore de cena que você deve preparar para implementar o qualificador do lado da parede:
Coloque 2 Area2Ds nas laterais do personagem e CollisionShape2D ambos não devem se sobrepor ao personagem. Assine os objetos WallLeft / WallRight apropriadamente e anexe os sinais _on_body_endered e _on_body_exited a um script de caractere único. Aqui está o código necessário para definir as paredes (adicionar no final do script):
#
# ,
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 # -
Vamos começar com o método de escalada. O código vai dizer tudo para mim
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
E precisamos reescrever o controle move_character () para que possamos não apenas segurar, mas subir e descer, já que temos direção:
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
E corrigimos nosso _physics_process ():
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))
O personagem agora deve ser capaz de escalar paredes.
Pule da parede
Agora vamos implementar o salto da parede.
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 # .
Adicionamos uma chamada a este método para nossa correspondência -> States.ON_WALL e anexamos nosso método ao resto do _physics_process ().
Conclusão
Neste artigo, mostrei a implementação de mecânicas relativamente complexas (para iniciantes) no GodotEngine. Mas esta não é a última parte de uma série de artigos, então peço àqueles que sabem como implementar os métodos que mostrei neste artigo que escrevam sobre eles nos comentários. Eu, e muitos leitores, seremos gratos por soluções rápidas e de alta qualidade.