godot4制作2D游戏03———添加人物状态机
添加状态机接口
- 创建一个script,名称为state.gd
1 | class_name State extends Node |
这个接口是各个状态机方法属性的定义
为Player添加StateMachine节点
- 在Player下创建一个StateMachine的节点
- 在StateMachine下新添加两个子节点Idle和Walk
- 创建 player_state_machine.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39class_name PlayerStateMachine extends Node
var states: Array[ State ]
var prev_state: State
var current_state: State
// 禁用所有StateMachine以及子节点的_process和_physics_process方法
func _ready() -> void:
process_mode = Node.PROCESS_MODE_DISABLED
pass
func _process(delta: float) -> void:
ChangeState(current_state.Process( delta ))
func _physics_process(delta: float) -> void:
ChangeState(current_state.PhysicsProcess( delta ))
func _unhandled_input(event: InputEvent) -> void:
ChangeState(current_state.HandleInput( event ))
// 获取所有子节点状态机并将第一个作为初始状态
func Initialize(player: Player) -> void:
for c in get_children():
if c is State:
states.append(c)
if states.size() > 0:
states[0].player = player
ChangeState(states[0])
process_mode = Node.PROCESS_MODE_INHERIT
func ChangeState(new_state: State) -> void:
if new_state == null || new_state == current_state:
return
if current_state:
current_state.Exit()
prev_state = current_state
current_state = new_state
current_state.Enter()- 将 player_state_machine.md 挂载到StateMachine节点上
创建两个子状态的脚本
state_idle.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51class_name StateIdle extends State
func _ready() -> void:
pass
func Enter() -> void:
player.UpdateAnimation("idle")
pass
func Exit() -> void:
pass
func Process(delta: float) -> State:
if player.direction != Vector2.ZERO:
return walk
player.velocity = Vector2.ZERO
return null
func PhysicsProcess(delta: float) -> State:
return null
func HandleInput(event: InputEvent) -> State:
return null
class_name StateIdle extends State
func _ready() -> void:
pass
func Enter() -> void:
player.UpdateAnimation("idle")
pass
func Exit() -> void:
pass
func Process(delta: float) -> State:
if player.direction != Vector2.ZERO:
return walk
player.velocity = Vector2.ZERO
return null
func PhysicsProcess(delta: float) -> State:
return null
func HandleInput(event: InputEvent) -> State:
return nullstate_walk.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class_name StateWalk extends State
var move_speed: float = 100.0
func _ready() -> void:
pass
func Enter() -> void:
player.UpdateAnimation("walk")
pass
func Exit() -> void:
pass
func Process(delta: float) -> State:
if player.direction == Vector2.ZERO:
return idle
player.velocity = player.direction * move_speed
if player.SetDirection():
player.UpdateAnimation("walk")
return null
func PhysicsProcess(delta: float) -> State:
return null
func HandleInput(event: InputEvent) -> State:
return null
修改Player节点的脚本
- player.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46class_name Player extends CharacterBody2D
var direction: Vector2 = Vector2.ZERO
var cardinal_direction: Vector2 = Vector2.DOWN
func _ready() -> void:
state_machine.Initialize(self)
pass
func _process(delta: float) -> void:
direction.x = Input.get_action_strength("right") - Input.get_action_strength("left")
direction.y = Input.get_action_strength("down") - Input.get_action_strength("up")
pass
func _physics_process(delta: float) -> void:
move_and_slide()
func SetDirection() -> bool:
var new_dir: Vector2 = cardinal_direction
if direction == Vector2.ZERO:
return false
if direction.y == 0:
new_dir = Vector2.LEFT if direction.x < 0 else Vector2.RIGHT
if direction.x == 0:
new_dir = Vector2.UP if direction.y < 0 else Vector2.DOWN
if cardinal_direction == direction:
return false
cardinal_direction = new_dir
sprite.scale.x = -1 if cardinal_direction == Vector2.LEFT else 1
return true
func UpdateAnimation(state: String) -> void:
animation_player.play(state + "_" + AnimationDirection())
func AnimationDirection() -> String:
if cardinal_direction == Vector2.DOWN:
return "down"
elif cardinal_direction == Vector2.UP:
return "up"
else:
return "side"
总结
- 创建一个StateMachine的节点,下面挂载多个子节点,通过获取子节点的方式动态扩展
- 用户节点脚本不再直接调用动画渲染,只修改direction
- 在执行StateMachine的_process和_physics_process方法时,调用当前State的对应方法来处理动画
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Have a nice day!!