Im working on a wolfenstein3d clone and i got recommended to do this in another post, but im confused and im not sure how i use the frame_changed signal to tell the weapons when to refire the weapon

code:

extends CanvasLayer

# Called when the node enters the scene tree for the first time.
func _ready():
	$AnimatedSprite2D.animation_finished.connect(_on_AnimatedSprite2d_animation_finished)
	$AnimatedSprite2D.play(Global.current_weapon + "_Idle")

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):

	if Global.current_weapon != "Knife" and Global.ammo <= 0:
		Global.current_weapon = "Knife"
		$AnimatedSprite2D.play("knife_idle")
		
	if Input.is_action_pressed("main_fire_weapon"):
		if Global.current_weapon == "Knife":
			$AnimatedSprite2D.play("Knife_Stab")
		else:
			$AnimatedSprite2D.play(Global.current_weapon + "_Fire")
		
		if Global.current_weapon != "Knife":
			if Global.ammo > 0:
				Global.ammo -= 1

func _on_AnimatedSprite2d_animation_finished():
	$AnimatedSprite2D.play(Global.current_weapon + "_Idle")


func _on_animated_sprite_2d_frame_changed():
	pass # Replace with function body.
  • xyz replied to this.

    JetteBoyTV You should have posted in the same thread to keep the context.
    Anyway, make fire() function that does firing and call it from _on_animated_sprite_2d_frame_changed() if the current animation frame is the frame you want to fire on.

      xyz offtopic... i have not researched this but is it possible to do something similar in 3d animations? like i have animation where lets say i swing a sword and at the mid swing it should let out a visual sprite or something to notify user that a spell or swing is done and damage is about to take place once that shock wave reaches someone?

      I remember playing around frames for animations and i know you can call animation and can connect animation end/played signal, but what if you need it to be mid animation? a sort of mid way signal?

      • xyz replied to this.

        kuligs2 What type of animation? With the AnimationPlayer the easiest solution would be to use a call method track. You simply add keyframes that call methods.

          kuligs2 You can always make an inherited scene from the asset and add it. Or use a new animation player node to handle it.

          xyz how do i put in the specific animation frame i want it to fire on when i call fire() in _on_animated_sprite_2d_frame_changed()

          • xyz replied to this.

            JetteBoyTV Look at the AnimatedSprite2D class reference. You'll see it has the frame property which contains the current animation frame number. Query this property in the signal handler and if it's the right frame number - call fire()

              xyz
              im running into problems, got a error on line 27 "Invalid call to function 'is_playing' in base 'AnimatedSprite2D'. Expected 0 arguments", plus im noticing problems with my code that im unsure how to fix.

              code:

              extends CanvasLayer
              
              # Called when the node enters the scene tree for the first time.
              func _ready():
              	$AnimatedSprite2D.animation_finished.connect(_on_AnimatedSprite2d_animation_finished)
              	$AnimatedSprite2D.play(Global.current_weapon + "_Idle")
              
              # Called every frame. 'delta' is the elapsed time since the previous frame.
              func _process(delta):
              
              	if Global.current_weapon != "Knife" and Global.ammo <= 0:
              		Global.current_weapon = "Knife"
              		$AnimatedSprite2D.play("knife_idle")
              		
              	if Input.is_action_pressed("main_fire_weapon"):
              		if Global.current_weapon == "Knife":
              			$AnimatedSprite2D.play("Knife_Stab")
              		else:
              			_on_animated_sprite_2d_frame_changed()
              
              func _on_AnimatedSprite2d_animation_finished():
              	$AnimatedSprite2D.play(Global.current_weapon + "_Idle")
              
              
              func _on_animated_sprite_2d_frame_changed():
              	fire()
              	if $AnimatedSprite2D.is_playing("Chain_Fire"):
              		if $AnimatedSprite2D.frame == 1 or 2:
              			fire()
              	if $AnimatedSprite2D.is_playing("MG_Fire"): 
              		if $AnimatedSprite2D.frame == 1:
              			fire()
              	if $AnimatedSprite2D.is_playing("Gun_Fire"):
              		if $AnimatedSprite2D.frame == 1:
              			fire()
              
              func fire():
              	if Global.current_weapon == "Knife":
              		$AnimatedSprite2D.play("Knife_Stab")
              	else:
              		$AnimatedSprite2D.play(Global.current_weapon + "_Fire")
              		
              	if Global.current_weapon != "Knife":
              		if Global.ammo > 0:
              			Global.ammo -= 1
              • xyz replied to this.

                JetteBoyTV im running into problems, got a error on line 27 "Invalid call to function 'is_playing' in base 'AnimatedSprite2D'. Expected 0 arguments"

                You need to learn to read (and understand) node class reference in the docs. AnimatedSprite2D::is_playing() only checks if any animation is currently playing, hence it doesn't take an animation name as an argument. To check which animation is current, use the animation property

                JetteBoyTV plus im noticing problems with my code that im unsure how to fix.

                At least communicate what those problems are. Saying you're noticing problems but not saying what they are or how they manifest is not really helpful to people who might help you resolve those problems.

                Btw if you want to check for multiple frames, this won't do it:

                if $AnimatedSprite2D.frame == 1 or 2:

                because the result of logical or operation in 1 or 2 will always be logical true. This true value is cast to integer value 1 when used in a comparison expression with another integer value. So your statement is equivalent to:

                if $AnimatedSprite2D.frame == 1:

                  xyz I applied the corrections, also the problems that i didnt really communicate what they were turned out to not be a problem, but now the animation just loops forever
                  code:

                  extends CanvasLayer
                  
                  # Called when the node enters the scene tree for the first time.
                  func _ready():
                  	$AnimatedSprite2D.animation_finished.connect(_on_AnimatedSprite2d_animation_finished)
                  	$AnimatedSprite2D.play(Global.current_weapon + "_Idle")
                  
                  # Called every frame. 'delta' is the elapsed time since the previous frame.
                  func _process(delta):
                  
                  	if Global.current_weapon != "Knife" and Global.ammo <= 0:
                  		Global.current_weapon = "Knife"
                  		$AnimatedSprite2D.play("knife_idle")
                  		
                  	if Input.is_action_pressed("main_fire_weapon"):
                  		if Global.current_weapon == "Knife":
                  			$AnimatedSprite2D.play("Knife_Stab")
                  		else:
                  			_on_animated_sprite_2d_frame_changed()
                  
                  func _on_AnimatedSprite2d_animation_finished():
                  	$AnimatedSprite2D.play(Global.current_weapon + "_Idle")
                  
                  
                  func _on_animated_sprite_2d_frame_changed():
                  	if Global.current_weapon == "Knife":
                  		$AnimatedSprite2D.play("Knife_Stab")
                  	else:
                  		$AnimatedSprite2D.play(Global.current_weapon + "_Fire")
                  		
                  	if $AnimatedSprite2D.animation == ("Chain_Fire"):
                  		if $AnimatedSprite2D.frame == 1:
                  			fire()
                  		if $AnimatedSprite2D.frame == 2:
                  			fire()
                  	if $AnimatedSprite2D.animation == ("MG_Fire"): 
                  		if $AnimatedSprite2D.frame == 1:
                  			fire()
                  	if $AnimatedSprite2D.animation == ("Gun_Fire"):
                  		if $AnimatedSprite2D.frame == 1:
                  			fire()
                  
                  func fire():
                  	if Global.current_weapon == "Knife":
                  		$AnimatedSprite2D.play("Knife_Stab")
                  	else:
                  		$AnimatedSprite2D.play(Global.current_weapon + "_Fire")
                  		
                  	if Global.current_weapon != "Knife":
                  		if Global.ammo > 0:
                  			Global.ammo -= 1
                  • xyz replied to this.

                    JetteBoyTV the animation just loops forever and the animation doesnt loop

                    It loops and it doesn't loop? That doesn't make any sense.

                      xyz i meant like i dont have loop turned on in the spriteframes editor, but it loops anyway for some reason (i should probably proof read shit before i send it)

                      • xyz replied to this.

                        JetteBoyTV Probably because you call play() again every time an animation is finished.

                          xyz im confused, i dont see anywhere in my code where that happens, and also after some testing, this whole method of delaying when the weapon fires isnt even working to delay the weapon

                          • xyz replied to this.

                            JetteBoyTV Can you clearly describe in detail the exact behavior you want to implement? I thought the question was how to fire a bullet on the specific frames of an animation.

                              xyz i thought this whole thing would delay the shots while holding down the fire button, but its not delaying the shots, plus its looping when its not supposed to be looping

                              • xyz replied to this.

                                JetteBoyTV Again, try to clearly and in detail describe what type of behavior you want to achieve. You're not being clear enough, nor elaborate enough. That's why the thread drags for this long. Put some effort into a precise description and we'll solve this in a minute.

                                  xyz in a earlier post you suggested me to do this whole sprite signal thing to have a frame cooldown that uses frames, but its not working as a firing cooldown, and its looping the firing

                                  • xyz replied to this.

                                    JetteBoyTV No i suggested is as an answer to your specific question which was this:

                                    the fire rate needs to be synced with the animation, and the animation is a minigun spinning, so if its not synced, it looks like the minigun suddenly stopped spinning, also in the animation it looks like the minigun fires two times in the two firing frames (each frame takes two 35fps frames)

                                    and this:

                                    with the sprite signal thing, what do i do if the animation has the gun refining two times in the animation?

                                    And I suggested other approaches as well.

                                    Why do you insist on linking the cooldown to animation frames? Just time it. There doesn't need to be any actual "cooldowns". It's just a timed fire rate that triggers the animation. There's no need to count time from the end of the current animation to the beginning of the next. Instead, count time between two consecutive beginnings. That way the interval won't be too short for a timer to handle it at 60fps processing. As I said earlier, trigger the beginning of the animation using a timer or delta counting. Not the other way around.

                                    If you want to do it using sprite animation frames, simply pad the ends of your firing animations with identical frames in the duration of the wanted cooldown for a specific weapon so that the total animation duration includes the cooldown time, and just play the looped animation while the button is pressed. Fire the actual bullet on specific frame(s) using the frame changed signal. That way you can fire either once in the duration of the animation cycle, or multiple times in the case of minigun.

                                    This is a trivial matter once you decide (and clearly communicate) how the system is supposed to behave.