以前の記事へのリンク
最初に、前の記事の情報を使用した人がプログラム全体をどのように想像したかを理解できるように、前のコードをすべて収集することにしました。
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
前の記事を読んだ人は、すべてがどのように機能するかをほぼ理解していることを願っています。それでは、開発に戻りましょう。
ステートマシン
ステートマシン(私の理解では)は、何かの状態を決定するプログラムの一部です:空中、床、天井、または壁、そしてまた、この場所またはその場所でキャラクターに何が起こるべきかを決定します。GodotEngineにはenumのようなものがあり、列挙を作成します。各要素はコードで指定された定数です。私はこれを例で示したほうがいいと思います:
enum States { # States, States.IN_AIR, States.ON_FLOOR...
IN_AIR, #
ON_FLOOR, #
ON_WALL #
}
このコードは、ゲームキャラクターのスクリプトの最初に安全に配置でき、存在することを覚えておいてください。次に、変数を適切な場所で初期化します。varcurrent_state:int = States.IN_AIR、printを使用する場合はゼロになります。次に、プレイヤーが現在の状態で何をするかをどうにかして決定する必要があります。 C ++から来た多くの経験豊富な開発者は、switch(){case:}の構築に精通していると思います。 GDScriptにも同様の適合構造がありますが、切り替えも議題になっています。構造はマッチと呼ばれます。
示すよりも伝えるのが難しいので、実際にこの構造を示す方が正しいと思います。
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()
#
しかし、私たちはまだ状態を変えません。コードに追加する必要がある変数current_stateを残りの変数に変更するために、一致する前に呼び出す別の関数を作成する必要があります。そして、関数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
ステートマシンの準備ができたので、たくさんの機能を追加できます。キャラクターへのアニメーションの追加を含む...それでも...キャラクターに大量のアニメーションを追加できます。システムはモジュール式になりました。しかし、ここでのコードはまだ完成していません。私は最初に、空中で余分なジャンプをする方法、登る方法、そして壁から飛び降りる方法を紹介すると言いました。順番に始めましょう。
空中での余分なジャンプ
まず、States.IN_AIR状態のジャンプ呼び出しを一致に追加します。これを少し調整します。
これが私が修正したジャンプのコードです:
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 # .
コードへのコメントは、基本的に私がプログラムをどのように変更したかを示しています。そこで私の言葉を理解していただければ幸いです。しかし実際には、これらの修正はジャンプの仕組みを変更し、2倍に改善するのに十分です。次の方法を発明するのに数ヶ月かかりました。実は昨日の前日、2020年10月1日に書いたんです。
壁を登る
残念ながら、GodotEngine Wall Normalでは知ることができません。つまり、小さなクラッチを作成する必要があります。まず、現在利用可能な変数の脚注を作成して、何が変更されたかを簡単に判断できるようにします。
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
次に、プレーヤーが登っている壁を特定する必要があります。
壁側修飾子を実装するために準備する必要があるシーンツリーは次の
とおりです。キャラクターの側面に2つのArea2Dを配置し、CollisionShape2Dの両方をキャラクターとオーバーラップさせないでください。WallLeft / WallRightオブジェクトに適切に署名し、_on_body_enderedおよび_on_body_exitedシグナルを単一の文字スクリプトに添付します。壁を定義するために必要なコードは次のとおりです(スクリプトの最後に追加):
#
# ,
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 # -
登山方法から始めましょう。コードは私のためにすべてを言うでしょう
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
そして、move_character()コントロールを書き直して、保持するだけでなく、方向性があるので上下に登ることができるようにする必要があります。
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
そして、_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))
これで、キャラクターは壁を登ることができるはずです。
壁から飛び降りる
それでは、壁からのジャンプを実装しましょう。
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 # .
このメソッドへの呼び出しをmatch-> States.ON_WALLに追加し、メソッドを残りの_physics_process()にアタッチしました。
結論
この記事では、GodotEngineでの比較的複雑なメカニズム(初心者向け)の実装を示しました。しかし、これは一連の記事の最後の部分ではないので、この記事で私が示した方法を実装する方法を知っている人には、コメントにそれらについて書くことをお勧めします。私と多くの読者は、高品質で迅速なソリューションに感謝します。