Search code examples
signalsgodotgdscriptgodot4

Not able to add multiple signals on my animators "animation_finished()" signal without game breaking


Ok so I have an animator that plays a transition screen everytime my player goes to a different room. In this main room I have 4 Area2Ds that go to different scenes. For the animation change and transition I need to add a signal to the Area2D. However, if I add all four Area2Ds to the animation player's signal, the player teleports to every room till the last one connected. I was wondering if there was a work around for this or am I using the signals wrong.

code for changing rooms for Area2D:

extends Area2D

var entered = false
@export var room = ""
var animation_playing = false
@export var xPosition = 0
@export var yPosition = 0
var player

func _on_body_entered(body: PhysicsBody2D):
    entered = true
    player = body
    
func _on_body_exited(body):
    entered = false

func _process(delta):
    if entered == true and animation_playing == false:
        var scene = get_parent()
        if scene.has_method("playFadeOutTransition"):
            animation_playing = true
            scene.playFadeOutTransition()

func _on_transition_animation_finished(anim_name):
    if anim_name == "fade_out":
        var autoload = get_node("/root/Global")
        autoload.playerStartPosition = Vector2(xPosition, yPosition)
        get_tree().change_scene_to_file(room);
    animation_playing = false

main scene script that is incharge of transitions:

extends Node2D

@export var fadeIn = false

func _ready():
    $Transition/ColorRect.visible = false
    if fadeIn == true:
        $Transition/ColorRect.visible = true
        playFadeInTransition()

func playFadeOutTransition():
    $Transition.play("fade_out")
    
func playFadeInTransition():
    $Transition.play("fade_in")

func _on_transition_animation_finished(_anim_name):
    $Transition/ColorRect.visible = false

I tried using an autoload for saving the room I need to switch to, however, it gets overwritten every time since every area2d will be called when the animation finishes. I'm not sure what else to do.


Solution

  • All of your areas are connected to the same signal, waiting for the same animation to end. So every function will be called if that animation finishes.

    There are two ways to fix that.

    1. Connect the signals from code when the player is entering the are
    2. Make a global variable your areas can check to see which one should handle the animation

    For the first option you have to remove the signals in the areas I assume you connected through the editor. The easiest way to do that would be by implementing a global event bus, but for the sake of text I will do it by passing the animation player directly to the area.

    The code would look like this:

    extends Area2D
    
    var entered = false
    @export var room = ""
    var animation_playing = false
    @export var xPosition = 0
    @export var yPosition = 0
    var player
    
    var transition_animation_player : AnimationPlayer = null
    # Called when the node enters the scene tree for the first time.
    func _ready():
        transition_animation_player = get_parent().get_node("Transistion")
    
    func _on_body_entered(body: PhysicsBody2D):
        entered = true
        player = body
        
    func _on_body_exited(body):
        entered = false
    
    func _process(delta):
        if entered == true and animation_playing == false:
            if transition_animation_player != null:
                transition_animation_player.animation_finished.connect(_on_transition_animation_finished) # connect on play
                animation_playing = true
                transition_animation_player.play("fade_out")
                
    
    func _on_transition_animation_finished(anim_name):
        if anim_name == "fade_out":
            transition_animation_player.animation_finished.disconnect(_on_transition_animation_finished) #disconnect signal when finished
            var autoload = get_node("/root/Global")
            autoload.playerStartPosition = Vector2(xPosition, yPosition)
            get_tree().change_scene_to_file(room);
        animation_playing = false
    

    Here I get the animation_player on_ready and connect the signals only if needed. The problem here is that it only works as long as only one signal is waiting for the fade_out animation.

    The second option is to set a global variable. For example the room to enter. You already got a global script so lets say it contains a variable like this:

    var room_to_enter = null
    

    We leave the signals conntected as they are and set the value, when entering an area:

    func _on_body_entered(body: PhysicsBody2D): entered = true var autoload = get_node("/root/Global") autoload.room_to_enter = room player = body

    The process of triggering the animation stays the same, but now we have a second statement to check, if the animation finishes:

    func _on_transition_animation_finished(anim_name):
        var autoload = get_node("/root/Global")
        if autoload.room_to_enter == room: #just do stuff if it's our room
            if anim_name == "fade_out":       
                autoload.playerStartPosition = Vector2(xPosition, yPosition)
                get_tree().change_scene_to_file(room);
            animation_playing = false