Bläddra i källkod

Fix and improve Role Playing Game: Gamepad, TileMapLayer, upgrade for 4.4.1 (#1248)

* Improve gamepad behaviour in rpg

Fix gamepad can't do dialogue.
Fix character often plays bump animation with gamepad when there's no
obstacles around.

Add gamepad A to ui_accept so gamepads can press buttons in dialogue and
combat.

Use get_vector so we get an automatic deadzone on our input.

Round our input direction so we don't pass Vector2i(0,0) to
request_move, get denied, and then do a bump animation.

* Use SpriteFrames instead of animating texture

Seems like Godot 4 doesn't allow toggling AnimationPlayer tracks by
making children editable so the texture is no longer being set and the
player is invisible. Regardless, this workflow is also not scalable to
multiple enemy types. Instead, use SpriteFrames to setup different
visuals for each slime.

Triggers animations from code alongside AnimationPlayer calls because
doing it in AnimationPlayer seemed unnecessarily complex.

* Give all slimes a common base class

Split player input handling from walker to allow both slimes to use the
same visual setup. In a real game, you'd want this kind of setup so
enemies are able to walk on the grid too.

Remove unused actor.gd -- looks like this was an early version of
walker.gd.

Setup opponent slime as type=Actor because otherwise you can't fight
them.

* Convert TileMap to TileMapLayer

Used "Extract TileMap layers as individual TileMapLayer nodes" in the
TileMap editor and removed the 0 layer argument from functions in
grid.gd

* Explain and rename facing direction node

When I first started looking into the character, I couldn't figure out
what this play button texture was for. After experimenting, it might be
an old version of the character visuals or a demo to show how to make
your character rotate.

* Fade to black between scenes

Fix blue screen when entering combat.

Our fade ColorRect was hidden so it never displayed. We removed the
exploration scene before fading so the fade started with an empty scene
with a blue skybox.

* Loop idle animation in combat

Add AnimationTree to combat character setup so we can tell when the
take_damage animation completes. This was harder than expected because
by default Godot modulates to black and sets scale to 0 unless you
specify otherwise with the RESET animation.

* Add a CanvasLayer above each clickable Control root

Fix mouse can't click buttons.

Seems like we're not able to click buttons in Godot 4 without a
CanvasLayer root. Adding that makes it work, but required some
restructuring to point at the correct node. In cases like combat+ui,
change it to emit a signal to use the "call down, signal up" pattern.

* Remove error comment that no longer occurs

Neither winning nor fleeing triggers this warning on Godot
v4.4.1.stable.official [49a5bc7b6].

* Rename parent -> grid

Make it clearer that the parent of the player and other pawns is
expected to be our Grid class.
David Briscoe 2 dagar sedan
förälder
incheckning
aecf661c9c

+ 15 - 4
2d/role_playing_game/combat/combat.gd

@@ -3,6 +3,17 @@ extends Node
 signal combat_finished(winner: Combatant, loser: Combatant)
 
 
+@onready var ui := $CombatCanvas/UI
+
+
+func _ready() -> void:
+	ui.flee.connect(_on_flee)
+
+
+func _on_flee(winner: Combatant, loser: Combatant) -> void:
+	finish_combat(winner, loser)
+
+
 func initialize(combat_combatants: Array[PackedScene]) -> void:
 	for combatant_scene in combat_combatants:
 		var combatant := combatant_scene.instantiate()
@@ -11,20 +22,20 @@ func initialize(combat_combatants: Array[PackedScene]) -> void:
 			combatant.get_node("Health").dead.connect(_on_combatant_death.bind(combatant))
 		else:
 			combatant.queue_free()
-	$UI.initialize()
+	ui.initialize()
 	$TurnQueue.initialize()
 
 
 func clear_combat() -> void:
 	for n in $Combatants.get_children():
+		# Player characters.
 		n.queue_free()
-	for n in $UI/Combatants.get_children():
+	for n in ui.get_node("Combatants").get_children():
+		# Health bars.
 		n.queue_free()
 
 
 func finish_combat(winner: Combatant, loser: Combatant) -> void:
-	# FIXME: Error calling from signal 'combat_finished' to callable:
-	# 'Node(game.gd)::_on_combat_finished': Cannot convert argument 1 from Object to Object.
 	combat_finished.emit(winner, loser)
 
 

+ 13 - 11
2d/role_playing_game/combat/combat.tscn

@@ -876,17 +876,19 @@ script = SubResource("1")
 script = ExtResource("2")
 combatants_list = NodePath("../Combatants")
 
-[node name="UI" type="Control" parent="." node_paths=PackedStringArray("combatants_node")]
+[node name="CombatCanvas" type="CanvasLayer" parent="."]
+
+[node name="UI" type="Control" parent="CombatCanvas" node_paths=PackedStringArray("combatants_node")]
 layout_mode = 3
 anchors_preset = 0
 offset_right = 1280.0
 offset_bottom = 720.0
 theme = ExtResource("3")
 script = ExtResource("4")
-combatants_node = NodePath("../Combatants")
+combatants_node = NodePath("../../Combatants")
 info_scene = ExtResource("5")
 
-[node name="Combatants" type="HBoxContainer" parent="UI"]
+[node name="Combatants" type="HBoxContainer" parent="CombatCanvas/UI"]
 layout_mode = 0
 offset_left = 20.0
 offset_top = 77.0
@@ -894,37 +896,37 @@ offset_right = 1260.0
 offset_bottom = 328.0
 theme_override_constants/separation = 360
 
-[node name="Buttons" type="PanelContainer" parent="UI"]
+[node name="Buttons" type="PanelContainer" parent="CombatCanvas/UI"]
 layout_mode = 0
 offset_left = 80.0
 offset_top = 376.0
 offset_right = 1200.0
 offset_bottom = 698.0
 
-[node name="GridContainer" type="GridContainer" parent="UI/Buttons"]
+[node name="GridContainer" type="GridContainer" parent="CombatCanvas/UI/Buttons"]
 layout_mode = 2
 size_flags_horizontal = 3
 size_flags_vertical = 3
 columns = 2
 
-[node name="Attack" type="Button" parent="UI/Buttons/GridContainer"]
+[node name="Attack" type="Button" parent="CombatCanvas/UI/Buttons/GridContainer"]
 layout_mode = 2
 size_flags_horizontal = 3
 size_flags_vertical = 3
 text = "Attack"
 
-[node name="Defend" type="Button" parent="UI/Buttons/GridContainer"]
+[node name="Defend" type="Button" parent="CombatCanvas/UI/Buttons/GridContainer"]
 layout_mode = 2
 size_flags_horizontal = 3
 size_flags_vertical = 3
 text = "Defend"
 
-[node name="Flee" type="Button" parent="UI/Buttons/GridContainer"]
+[node name="Flee" type="Button" parent="CombatCanvas/UI/Buttons/GridContainer"]
 layout_mode = 2
 size_flags_horizontal = 3
 size_flags_vertical = 3
 text = "Flee"
 
-[connection signal="button_up" from="UI/Buttons/GridContainer/Attack" to="UI" method="_on_Attack_button_up"]
-[connection signal="button_up" from="UI/Buttons/GridContainer/Defend" to="UI" method="_on_Defend_button_up"]
-[connection signal="button_up" from="UI/Buttons/GridContainer/Flee" to="UI" method="_on_Flee_button_up"]
+[connection signal="button_up" from="CombatCanvas/UI/Buttons/GridContainer/Attack" to="CombatCanvas/UI" method="_on_Attack_button_up"]
+[connection signal="button_up" from="CombatCanvas/UI/Buttons/GridContainer/Defend" to="CombatCanvas/UI" method="_on_Defend_button_up"]
+[connection signal="button_up" from="CombatCanvas/UI/Buttons/GridContainer/Flee" to="CombatCanvas/UI" method="_on_Flee_button_up"]

+ 3 - 1
2d/role_playing_game/combat/combatants/combatant.gd

@@ -8,6 +8,8 @@ signal turn_finished
 
 var active := false: set = set_active
 
+@onready var animation_playback: AnimationNodeStateMachinePlayback = $Sprite2D/AnimationTree.get("parameters/playback")
+
 func set_active(value: bool) -> void:
 	active = value
 	set_process(value)
@@ -35,4 +37,4 @@ func flee() -> void:
 
 func take_damage(damage_to_take: float) -> void:
 	$Health.take_damage(damage_to_take)
-	$Sprite2D/AnimationPlayer.play("take_damage")
+	animation_playback.start("take_damage")

+ 70 - 3
2d/role_playing_game/combat/combatants/sprites/sprite.tscn

@@ -1,11 +1,51 @@
-[gd_scene load_steps=6 format=3 uid="uid://pxvb8ikxb0k"]
+[gd_scene load_steps=13 format=3 uid="uid://pxvb8ikxb0k"]
 
 [ext_resource type="Texture2D" uid="uid://bxrjk2lilj53q" path="res://combat/combatants/sprites/shadow.png" id="1"]
 [ext_resource type="Texture2D" uid="uid://c82ex4vybwch5" path="res://combat/combatants/sprites/player_battle.png" id="2"]
 
+[sub_resource type="Animation" id="Animation_q241x"]
+length = 0.001
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Pivot/Body:modulate")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Color(1, 1, 1, 1)]
+}
+tracks/1/type = "value"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("Pivot/Body:position")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Vector2(0, -41)]
+}
+tracks/2/type = "value"
+tracks/2/imported = false
+tracks/2/enabled = true
+tracks/2/path = NodePath("Pivot/Body:scale")
+tracks/2/interp = 1
+tracks/2/loop_wrap = true
+tracks/2/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Vector2(1, 1)]
+}
+
 [sub_resource type="Animation" id="2"]
 resource_name = "idle"
 length = 1.5
+loop_mode = 1
 tracks/0/type = "value"
 tracks/0/imported = false
 tracks/0/enabled = true
@@ -50,17 +90,40 @@ tracks/0/keys = {
 
 [sub_resource type="AnimationLibrary" id="AnimationLibrary_cqku5"]
 _data = {
+&"RESET": SubResource("Animation_q241x"),
 &"idle": SubResource("2"),
 &"take_damage": SubResource("1")
 }
 
+[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_q241x"]
+animation = &"idle"
+
+[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_ylufg"]
+animation = &"take_damage"
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_cg2b2"]
+advance_mode = 2
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_8xcit"]
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_5sgx1"]
+switch_mode = 2
+advance_mode = 2
+
+[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_mjsen"]
+states/idle/node = SubResource("AnimationNodeAnimation_q241x")
+states/idle/position = Vector2(407.333, 120.667)
+states/take_damage/node = SubResource("AnimationNodeAnimation_ylufg")
+states/take_damage/position = Vector2(700.667, 192.667)
+transitions = ["Start", "idle", SubResource("AnimationNodeStateMachineTransition_cg2b2"), "idle", "take_damage", SubResource("AnimationNodeStateMachineTransition_8xcit"), "take_damage", "idle", SubResource("AnimationNodeStateMachineTransition_5sgx1")]
+
 [node name="Sprite2D" type="Node2D"]
 
 [node name="AnimationPlayer" type="AnimationPlayer" parent="."]
-autoplay = "idle"
 libraries = {
-"": SubResource("AnimationLibrary_cqku5")
+&"": SubResource("AnimationLibrary_cqku5")
 }
+autoplay = "idle"
 next/take_damage = &"idle"
 
 [node name="Pivot" type="Marker2D" parent="."]
@@ -73,3 +136,7 @@ texture = ExtResource("1")
 [node name="Body" type="Sprite2D" parent="Pivot"]
 position = Vector2(0, -41)
 texture = ExtResource("2")
+
+[node name="AnimationTree" type="AnimationTree" parent="."]
+tree_root = SubResource("AnimationNodeStateMachine_mjsen")
+anim_player = NodePath("../AnimationPlayer")

+ 4 - 1
2d/role_playing_game/combat/interface/ui.gd

@@ -1,5 +1,7 @@
 extends Control
 
+signal flee(winner: Combatant, loser: Combatant)
+
 
 @export var combatants_node: Node
 @export var info_scene: PackedScene
@@ -38,6 +40,7 @@ func _on_Flee_button_up() -> void:
 		return
 
 	combatants_node.get_node("Player").flee()
+
 	var loser: Combatant = combatants_node.get_node("Player")
 	var winner: Combatant = combatants_node.get_node("Opponent")
-	get_parent().finish_combat(winner, loser)
+	flee.emit(winner, loser)

+ 2 - 2
2d/role_playing_game/decoration/wind_sway.tres

@@ -53,6 +53,6 @@ shader_parameter/maxStrength = 0.01
 shader_parameter/strengthScale = 100.0
 shader_parameter/interval = 3.5
 shader_parameter/detail = 1.0
-shader_parameter/distortion = null
-shader_parameter/heightOffset = null
+shader_parameter/distortion = 0.0
+shader_parameter/heightOffset = 0.0
 shader_parameter/offset = 0.0

+ 5 - 5
2d/role_playing_game/game.gd

@@ -21,13 +21,13 @@ func _ready() -> void:
 
 
 func start_combat(combat_actors: Array[PackedScene]) -> void:
-	remove_child($Exploration)
-	$AnimationPlayer.play("fade")
+	$AnimationPlayer.play("fade_to_black")
 	await $AnimationPlayer.animation_finished
+	remove_child($Exploration)
 	add_child(combat_screen)
 	combat_screen.show()
 	combat_screen.initialize(combat_actors)
-	$AnimationPlayer.play_backwards("fade")
+	$AnimationPlayer.play_backwards("fade_to_black")
 
 
 func _on_opponent_dialogue_finished(opponent: Pawn) -> void:
@@ -40,7 +40,7 @@ func _on_opponent_dialogue_finished(opponent: Pawn) -> void:
 
 func _on_combat_finished(winner: Combatant, _loser: Combatant) -> void:
 	remove_child(combat_screen)
-	$AnimationPlayer.play_backwards("fade")
+	$AnimationPlayer.play_backwards("fade_to_black")
 	add_child(exploration_screen)
 	var dialogue: Node = load("res://dialogue/dialogue_player/dialogue_player.tscn").instantiate()
 
@@ -51,7 +51,7 @@ func _on_combat_finished(winner: Combatant, _loser: Combatant) -> void:
 
 	await $AnimationPlayer.animation_finished
 	var player: Pawn = $Exploration/Grid/Player
-	exploration_screen.get_node("DialogueUI").show_dialogue(player, dialogue)
+	exploration_screen.get_node("DialogueCanvas/DialogueUI").show_dialogue(player, dialogue)
 	combat_screen.clear_combat()
 	await dialogue.dialogue_finished
 	dialogue.queue_free()

+ 2 - 3
2d/role_playing_game/game.tscn

@@ -21,7 +21,7 @@ tracks/0/keys = {
 
 [sub_resource type="AnimationLibrary" id="AnimationLibrary_53g8u"]
 _data = {
-&"fade": SubResource("1")
+&"fade_to_black": SubResource("1")
 }
 
 [node name="Game" type="Node" node_paths=PackedStringArray("combat_screen", "exploration_screen")]
@@ -31,13 +31,12 @@ exploration_screen = NodePath("Exploration")
 
 [node name="AnimationPlayer" type="AnimationPlayer" parent="."]
 libraries = {
-"": SubResource("AnimationLibrary_53g8u")
+&"": SubResource("AnimationLibrary_53g8u")
 }
 
 [node name="Transition" type="CanvasLayer" parent="."]
 
 [node name="ColorRect" type="ColorRect" parent="Transition"]
-visible = false
 anchors_preset = 15
 anchor_right = 1.0
 anchor_bottom = 1.0

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 6 - 5
2d/role_playing_game/grid_movement/exploration.tscn


+ 7 - 5
2d/role_playing_game/grid_movement/grid/grid.gd

@@ -1,4 +1,5 @@
-extends TileMap
+class_name Grid
+extends TileMapLayer
 
 enum CellType {
 	ACTOR,
@@ -10,7 +11,7 @@ enum CellType {
 
 func _ready() -> void:
 	for child in get_children():
-		set_cell(0, local_to_map(child.position), child.type, Vector2i.ZERO)
+		set_cell(local_to_map(child.position), child.type, Vector2i.ZERO)
 
 
 func get_cell_pawn(cell: Vector2i, type: CellType = CellType.ACTOR) -> Node2D:
@@ -27,12 +28,13 @@ func request_move(pawn: Pawn, direction: Vector2i) -> Vector2i:
 	var cell_start := local_to_map(pawn.position)
 	var cell_target := cell_start + direction
 
-	var cell_tile_id := get_cell_source_id(0, cell_target)
+	var cell_tile_id := get_cell_source_id(cell_target)
 	match cell_tile_id:
 		-1:
-			set_cell(0, cell_target, CellType.ACTOR, Vector2i.ZERO)
-			set_cell(0, cell_start, -1, Vector2i.ZERO)
+			set_cell(cell_target, CellType.ACTOR, Vector2i.ZERO)
+			set_cell(cell_start, -1, Vector2i.ZERO)
 			return map_to_local(cell_target)
+
 		CellType.OBJECT, CellType.ACTOR:
 			var target_pawn := get_cell_pawn(cell_target, cell_tile_id)
 			#print("Cell %s contains %s" % [cell_target, target_pawn.name])

+ 0 - 51
2d/role_playing_game/grid_movement/pawns/actor.gd

@@ -1,51 +0,0 @@
-extends Pawn
-
-var lost = false
-@onready var Grid = get_parent()
-
-
-func _ready():
-	update_look_direction(Vector2.RIGHT)
-
-
-func _process(delta):
-	var input_direction = get_input_direction()
-	if not input_direction:
-		return
-	update_look_direction(input_direction)
-
-	var target_position = Grid.request_move(self, input_direction)
-	if target_position:
-		move_to(target_position)
-	else:
-		bump()
-
-
-func get_input_direction():
-	return Vector2(
-		Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left"),
-		Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
-	)
-
-
-func update_look_direction(direction):
-	$Pivot/Sprite2D.rotation = direction.angle()
-
-
-func move_to(target_position):
-	set_process(false)
-	$AnimationPlayer.play("walk")
-	var move_direction = (position - target_position).normalized()
-	var tween := create_tween()
-	tween.set_ease(Tween.EASE_IN)
-	tween.tween_property($Pivot, "position", $Pivot.position + move_direction * 32, $AnimationPlayer.current_animation_length)
-	$Pivot/Sprite2D.position = position - target_position
-	position = target_position
-
-	await $AnimationPlayer.animation_finished
-
-	set_process(true)
-
-
-func bump():
-	$AnimationPlayer.play("bump")

+ 0 - 1
2d/role_playing_game/grid_movement/pawns/actor.gd.uid

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

+ 22 - 0
2d/role_playing_game/grid_movement/pawns/anim_opponent.tres

@@ -0,0 +1,22 @@
+[gd_resource type="SpriteFrames" load_steps=2 format=3 uid="uid://bgwcs0i1rmrn8"]
+
+[ext_resource type="Texture2D" uid="uid://bb6xgdlxov660" path="res://grid_movement/pawns/opponent_exploration.png" id="1_wm3cl"]
+
+[resource]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("1_wm3cl")
+}],
+"loop": false,
+"name": &"bump",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("1_wm3cl")
+}],
+"loop": true,
+"name": &"idle",
+"speed": 5.0
+}]

+ 23 - 0
2d/role_playing_game/grid_movement/pawns/anim_player.tres

@@ -0,0 +1,23 @@
+[gd_resource type="SpriteFrames" load_steps=3 format=3 uid="uid://gucxux3cqds3"]
+
+[ext_resource type="Texture2D" uid="uid://c5mr2yqxvctld" path="res://grid_movement/pawns/player_exploration_bump.png" id="1_j46su"]
+[ext_resource type="Texture2D" uid="uid://c7n37h1euodch" path="res://grid_movement/pawns/player_exploration.png" id="2_jkcju"]
+
+[resource]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("1_j46su")
+}],
+"loop": false,
+"name": &"bump",
+"speed": 5.0
+}, {
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2_jkcju")
+}],
+"loop": false,
+"name": &"idle",
+"speed": 5.0
+}]

+ 15 - 55
2d/role_playing_game/grid_movement/pawns/character.tscn

@@ -1,9 +1,8 @@
-[gd_scene load_steps=21 format=3 uid="uid://bdni5iw2j108j"]
+[gd_scene load_steps=19 format=3 uid="uid://bdni5iw2j108j"]
 
 [ext_resource type="Script" uid="uid://b240sdxvva6wr" path="res://grid_movement/pawns/walker.gd" id="1"]
 [ext_resource type="Texture2D" uid="uid://ba5rklp7brg7" path="res://grid_movement/pawns/character.png" id="2"]
-[ext_resource type="Texture2D" uid="uid://c7n37h1euodch" path="res://grid_movement/pawns/player_exploration.png" id="3"]
-[ext_resource type="Texture2D" uid="uid://c5mr2yqxvctld" path="res://grid_movement/pawns/player_exploration_bump.png" id="4"]
+[ext_resource type="SpriteFrames" uid="uid://gucxux3cqds3" path="res://grid_movement/pawns/anim_player.tres" id="4_1c7h6"]
 
 [sub_resource type="Animation" id="3"]
 length = 0.001
@@ -15,7 +14,7 @@ step = 0.01
 tracks/0/type = "value"
 tracks/0/imported = false
 tracks/0/enabled = true
-tracks/0/path = NodePath("Pivot/Sprite2D:position")
+tracks/0/path = NodePath("Pivot/FacingDirection:position")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
 tracks/0/keys = {
@@ -48,18 +47,6 @@ tracks/2/keys = {
 "update": 0,
 "values": [Vector2(1, 1), Vector2(1, 1)]
 }
-tracks/3/type = "value"
-tracks/3/imported = false
-tracks/3/enabled = true
-tracks/3/path = NodePath("Pivot/Slime:texture")
-tracks/3/interp = 1
-tracks/3/loop_wrap = true
-tracks/3/keys = {
-"times": PackedFloat32Array(0, 0.24, 0.25),
-"transitions": PackedFloat32Array(1, 1, 1),
-"update": 1,
-"values": [ExtResource("4"), ExtResource("4"), ExtResource("3")]
-}
 
 [sub_resource type="Animation" id="4"]
 resource_name = "idle"
@@ -89,18 +76,6 @@ tracks/1/keys = {
 "update": 0,
 "values": [Vector2(1, 1), Vector2(1.125, 0.844), Vector2(0.906, 1.141), Vector2(1, 1)]
 }
-tracks/2/type = "value"
-tracks/2/imported = false
-tracks/2/enabled = false
-tracks/2/path = NodePath("Pivot/Slime:texture")
-tracks/2/interp = 1
-tracks/2/loop_wrap = true
-tracks/2/keys = {
-"times": PackedFloat32Array(0, 1.5),
-"transitions": PackedFloat32Array(1, 1),
-"update": 1,
-"values": [ExtResource("3"), ExtResource("3")]
-}
 
 [sub_resource type="Animation" id="2"]
 resource_name = "walk"
@@ -108,8 +83,8 @@ length = 0.25
 step = 0.05
 tracks/0/type = "value"
 tracks/0/imported = false
-tracks/0/enabled = false
-tracks/0/path = NodePath("Pivot/Sprite2D:self_modulate")
+tracks/0/enabled = true
+tracks/0/path = NodePath("Pivot/FacingDirection:self_modulate")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
 tracks/0/keys = {
@@ -121,7 +96,7 @@ tracks/0/keys = {
 tracks/1/type = "value"
 tracks/1/imported = false
 tracks/1/enabled = true
-tracks/1/path = NodePath("Pivot/Sprite2D:position")
+tracks/1/path = NodePath("Pivot/FacingDirection:position")
 tracks/1/interp = 1
 tracks/1/loop_wrap = true
 tracks/1/keys = {
@@ -133,7 +108,7 @@ tracks/1/keys = {
 tracks/2/type = "value"
 tracks/2/imported = false
 tracks/2/enabled = true
-tracks/2/path = NodePath("Pivot/Sprite2D:scale")
+tracks/2/path = NodePath("Pivot/FacingDirection:scale")
 tracks/2/interp = 1
 tracks/2/loop_wrap = true
 tracks/2/keys = {
@@ -166,18 +141,6 @@ tracks/4/keys = {
 "update": 0,
 "values": [Vector2(1, 1), Vector2(1.2, 0.917), Vector2(0.917, 1.135), Vector2(1, 1)]
 }
-tracks/5/type = "value"
-tracks/5/imported = false
-tracks/5/enabled = false
-tracks/5/path = NodePath("Pivot/Slime:texture")
-tracks/5/interp = 1
-tracks/5/loop_wrap = true
-tracks/5/keys = {
-"times": PackedFloat32Array(0, 0.25),
-"transitions": PackedFloat32Array(1, 1),
-"update": 1,
-"values": [ExtResource("3"), ExtResource("3")]
-}
 
 [sub_resource type="AnimationLibrary" id="AnimationLibrary_yxglf"]
 _data = {
@@ -222,28 +185,27 @@ states/walk/position = Vector2(310, 116)
 transitions = ["walk", "idle", SubResource("11"), "bump", "idle", SubResource("12"), "idle", "walk", SubResource("13"), "idle", "bump", SubResource("14"), "Start", "idle", SubResource("AnimationNodeStateMachineTransition_ed7tp"), "idle", "End", SubResource("AnimationNodeStateMachineTransition_j3wgi")]
 graph_offset = Vector2(-609, -158)
 
-[sub_resource type="AnimationNodeStateMachinePlayback" id="6"]
-
 [node name="Character" type="Node2D"]
 z_index = 1
-position = Vector2(32, 32)
 script = ExtResource("1")
 
 [node name="AnimationPlayer" type="AnimationPlayer" parent="."]
 libraries = {
-"": SubResource("AnimationLibrary_yxglf")
+&"": SubResource("AnimationLibrary_yxglf")
 }
 
 [node name="Pivot" type="Marker2D" parent="."]
 
-[node name="Slime" type="Sprite2D" parent="Pivot"]
-position = Vector2(0, -4.37912)
-scale = Vector2(0.908741, 1.13728)
-texture = ExtResource("3")
+[node name="Slime" type="AnimatedSprite2D" parent="Pivot"]
+position = Vector2(0, -3.21718)
+scale = Vector2(0.932767, 1.10085)
+sprite_frames = ExtResource("4_1c7h6")
+animation = &"idle"
 centered = false
 offset = Vector2(-32, -32)
 
-[node name="Sprite2D" type="Sprite2D" parent="Pivot"]
+[node name="FacingDirection" type="Sprite2D" parent="Pivot"]
+editor_description = "Enable this node to see the character's facing direction."
 visible = false
 self_modulate = Color(0, 0, 0, 1)
 scale = Vector2(1e-05, 1e-05)
@@ -254,5 +216,3 @@ offset = Vector2(-32, -32)
 [node name="AnimationTree" type="AnimationTree" parent="."]
 tree_root = SubResource("5")
 anim_player = NodePath("../AnimationPlayer")
-active = true
-parameters/playback = SubResource("6")

+ 2 - 4
2d/role_playing_game/grid_movement/pawns/opponent.gd

@@ -1,7 +1,5 @@
-extends Pawn
-
-@export var combat_actor: PackedScene
-var lost := false
+extends Walker
 
 func _ready() -> void:
+	super._ready()
 	set_process(false)

+ 21 - 0
2d/role_playing_game/grid_movement/pawns/player.gd

@@ -0,0 +1,21 @@
+extends Walker
+
+func _process(_delta: float) -> void:
+	var input_direction := get_input_direction()
+	# We only move in integer increments.
+	input_direction = input_direction.round()
+
+	if input_direction.is_zero_approx():
+		return
+
+	update_look_direction(input_direction)
+
+	var target_position: Vector2 = grid.request_move(self, input_direction)
+	if target_position:
+		move_to(target_position)
+	elif active:
+		bump()
+
+
+func get_input_direction() -> Vector2:
+	return Input.get_vector("move_left", "move_right", "move_up", "move_down")

+ 1 - 0
2d/role_playing_game/grid_movement/pawns/player.gd.uid

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

+ 12 - 24
2d/role_playing_game/grid_movement/pawns/walker.gd

@@ -1,48 +1,33 @@
+## A pawn that can animate and walk around the grid.
+class_name Walker
 extends Pawn
 
 @export var combat_actor: PackedScene
+@export var pose_anims: SpriteFrames
 
 var lost := false
 var grid_size: float
 
-@onready var parent := get_parent()
+@onready var grid : Grid = get_parent()
 @onready var animation_playback: AnimationNodeStateMachinePlayback = $AnimationTree.get("parameters/playback")
 @onready var walk_animation_time: float = $AnimationPlayer.get_animation("walk").length
+@onready var pose := $Pivot/Slime
 
 
 func _ready() -> void:
+	pose.sprite_frames = pose_anims
 	update_look_direction(Vector2.RIGHT)
-	grid_size = parent.tile_set.tile_size.x
-
-
-func _process(_delta: float) -> void:
-	var input_direction := get_input_direction()
-	if input_direction.is_zero_approx():
-		return
-
-	update_look_direction(input_direction)
-
-	var target_position: Vector2 = parent.request_move(self, input_direction)
-	if target_position:
-		move_to(target_position)
-	elif active:
-		bump()
-
-
-func get_input_direction() -> Vector2:
-	return Vector2(
-			Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
-			Input.get_action_strength("move_down") - Input.get_action_strength("move_up")
-	)
+	grid_size = grid.tile_set.tile_size.x
 
 
 func update_look_direction(direction: Vector2) -> void:
-	$Pivot/Sprite2D.rotation = direction.angle()
+	$Pivot/FacingDirection.rotation = direction.angle()
 
 
 func move_to(target_position: Vector2) -> void:
 	set_process(false)
 	var move_direction := (target_position - position).normalized()
+	pose.play("idle")
 	animation_playback.start("walk")
 
 	var tween := create_tween()
@@ -54,13 +39,16 @@ func move_to(target_position: Vector2) -> void:
 	$Pivot.position = Vector2.ZERO
 	position = target_position
 	animation_playback.start("idle")
+	pose.play("idle")
 
 	set_process(true)
 
 
 func bump() -> void:
 	set_process(false)
+	pose.play("bump")
 	animation_playback.start("bump")
 	await $AnimationTree.animation_finished
 	animation_playback.start("idle")
+	pose.play("idle")
 	set_process(true)

+ 8 - 0
2d/role_playing_game/project.godot

@@ -36,6 +36,14 @@ import/blender/enabled=false
 
 [input]
 
+ui_accept={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":true,"script":null)
+]
+}
 move_up={
 "deadzone": 0.2,
 "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)

Vissa filer visades inte eftersom för många filer har ändrats