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"]
libraries = {
"": SubResource("AnimationLibrary_qbwwp")
&"": SubResource("AnimationLibrary_qbwwp")
}
[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
allow_system_fallback=true
force_autohinter=false
modulate_color_glyphs=false
hinting=1
subpixel_positioning=1
keep_rounding_remainders=true

View File

@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/icon.webp-e94f9a68b0f625a567a797079e4d325f.ct
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
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/premult_alpha=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/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
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/premult_alpha=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")
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("fire"):
func _unhandled_input(input_event: InputEvent) -> void:
if input_event.is_action_pressed("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"
var PLAYER_STATE = preload("res://player/player_state.gd").PLAYER_STATE
@onready var idle: Node = $Idle
@onready var move: Node = $Move
@onready var jump: Node = $Jump
@@ -8,11 +10,11 @@ extends "res://state_machine/state_machine.gd"
func _ready() -> void:
states_map = {
"idle": idle,
"move": move,
"jump": jump,
"stagger": stagger,
"attack": attack,
PLAYER_STATE.idle: idle,
PLAYER_STATE.move: move,
PLAYER_STATE.jump: jump,
PLAYER_STATE.stagger: stagger,
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.
if not _active:
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])
if state_name == "jump" and current_state == move:
if state_name == PLAYER_STATE.jump and current_state == move:
jump.initialize(move.speed, move.velocity)
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,
# 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]:
return
_change_state("attack")
_change_state(PLAYER_STATE.attack)
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/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
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/premult_alpha=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:
owner.get_node(^"AnimationPlayer").play("idle")
owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.idle)
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 animation only affects the Body Sprite2D's modulate property so it
# could stack with other animations if we had two AnimationPlayer nodes.
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:
assert(anim_name == "stagger")
finished.emit("previous")
assert(anim_name == PLAYER_STATE.stagger)
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.
func enter() -> void:
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:
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()
vertical_speed = 600.0
owner.get_node(^"AnimationPlayer").play("idle")
owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.idle)
func update(delta: float) -> void:
@@ -46,7 +46,7 @@ func update(delta: float) -> void:
move_horizontally(delta, input_direction)
animate_jump_height(delta)
if height <= 0.0:
finished.emit("previous")
finished.emit(PLAYER_STATE.previous)
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.
func handle_input(event: InputEvent) -> void:
if event.is_action_pressed("simulate_damage"):
finished.emit("stagger")
func handle_input(input_event: InputEvent) -> void:
if input_event.is_action_pressed("simulate_damage"):
finished.emit(PLAYER_STATE.stagger)
func get_input_direction() -> Vector2:

View File

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

View File

@@ -1,14 +1,14 @@
extends "on_ground.gd"
func enter() -> void:
owner.get_node(^"AnimationPlayer").play("idle")
owner.get_node(^"AnimationPlayer").play(PLAYER_STATE.idle)
func handle_input(event: InputEvent) -> void:
return super.handle_input(event)
func handle_input(input_event: InputEvent) -> void:
return super.handle_input(input_event)
func update(_delta: float) -> void:
var input_direction: Vector2 = get_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()
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:
return super.handle_input(event)
func handle_input(input_event: InputEvent) -> void:
return super.handle_input(input_event)
func update(_delta: float) -> void:
var input_direction := get_input_direction()
if input_direction.is_zero_approx():
finished.emit("idle")
finished.emit(PLAYER_STATE.idle)
update_look_direction(input_direction)
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 velocity := Vector2()
func handle_input(event: InputEvent) -> void:
if event.is_action_pressed("jump"):
finished.emit("jump")
return super.handle_input(event)
func handle_input(input_event: InputEvent) -> void:
if input_event.is_action_pressed("jump"):
finished.emit(PLAYER_STATE.jump)
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
func _unhandled_input(event: InputEvent) -> void:
func _unhandled_input(input_event: InputEvent) -> void:
if not state == States.ATTACK:
return
if attack_input_state != AttackInputStates.LISTENING:
return
if event.is_action_pressed("attack"):
if input_event.is_action_pressed("attack"):
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/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
@@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
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/premult_alpha=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."
config/tags=PackedStringArray("2d", "ai", "demo", "official")
run/main_scene="res://Demo.tscn"
config/features=PackedStringArray("4.4")
config/features=PackedStringArray("4.5")
config/icon="res://icon.webp"
[debug]

View File

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

View File

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

View File

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

View File

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