(摘) Godot状态鸡(状态机)

声明:内容源自网络,版权归原作者所有。若有侵权请在网页聊天中联系我

状态鸡就是 处理对象状态的机器。目的还是简化大型程序,分而治之。避免“史山代码”。

管理机(StateMachine)

顾名思义,即进行状态的管理。其实这里的代码就只是把初始状态传入(_ready中),一些对象(管理机自己)传入。
然后将各事件传给各状态(handle_input/update/physics_update)。定义一个切换状态的函数(transition_to)
看起来没进行啥真正的管理。各个状态都在它之下进行切换。新添加其它状态也一样。

extends Node
class_name StateMachine

signal transitioned(state_name) 												# 状态改变信号
@export var initial_state := NodePath()											# 初始状态场景
@onready var state :State = get_node(initial_state)								# 获取初始状态场景节点

func _ready() -> void:
	await owner.ready															# 等待父节点加载完成
	for child in get_children():												# 获取所有子节点
		if child is State:														# 如果节点是状态节点
			child.state_machine = self											# 子节点状态机对象设为自己(把自己传入,便于每个状态调用)
	state.enter()																# 进入初始状态
	
func _unhandled_input(event: InputEvent) -> void:								# 把输入事件传给状态机
	state.handle_input(event)
	
func _process(delta: float) -> void:											# 把更新事件传给状态机
	state.update(delta)
	
func _physics_process(delta: float) -> void:									# 把物理更新事件传给状态机
	state.physics_update(delta)

# 切换状态
# 这里在状态中调用,切换为另一状态
func transition_to(traget_state_name: String):
	if not has_node(traget_state_name):
		return
		
	state.exit()																# 退出原状态
	state = get_node(traget_state_name)											# 根据traget_state_name名,获取节点
	state.enter()																# 进入新状态
	emit_signal("transitioned",state.name)										# 触发状态修改信号

状态类(State)

这是一个类,定义了常用函数(enter\exit…),获取状态机和玩家player节点等,用于控制。
具体每个状态需继承于此类。

extends Node
class_name State

var state_machine :StateMachine = null
var player

func _ready() -> void:
	await owner.ready
	player = owner.find_child("Player")

func handle_input(_event:InputEvent) -> void:
	pass
	
func update(_delta:float) -> void:
	pass
	
func physics_update(_delta: float) -> void:
	pass
	
func enter() -> void:
	pass

func exit() -> void:
	pass

具体状态

例如Idle状态 state_idel.gd

extends State

var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")		# 获取项目中设置的重力

func enter():
	# name为当前节点名(状态)
	# owner 该节点的所有者
	# state_machine 为状态机节点
	# player 继承自State,获取玩家节点。这里与owner相同,只是在State类里自动加载为player
	# print("进入",name, "  ",owner, "  ", state_machine,"  ",player)  
	player.velocity = Vector2.ZERO	# 当前速度向量,单位为像素每秒
        # 这里的play节点是定义在玩家Player节点之下的动画播放节点  
        # 代码播放动画节点下面的定义的动画
	player.find_child("play").play(name.to_lower())  

# 处理按键切换其它状态
func update(_delta) -> void:
	if not player.is_on_floor():
		player.velocity.y += gravity * _delta # gravity 重力强度
		player.move_and_slide()
	var direction := Input.get_axis("ui_left","ui_right")	# 当左右移动时,进入Run状态
	if direction != 0:
		state_machine.transition_to("Run")
	if Input.is_action_just_pressed("ui_accept"):  # 跳
		state_machine.transition_to("Jump")
		player.velocity.y = player.JUMP_VELOCITY
	if Input.is_action_just_pressed("ui_k"):   # 攻击
		state_machine.transition_to("Attack")

func exit():
	print("退出",name)

相关文章