Godotエンジンにプラットフォーマーを実装するためのメカニズム。パート2

こんにちは、これはGodotEngineでプレイ可能なキャラクターを作成することに関する前の記事の続きです。私はついに、空中での2回目のジャンプ、登る、壁から飛び降りるなど、いくつかのメカニズムを実装する方法を理解しました。最初の部分は、後でそれを改良またはやり直すために何かから始める必要があったので、飽和の点でより単純でした。



以前の記事へのリンク



最初に、前の記事の情報を使用した人がプログラム全体をどのように想像したかを理解できるように、前のコードをすべて収集することにしました。



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での比較的複雑なメカニズム(初心者向け)の実装を示しました。しかし、これは一連の記事の最後の部分ではないので、この記事で私が示した方法を実装する方法を知っている人には、コメントにそれらについて書くことをお勧めします。私と多くの読者は、高品質で迅速なソリューションに感謝します。



All Articles