Add player_state dictionary to manage player state names for FSM demo (#1143)

* Add player_state dictionary to manage player state names for FSM demo

* Use a constant typed Dictionary of StringNames and update to Godot 4.5

---------

Co-authored-by: Aaron Franke <arnfranke@yahoo.com>
This commit is contained in:
Yaxian
2025-10-02 12:33:27 +08:00
committed by GitHub
parent 69b317692e
commit 1d8fa9c44d
40 changed files with 108 additions and 67 deletions

View File

@@ -41,7 +41,7 @@ start_state = NodePath("Idle")
[node name="AnimationPlayer" parent="Player" index="1"] [node name="AnimationPlayer" parent="Player" index="1"]
libraries = { libraries = {
"": SubResource("AnimationLibrary_qbwwp") &"": SubResource("AnimationLibrary_qbwwp")
} }
[node name="StateNameDisplayer" parent="Player" index="5"] [node name="StateNameDisplayer" parent="Player" index="5"]

View File

@@ -1 +1 @@
uid://8e5wvxrlv1ct uid://h4p2eq3snhjs

View File

@@ -21,6 +21,7 @@ msdf_pixel_range=8
msdf_size=48 msdf_size=48
allow_system_fallback=true allow_system_fallback=true
force_autohinter=false force_autohinter=false
modulate_color_glyphs=false
hinting=1 hinting=1
subpixel_positioning=1 subpixel_positioning=1
keep_rounding_remainders=true keep_rounding_remainders=true

View File

@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/icon.webp-e94f9a68b0f625a567a797079e4d325f.ct
compress/mode=0 compress/mode=0
compress/high_quality=false compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1 compress/hdr_compression=1
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true process/fix_alpha_border=true
process/premult_alpha=false process/premult_alpha=false
process/normal_map_invert_y=false process/normal_map_invert_y=false

View File

@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/body.png-313f6363670a5852a7b7126ab476d8b1.cte
compress/mode=0 compress/mode=0
compress/high_quality=false compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1 compress/hdr_compression=1
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true process/fix_alpha_border=true
process/premult_alpha=false process/premult_alpha=false
process/normal_map_invert_y=false process/normal_map_invert_y=false

View File

@@ -1 +1 @@
uid://crm4tdogdpumr uid://b44c6s1shm5f7

View File

@@ -2,8 +2,8 @@ extends Node2D
var bullet := preload("Bullet.tscn") var bullet := preload("Bullet.tscn")
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(input_event: InputEvent) -> void:
if event.is_action_pressed("fire"): if input_event.is_action_pressed("fire"):
fire() fire()

View File

@@ -1 +1 @@
uid://duw4ejcjfrp7o uid://toean0857ojh

View File

@@ -1 +1 @@
uid://clya0ymtv6cnn uid://bctcyb2y6vuvd

View File

@@ -0,0 +1,13 @@
extends "res://state_machine/state.gd"
const PLAYER_STATE: Dictionary[StringName, StringName]= {
&"previous": &"previous",
&"jump": &"jump",
&"idle": &"idle",
&"move": &"move",
&"stagger": &"stagger",
&"attack": &"attack",
&"die": &"die",
&"dead": &"dead",
&"walk": &"walk",
}

View File

@@ -0,0 +1 @@
uid://de3fjmahdmo0r

View File

@@ -1,5 +1,7 @@
extends "res://state_machine/state_machine.gd" extends "res://state_machine/state_machine.gd"
var PLAYER_STATE = preload("res://player/player_state.gd").PLAYER_STATE
@onready var idle: Node = $Idle @onready var idle: Node = $Idle
@onready var move: Node = $Move @onready var move: Node = $Move
@onready var jump: Node = $Jump @onready var jump: Node = $Jump
@@ -8,11 +10,11 @@ extends "res://state_machine/state_machine.gd"
func _ready() -> void: func _ready() -> void:
states_map = { states_map = {
"idle": idle, PLAYER_STATE.idle: idle,
"move": move, PLAYER_STATE.move: move,
"jump": jump, PLAYER_STATE.jump: jump,
"stagger": stagger, PLAYER_STATE.stagger: stagger,
"attack": attack, PLAYER_STATE.attack: attack,
} }
@@ -20,21 +22,21 @@ func _change_state(state_name: String) -> void:
# The base state_machine interface this node extends does most of the work. # The base state_machine interface this node extends does most of the work.
if not _active: if not _active:
return return
if state_name in ["stagger", "jump", "attack"]: if state_name in [PLAYER_STATE.stagger, PLAYER_STATE.jump, PLAYER_STATE.attack]:
states_stack.push_front(states_map[state_name]) states_stack.push_front(states_map[state_name])
if state_name == "jump" and current_state == move: if state_name == PLAYER_STATE.jump and current_state == move:
jump.initialize(move.speed, move.velocity) jump.initialize(move.speed, move.velocity)
super._change_state(state_name) super._change_state(state_name)
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(input_event: InputEvent) -> void:
# Here we only handle input that can interrupt states, attacking in this case, # Here we only handle input that can interrupt states, attacking in this case,
# otherwise we let the state node handle it. # otherwise we let the state node handle it.
if event.is_action_pressed("attack"): if input_event.is_action_pressed(PLAYER_STATE.attack):
if current_state in [attack, stagger]: if current_state in [attack, stagger]:
return return
_change_state("attack") _change_state(PLAYER_STATE.attack)
return return
current_state.handle_input(event) current_state.handle_input(input_event)

View File

@@ -1 +1 @@
uid://bt4prj2loisak uid://c5dcvd5nvu0e0

View File

@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/shadow.png-493c4635eca1ce8bdece629560617dc7.c
compress/mode=0 compress/mode=0
compress/high_quality=false compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1 compress/hdr_compression=1
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true process/fix_alpha_border=true
process/premult_alpha=false process/premult_alpha=false
process/normal_map_invert_y=false process/normal_map_invert_y=false

View File

@@ -1,8 +1,8 @@
extends "res://state_machine/state.gd" extends "res://player/player_state.gd"
func enter() -> void: func enter() -> void:
owner.get_node(^"AnimationPlayer").play("idle") owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.idle)
func _on_Sword_attack_finished() -> void: func _on_Sword_attack_finished() -> void:
finished.emit("previous") finished.emit(PLAYER_STATE.previous)

View File

@@ -1 +1 @@
uid://bianktk6rvu15 uid://d2qmapjilk64d

View File

@@ -1,12 +1,12 @@
extends "res://state_machine/state.gd" extends "res://player/player_state.gd"
# The stagger state end with the stagger animation from the AnimationPlayer. # The stagger state end with the stagger animation from the AnimationPlayer.
# The animation only affects the Body Sprite2D's modulate property so it # The animation only affects the Body Sprite2D's modulate property so it
# could stack with other animations if we had two AnimationPlayer nodes. # could stack with other animations if we had two AnimationPlayer nodes.
func enter() -> void: func enter() -> void:
owner.get_node(^"AnimationPlayer").play("stagger") owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.stagger)
func _on_animation_finished(anim_name: String) -> void: func _on_animation_finished(anim_name: String) -> void:
assert(anim_name == "stagger") assert(anim_name == PLAYER_STATE.stagger)
finished.emit("previous") finished.emit(PLAYER_STATE.previous)

View File

@@ -1 +1 @@
uid://bdx6kcg1n8na3 uid://c5kuv4fn7sjn

View File

@@ -1 +1 @@
uid://dgpfeu3yan7hq uid://qc5os1n5tx4u

View File

@@ -1,10 +1,10 @@
extends "res://state_machine/state.gd" extends "res://player/player_state.gd"
# Initialize the state. E.g. change the animation. # Initialize the state. E.g. change the animation.
func enter() -> void: func enter() -> void:
owner.set_dead(true) owner.set_dead(true)
owner.get_node(^"AnimationPlayer").play("die") owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.die)
func _on_animation_finished(_anim_name: String) -> void: func _on_animation_finished(_anim_name: String) -> void:
finished.emit("dead") finished.emit(PLAYER_STATE.dead)

View File

@@ -1 +1 @@
uid://badi2yipj0y74 uid://dt2ivp7vxkcwu

View File

@@ -36,7 +36,7 @@ func enter() -> void:
horizontal_velocity = Vector2() horizontal_velocity = Vector2()
vertical_speed = 600.0 vertical_speed = 600.0
owner.get_node(^"AnimationPlayer").play("idle") owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.idle)
func update(delta: float) -> void: func update(delta: float) -> void:
@@ -46,7 +46,7 @@ func update(delta: float) -> void:
move_horizontally(delta, input_direction) move_horizontally(delta, input_direction)
animate_jump_height(delta) animate_jump_height(delta)
if height <= 0.0: if height <= 0.0:
finished.emit("previous") finished.emit(PLAYER_STATE.previous)
func move_horizontally(delta: float, direction: Vector2) -> void: func move_horizontally(delta: float, direction: Vector2) -> void:

View File

@@ -1 +1 @@
uid://c3jdmjrutxlu3 uid://dqcb4mauxmn6i

View File

@@ -1,9 +1,9 @@
extends "res://state_machine/state.gd" extends "res://player/player_state.gd"
# Collection of important methods to handle direction and animation. # Collection of important methods to handle direction and animation.
func handle_input(event: InputEvent) -> void: func handle_input(input_event: InputEvent) -> void:
if event.is_action_pressed("simulate_damage"): if input_event.is_action_pressed("simulate_damage"):
finished.emit("stagger") finished.emit(PLAYER_STATE.stagger)
func get_input_direction() -> Vector2: func get_input_direction() -> Vector2:

View File

@@ -1 +1 @@
uid://bbwnftow50xk3 uid://b1olwifehhkdo

View File

@@ -1,14 +1,14 @@
extends "on_ground.gd" extends "on_ground.gd"
func enter() -> void: func enter() -> void:
owner.get_node(^"AnimationPlayer").play("idle") owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.idle)
func handle_input(event: InputEvent) -> void: func handle_input(input_event: InputEvent) -> void:
return super.handle_input(event) return super.handle_input(input_event)
func update(_delta: float) -> void: func update(_delta: float) -> void:
var input_direction: Vector2 = get_input_direction() var input_direction: Vector2 = get_input_direction()
if input_direction: if input_direction:
finished.emit("move") finished.emit(PLAYER_STATE.move)

View File

@@ -1 +1 @@
uid://duqfhrergdg0p uid://txngtvc75ll4

View File

@@ -9,17 +9,17 @@ func enter() -> void:
var input_direction := get_input_direction() var input_direction := get_input_direction()
update_look_direction(input_direction) update_look_direction(input_direction)
owner.get_node(^"AnimationPlayer").play("walk") owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.walk)
func handle_input(event: InputEvent) -> void: func handle_input(input_event: InputEvent) -> void:
return super.handle_input(event) return super.handle_input(input_event)
func update(_delta: float) -> void: func update(_delta: float) -> void:
var input_direction := get_input_direction() var input_direction := get_input_direction()
if input_direction.is_zero_approx(): if input_direction.is_zero_approx():
finished.emit("idle") finished.emit(PLAYER_STATE.idle)
update_look_direction(input_direction) update_look_direction(input_direction)
if Input.is_action_pressed("run"): if Input.is_action_pressed("run"):

View File

@@ -1 +1 @@
uid://bprpybddlsqly uid://qvf7gyuvbbd3

View File

@@ -3,7 +3,7 @@ extends "../motion.gd"
var speed := 0.0 var speed := 0.0
var velocity := Vector2() var velocity := Vector2()
func handle_input(event: InputEvent) -> void: func handle_input(input_event: InputEvent) -> void:
if event.is_action_pressed("jump"): if input_event.is_action_pressed("jump"):
finished.emit("jump") finished.emit(PLAYER_STATE.jump)
return super.handle_input(event) return super.handle_input(input_event)

View File

@@ -1 +1 @@
uid://qyf46nijd62s uid://vj5xnepb8ost

View File

@@ -66,12 +66,12 @@ func _change_state(new_state: States) -> void:
state = new_state state = new_state
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(input_event: InputEvent) -> void:
if not state == States.ATTACK: if not state == States.ATTACK:
return return
if attack_input_state != AttackInputStates.LISTENING: if attack_input_state != AttackInputStates.LISTENING:
return return
if event.is_action_pressed("attack"): if input_event.is_action_pressed("attack"):
attack_input_state = AttackInputStates.REGISTERED attack_input_state = AttackInputStates.REGISTERED

View File

@@ -1 +1 @@
uid://1j2saaqxja3i uid://bqq2dspaof34w

View File

@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/sword.png-fc7f0084cdf333c826eda2b33f2ec3cc.ct
compress/mode=0 compress/mode=0
compress/high_quality=false compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1 compress/hdr_compression=1
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true process/fix_alpha_border=true
process/premult_alpha=false process/premult_alpha=false
process/normal_map_invert_y=false process/normal_map_invert_y=false

View File

@@ -1 +1 @@
uid://d1hwnb818bhik uid://beommici1nnah

View File

@@ -16,7 +16,7 @@ pattern in GDscript, including Hierarchical States, and a
pushdown automaton." pushdown automaton."
config/tags=PackedStringArray("2d", "ai", "demo", "official") config/tags=PackedStringArray("2d", "ai", "demo", "official")
run/main_scene="res://Demo.tscn" run/main_scene="res://Demo.tscn"
config/features=PackedStringArray("4.4") config/features=PackedStringArray("4.5")
config/icon="res://icon.webp" config/icon="res://icon.webp"
[debug] [debug]

View File

@@ -4,7 +4,7 @@ extends Node
# and makes sure every State object had all of these methods. # and makes sure every State object had all of these methods.
@warning_ignore("unused_signal") @warning_ignore("unused_signal")
signal finished(next_state_name: String) signal finished(next_state_name: StringName)
# Initialize the state. E.g. change the animation. # Initialize the state. E.g. change the animation.
func enter() -> void: func enter() -> void:
@@ -16,7 +16,7 @@ func exit() -> void:
pass pass
func handle_input(_event: InputEvent) -> void: func handle_input(_input_event: InputEvent) -> void:
pass pass

View File

@@ -1 +1 @@
uid://dhqouwibt7w6w uid://bap5du402gbi

View File

@@ -45,8 +45,8 @@ func set_active(value: bool) -> void:
current_state = null current_state = null
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(input_event: InputEvent) -> void:
current_state.handle_input(event) current_state.handle_input(input_event)
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:

View File

@@ -1 +1 @@
uid://b472efbw14vob uid://cq4kyoh2byjwr