Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add layers to Sprite2D for easier animation #4370

Open
MythicPalette opened this issue Apr 9, 2022 · 9 comments
Open

Add layers to Sprite2D for easier animation #4370

MythicPalette opened this issue Apr 9, 2022 · 9 comments

Comments

@MythicPalette
Copy link

MythicPalette commented Apr 9, 2022

Describe the project you are working on

So, I was working on a game with 2D Sprites, something like this.
image

This "actor" scene has 19 sprites, all of which use the exact same key frames for every sprite. That means I'm setting 19 sprites to the exact same thing for every frame of every animation I make every time. So my suggestion is a LayeredSprite node. Instead of "Texture" and "Normal" have a "Layers" integer that decides the size of a "Textures" array and a "Normals" array and you can dump all the sprite sheets you want into this array in the order they are drawn (top drawn first to match Scene order).

The key feature of this LayeredSprite is that it everything is identical on all layers (except texture and normals, obviously) If you change VFrames, every layer will use the same. If you change the Frame from 0 to 1, it changes the frame on all layers. It would allow working with sprite sheets like this

Female001_No hair shadow-01

which has a matching sheet for hair, eyes, clothes, etc so much more intuitive. I'm doing 19 times the work than I would if this LayeredSprite node existed.

Describe the problem or limitation you are having in your project

The problem is that I have to animate 19 sprites with the exact same information every frame for every animation and it adds up.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

By adding this simple LayeredSprite, anyone that uses layered sprite sheets like what I have and what you find all over the internet would be able to animate all of it with exponentially less work. The most tedious part of animating in my game is having to go "Ok, next frame. Shadow, frame 0. Ear, 0, headaccessory, 0..." through all 19 of them just to then turn around and do it again for frame 1.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

It should be simple enough to implement I would think. I actually made my own version of it as a Scene object. Perhaps this code could be useful to derive from in some way. I'm sure my design is far from optimal but hopefully it serves to demonstrate my idea.

    [Tool]
    public class LayeredSprite : Sprite
    {
        [Export] internal Texture[] Layers { get; set; } = new Texture[0];

        public override void _Process( float delta )
        {
            base._Process( delta );
            // Get the length of the array compared to the number of children
            if ( this.Layers is null )
                return;

            // If the count is wrong, clear all layers to rebuild.
            if ( this.Layers.Length < this.GetChildCount() )
                this.Clear();

            // While we have too few layers, add sprites
            int i = 0;
            while ( this.Layers.Length > this.GetChildCount() )
                this.AddChild( new Sprite() { Name = $"Layer{i++}", Position = new( 0, 0 ) } );

            // Assign texture to each sprite
            UpdateSprites();
        }

        internal void UpdateSprites()
        {
            for ( int x = 0; x < this.Layers.Length; ++x )
            {
                Sprite s = (Sprite)this.GetChild( x );
                s.Texture = this.Layers[x];
                s.Centered = this.Centered;
                s.Offset = this.Offset;
                s.FlipH = this.FlipH;
                s.FlipV = this.FlipV;
                s.Hframes = this.HFrames;
                s.Vframes = this.VFrames;
                s.Frame = this.Frame;
                s.RegionEnabled = this.RegionEnabled;
                s.RegionRect = this.Region;
                s.RegionFilterClip = this.FilterClip;
            }
        }

        internal void Clear()
        {
            foreach ( Node child in this.GetChildren() )
                this.RemoveChild( child );
        }
    }

If this enhancement will not be used often, can it be worked around with a few lines of script?

Well, that's up to you to decide.

Is there a reason why this should be core and not an add-on in the asset library?

I feel like it's a simple enough quality of life thing that having it there would improve things for everyone that could use it and it wouldn't affect anyone that didn't. I would think it would be

@KoBeWi KoBeWi added the topic:2d label Apr 9, 2022
@KoBeWi
Copy link
Member

KoBeWi commented Apr 9, 2022

If all your sprites have the same size, you can just set the texture from code or even as part of the animation.

@MythicPalette
Copy link
Author

If all your sprites have the same size, you can just set the texture from code or even as part of the animation.

The goal of my layered sprite isn't to change the textures it's to be able to synchronize multiple sprites. I turned this

image

Into this
image

@MythicPalette
Copy link
Author

MythicPalette commented Apr 9, 2022

See, I have layered sprite sheets. One for skin color, four different ones for hair, then there is clothing, etc. They all have exactly the same frames and they all have to move together at exactly the same time always. Doing this manually, I made several mistakes and would accidentally set one thing one frame off on one animation and have to go back and fix it, not to mention, putting the same Frame and Position on every single sprite. The first one, You can see (for example) Hair goes Frame 0 -> Frame 1 -> Frame 2 -> Frame 1 Then I had to do that for body Frame 0 -> Frame 1 -> Frame 2 -> Frame 1.... 19 different times for every frame of every animation. thats sprites*(frames* animations) (s*(f*a)) amounts of work. Now, I put all of the sprite textures into an array like this
image

And it animates all of them at once. No need for manually doing it all. They all have to move exactly the same anyway.

@MythicPalette
Copy link
Author

In my case the amount of work involved before making this LayeredSprite of mine was 19 * ( 4 * 16 ) or 1,216 keys entered manually. With my layered sprite it became 64 keys.

@Calinou
Copy link
Member

Calinou commented Apr 9, 2022

I remember seeing a very similar proposal about this, but I can't find it right now.

@Calinou Calinou changed the title Layered Sprite Add layers to Sprite2D for easier animation Apr 9, 2022
@KoBeWi
Copy link
Member

KoBeWi commented Apr 9, 2022

The proposal makes more sense now that you better explained your use-case.

btw the script can be simpler if you inherit Sprite instead of Node2D (just don't assign it any texture). Also instead of keeping track of the layers in an array, you could just iterate all children.

@MythicPalette
Copy link
Author

Just realized I did the math slightly wrong. It's not work = sprites * ( frames * animations ) which actually should be work = (sprites * tracks) * (frames * animations) which means, with my two properties, I had manually entered 2,432 keys into AnimationPlayer for 16 animations. With my LayeredSprite idea, that cut down to 128 keys. Literally cut animation time and effort by 95%

@MythicPalette
Copy link
Author

@KoBeWi
So the Layers array is actually the array of textures to be drawn. To assign everything I iterate through children.

@MythicPalette
Copy link
Author

I do like the idea of inheriting from Sprite instead of Node2D for the properties though. I guess I felt weird making a sprite derivative that created a bunch of child sprites, lol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants