Repository: godotengine/godot (language: cpp)

Issue:
BlendSpace2D Sync does not work with `BlendModeDiscrete`
### Tested versions

Reproducible in 4.3 stable.

### System information

Godot v4.3.stable.mono - Windows 10.0.22631 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 2070 SUPER (NVIDIA; 31.0.15.5222) - AMD Ryzen 5 5600X 6-Core Processor (12 Threads)

### Issue description

The sync option should play the animation starting from the last frame of the previous animation. This works correctly when using the continuous blend mode, but not when using discrete. This is readily apparent when using pixel art sprites that face the cardinal directions. 

Using the BlendSpace2D in discrete mode to transition between run animations based on velocity, where continuous mode is inappropriate but maintaining timing between footsteps played on the animation, or attack frames when turning during an attack, is important. [There seem to be other users affected by this as well](https://www.reddit.com/r/godot/comments/1ecl1fk/is_the_blendspace2d_sync_option_broken/).

![Image](https://github.com/user-attachments/assets/e014d110-4bb9-465e-8e27-410ad9bf17b3)

### Steps to reproduce 

The minimal example project has a player that can move using WASD. They use their velocity and last facing direction to determine if they are in the idle or walking animation, and in which direction by using a BlendSpace2D. All of the walk animations have the same number of frames, and the walk animation on the first frame plays a footstep sound effect. The BlendSpace2D is in Sync and Discrete modes. 

Notice when changing direction while still moving the sound effect is played rapidly, indicating it is playing the first frame of the animation immediately always, despite the fact that Sync is on. Now switch to Continuous mode and perform the same task, noting that the sound animation is not played immediately on changing directions but at the expected regular interval, indicating that Sync is working correctly.

I suspect the issue occurs for BlendSpace1D as well from limited testing.

### Minimal reproduction project (MRP)

[blendspace2dbug.zip](https://github.com/user-attachments/files/18008918/blendspace2dbug.zip)



Discussion / hints:

Fix patch:
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index ff19eb8c12e5..03e6c4cff51e 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -374,21 +374,20 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
 		}
 
 		if (new_closest != cur_closest && new_closest != -1) {
-			NodeTimeInfo from;
 			if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
-				//for ping-pong loop
+				NodeTimeInfo from;
+				// For ping-pong loop.
 				Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
 				Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
 				if (!na_c.is_null() && !na_n.is_null()) {
 					na_n->set_backward(na_c->is_backward());
 				}
-				//see how much animation remains
+				// See how much animation remains.
 				pi.seeked = false;
 				pi.weight = 0;
-				from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
+				from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true);
+				pi.time = from.position;
 			}
-
-			pi.time = from.position;
 			pi.seeked = true;
 			pi.weight = 1.0;
 			mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index 0a6546f7da64..7863902029b0 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -551,21 +551,20 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM
 		}
 
 		if (new_closest != cur_closest && new_closest != -1) {
-			NodeTimeInfo from;
 			if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
-				//for ping-pong loop
+				NodeTimeInfo from;
+				// For ping-pong loop.
 				Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
 				Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
 				if (!na_c.is_null() && !na_n.is_null()) {
 					na_n->set_backward(na_c->is_backward());
 				}
-				//see how much animation remains
+				// See how much animation remains.
 				pi.seeked = false;
 				pi.weight = 0;
-				from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
+				from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true);
+				pi.time = from.position;
 			}
-
-			pi.time = from.position;
 			pi.seeked = true;
 			pi.weight = 1.0;
 			mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);


Test patch: