• RuntimesGodot
  • Two tracks - reset second without reseting state

I working on Godot Spine 4.2 - 4.2.2

I try to use animations on tracks, because i have some movement animations like run, idle or jump. Also I have weapon animations. I want to shoot during running or idling so I put shoot animation on second track. The problem is when shoot animation is ending. Shoot animation use some parts od body, like eyes, hands and others. So when second track used this and animation finished, the first track can use that anymore if i wont empty second track.
Ok, So i did that, but this made it more weird. For example when i shoot then jump, the empty process make jumping animation back and start again (in the middle of animation).

What is the best way to make it working properly?

  • Misaki hat auf diesen Beitrag geantwortet.
    Related Discussions
    ...

    To achieve the desired behavior of playing a weapon animation on a second track without disrupting the main movement animation on the first track, you can use empty animations to smoothly transition back to the main animation state. However, you need to ensure that the empty animation does not interfere with the main track's animation. Here is a step-by-step approach to handle this:

    1. Set the main animation on the first track:

         var animation_state = get_animation_state()
         animation_state.set_animation("run", true, 0)
    2. Add the shoot animation on the second track:

         animation_state.set_animation("shoot", false, 1)
    3. Queue an empty animation on the second track to smoothly transition back:

         animation_state.add_empty_animation(1, 0.5, 0)
    4. Ensure the empty animation does not reset the main track's animation:

      • When the shoot animation ends, the empty animation will mix out the shoot animation smoothly over 0.5 seconds.
      • This should not affect the main track's animation, as the empty animation only affects the second track.

    Here is a complete example in GDScript:

    extends SpineSprite
    
    func _ready():
        var animation_state = get_animation_state()
        # Set the main animation on track 0
        animation_state.set_animation("run", true, 0)
        
        # Add the shoot animation on track 1
        animation_state.set_animation("shoot", false, 1)
        
        # Queue an empty animation on track 1 to smoothly transition back
        animation_state.add_empty_animation(1, 0.5, 0)

    This approach ensures that the main animation on track 0 continues uninterrupted while the shoot animation on track 1 plays and then smoothly transitions back to the main animation state.

    If you encounter issues with the main animation restarting or jumping, ensure that the empty animation duration and the mix duration are set correctly to avoid abrupt changes.

    • Kandif hat auf diesen Beitrag geantwortet.

      Spinebot
      I typed exactly the same code and I also explained what visual glitch it created.
      The problem is that the shoot animation uses the same parts as the movements animations. So if I shoot and then for example jump, when empty animation active it make animation jump back and start again.

      Kandif Hmm, sounds really strange. To clarify, could you please show us the script you actually wrote?

      • Kandif hat auf diesen Beitrag geantwortet.

        Misaki Its hard at this moment to show all the code, because I have splitted but i try to change animation in one script:

        func animate_chicken(velocity):
        	match(status.gameplay):
        		status.IN_GAME_CURSOR:
        			status.animation = status.IDLE
        			if get_name_animation(0) != "Sigth":
        				play("Sigth",true,0)
        		status.IN_GAME:
        			var act_vel = velocity
        			act_vel.y = 0
        			if act_vel == Vector3.ZERO and ray1.is_colliding() and ray2.is_colliding():	
        				if status.animation != status.CROUCH:
        					status.animation = status.CROUCH
        					current_animation = play("Sigth",true,0)
        			elif !chicken.is_on_floor():
        				if status.animation != status.JUMP :
        					status.animation = status.JUMP
        					current_animation = play("jump",true,0)
        					if movement.mouse_side==1 and chicken.velocity.x>0 || movement.mouse_side==-1 and chicken.velocity.x<0:
        						current_animation.set_reverse(false)
        					else:
        						current_animation.set_reverse(true)
        			elif spine.preview_animation == "shot":
        				pass
        			elif act_vel != Vector3.ZERO && status.animation != status.RUN:
        				if status.animation == status.JUMP:
        					current_animation.set_time_scale(2)
        				else:
        					status.animation = status.RUN
        					current_animation = play("run",true,0)
        					set_time_scale(1)
        					var dire = -1 if chicken.velocity.x<0 else 1
        					set_animation_direction(dire,movement.mouse_side)
        			elif act_vel == Vector3.ZERO && status.animation != status.IDLE:
        				status.animation = status.IDLE
        				play("idle",true,0)

        Its how i change the animations. This how function play() works:

        func play(animation_name:String,looping:bool,track:int):
        	var animation = spine.get_animation_state().set_animation(animation_name,looping,track)
        	if !looping:
        		spine.get_animation_state().add_empty_animation(track,0.2,0)
        	return animation

        And in another script i just run shoot animation:

        animation.play("shot",false,1)

        I spent so much time to find the glitch. Months ago i create for test signal and did sth stupid:

        func _on_spine_sprite_animation_completed(spine_sprite, animation_state:SpineAnimationState, track_entry:SpineTrackEntry):
        	#if status.animation == JUMP:
        		#status.animation = ANY

        It make after finish any animation during jump changing status to ANY... It make play jump again...

        • Misaki hat auf diesen Beitrag geantwortet.

          Kandif Thank you for showing us the code. And it is good to know that the problem was the code you wrote for testing!