godot

How to change the texture of AnimatedSprite programatically


I have created a base scene that I intend to use for all human characters of my game. I am using an AnimatedSprite where I defined different animations for the different positions of the character, all using a texture that contains all the frames.

This works for a specific character, but now I would like to create other characters. Since I am using a character generator, all the sprite sheets are basically the same, but with different clothes, accessories, etc. I would like to avoid replicating the animation definitions for the other characters. I could achieve that by setting a different texture on each instance of the scene, but I can't find a way to do it.

If I edit the tscn file and set a different image, it does what I want.

I tried updating the atlas property of the animation frames, but doing that affects all instances of the scene:

func update_texture(value: Texture):
    for animation in $AnimatedSprite.frames.animations:
        for frame in animation.frames:
            frame.atlas = value

I also tried cloning a SpriteFrames instance, by calling duplicate(0), updating it with the above code, then setting $AnimatedSprite.frames, but this also updates all instances of the scene.

What is the proper way to change the texture of a specific instance of AnimatedSprite?


Solution

  • I found a solution. The problem was that the duplicate method does not perform a deep clone, so I was having references to the same frame instances. Here's my updated version:

    func update_texture(texture: Texture):
        var reference_frames: SpriteFrames = $AnimatedSprite.frames
        var updated_frames = SpriteFrames.new()
        
        for animation in reference_frames.get_animation_names():
            if animation != "default":
                updated_frames.add_animation(animation)
                updated_frames.set_animation_speed(animation, reference_frames.get_animation_speed(animation))
                updated_frames.set_animation_loop(animation, reference_frames.get_animation_loop(animation))
                
                for i in reference_frames.get_frame_count(animation):
                    var updated_texture: AtlasTexture = reference_frames.get_frame(animation, i).duplicate()
                    updated_texture.atlas = texture
                    updated_frames.add_frame(animation, updated_texture)
    
        updated_frames.remove_animation("default")
    
        $AnimatedSprite.frames = updated_frames