Links zu früheren Artikeln
Zunächst habe ich beschlossen, den gesamten vorherigen Code zu sammeln, damit diejenigen, die die Informationen aus dem vorherigen Artikel verwendet haben, verstehen, wie ich mir das Programm in seiner Gesamtheit vorgestellt habe:
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
Ich hoffe, diejenigen, die den vorherigen Artikel gelesen haben, haben ungefähr verstanden, wie alles funktioniert. Kommen wir nun zur Entwicklung zurück.
Zustandsmaschine
Die Zustandsmaschine (nach meinem Verständnis) ist Teil des Programms, das den Zustand von etwas bestimmt: in der Luft, auf dem Boden, an der Decke oder an der Wand und auch, was mit dem Charakter an dem einen oder anderen Ort geschehen soll. GodotEngine hat so etwas wie eine Aufzählung, die eine Aufzählung erstellt, wobei jedes Element eine im Code angegebene Konstante ist. Ich denke, ich sollte dies besser anhand eines Beispiels zeigen:
enum States { # States, States.IN_AIR, States.ON_FLOOR...
IN_AIR, #
ON_FLOOR, #
ON_WALL #
}
Dieser Code kann sicher ganz am Anfang des Skripts des Spielcharakters eingefügt werden und berücksichtigt, dass er vorhanden ist. Als nächstes initialisieren wir die Variable an der richtigen Stelle var current_state: int = States.IN_AIR, was bei Verwendung von print gleich Null ist. Als nächstes müssen Sie irgendwie bestimmen, was der Spieler im aktuellen Zustand tun wird. Ich denke, viele erfahrene Entwickler, die aus C ++ kamen, sind mit der Konstruktion von switch () {case:} vertraut. GDScript hat ein ähnlich angepasstes Konstrukt, obwohl der Wechsel ebenfalls auf der Tagesordnung steht. Die Konstruktion heißt Match.
Ich denke, es wäre richtiger, diese Konstruktion in der Praxis zu zeigen, da es schwieriger zu sagen sein wird als zu zeigen:
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()
#
Aber wir ändern den Zustand immer noch nicht. Wir müssen eine separate Funktion erstellen, die wir vor dem Match aufrufen, um die Variable current_state zu ändern, die dem Code zu den restlichen Variablen hinzugefügt werden soll. Und wir rufen die Funktion update_state () auf.
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
Jetzt, da die Zustandsmaschine bereit ist, können wir eine Menge Funktionen hinzufügen. Einschließlich des Hinzufügens von Animationen zum Charakter ... Nicht einmal das ... Wir können dem Charakter eine Menge Animationen hinzufügen. Das System ist modular aufgebaut. Aber wir sind hier nicht mit dem Code fertig. Ich sagte am Anfang, dass ich dir zeigen würde, wie man einen zusätzlichen Sprung in die Luft macht, hochklettert und von der Wand springt. Beginnen wir in der richtigen Reihenfolge.
Zusätzlicher Sprung in die Luft
Fügen Sie zuerst den Sprungaufruf im Status States.IN_AIR zu unserem Match hinzu, den wir ein wenig optimieren werden.
Hier ist unser Sprungcode, den ich behoben habe:
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 # .
Die Kommentare zum Code sagen im Grunde, wie ich das Programm geändert habe. Ich hoffe, Sie verstehen meine Worte dort. Tatsächlich reichen diese Korrekturen jedoch aus, um die Sprungmechanik zu ändern und zu verdoppeln. Ich habe ein paar Monate gebraucht, um die folgenden Methoden zu erfinden. Ich habe sie tatsächlich vorgestern, am 1. Oktober 2020, geschrieben.
Die Wände hochklettern
Leider lässt uns die GodotEngine Wall Normal nicht wissen, was bedeutet, dass wir eine kleine Krücke bauen müssen. Zunächst mache ich eine Fußnote zu den derzeit verfügbaren Variablen, damit Sie leicht erkennen können, was sich geändert hat.
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
Jetzt müssen Sie bestimmen, auf welche Wand der Spieler klettert.
Hier ist der
Szenenbaum , den Sie vorbereiten sollten, um das Qualifikationsmerkmal für die Wandseite zu implementieren: Platzieren Sie 2 Area2Ds an den Seiten des Charakters und CollisionShape2D dürfen sich nicht mit dem Charakter überschneiden. Signieren Sie die WallLeft / WallRight-Objekte entsprechend und hängen Sie die Signale _on_body_endered und _on_body_exited an ein einzelnes Zeichenskript an. Hier ist der Code, der zum Definieren der Wände benötigt wird (am Ende des Skripts hinzufügen):
#
# ,
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 # -
Beginnen wir mit der Klettermethode. Der Code sagt alles für mich
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
Und wir müssen das move_character () - Steuerelement neu schreiben, damit wir nicht nur festhalten, sondern auf und ab klettern können, da wir die Richtung haben:
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
Und wir reparieren unseren _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))
Der Charakter sollte nun in der Lage sein, Wände zu besteigen.
Von der Wand springen
Lassen Sie uns nun den Sprung von der Wand ausführen.
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 # .
Wir fügen dieser Methode einen Aufruf zu unserer Übereinstimmung hinzu -> States.ON_WALL und hängen unsere Methode an den Rest des _physics_process () an.
Fazit
In diesem Artikel habe ich die Implementierung relativ komplexer Mechaniken (für Anfänger) in GodotEngine gezeigt. Dies ist jedoch nicht der letzte Teil einer Reihe von Artikeln. Deshalb bitte ich diejenigen, die wissen, wie man die in diesem Artikel gezeigten Methoden implementiert, besser, in den Kommentaren darüber zu schreiben. Ich und viele Leser werden für hochwertige und schnelle Lösungen dankbar sein.