0
\$\begingroup\$

I have a spritesheet for a character. The spritesheet has several rows, and each row represents an animation. The elements within a row might vary in width.

How can I load the different sections of the spritesheet and set animations for each game element?

My first approach to this problem was to manually calculate the rectangle for each frame and hardcoding them into the respective object classes. The Hero class, for example, would have several arrays of rectangles with the hardcoded bounds of every frame of animation. This method is really primitive, however, so I am looking for a solution that takes this to the next level.

-

I am at the moment using an interesting approach which although in the end is very easy to use, it sounds a little complex, but i would like an opinion about it.

I have an Animation class which has a function setFrames(Rectangle.Float[] frames); Every game element has an animation, even if it only has a sigle sprite its whole lifetime. This class takes care of which frame is time to show at a certain time of the game. The way to use it is simply set the frames everytime a new animation is supposed to play, and receive the rectangle in the draw (and update is, ofc)

Example: While Idle, I press right button, this makes me walk right setFrames method is called with the "walking" rectangle array passed in its argument, thus changing which frames are looping.

Now, the way i abstracted from the rectangles was I started packing my sprites with a program called TexturePacker which places the sprites in a spritesheet by itself and creates an XML file with each sheet element's name and position, as simple as this:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with TexturePacker http://www.codeandweb.com/texturepacker-->
<!-- $TexturePacker:SmartUpdate:d4e1eb1edb1e925f5e2b7fea77dae064:e103ddd8b5f54e4f084f4075e778872a:e8412e2a02129a8b5ad20319afc153de$ -->
<!--Format:
n  => name of the sprite
x  => sprite x pos in texture
y  => sprite y pos in texture
w  => sprite width (may be trimmed)
h  => sprite height (may be trimmed)
oX => sprite's x-corner offset (only available if trimmed)
oY => sprite's y-corner offset (only available if trimmed)
oW => sprite's original width (only available if trimmed)
oH => sprite's original height (only available if trimmed)
r => 'y' only set if sprite is rotated
-->
<TextureAtlas imagePath="blabla.png" width="128" height="128">
    <sprite n="four" x="2" y="2" w="100" h="32"/>
    <sprite n="one" x="2" y="36" w="32" h="32"/>
    <sprite n="three" x="36" y="36" w="53" h="32"/>
    <sprite n="two" x="91" y="36" w="12" h="32"/>
</TextureAtlas>

Then, I created a SheetLoader class, which is essentialy a parser of the XML file and receives the XML path in its argument and fills this:

private HashMap<String, Rectangle.Float> textures;

with the elements names as keys and the elements rectangles. This class also has a method called getSprite(String); which returns a rectangle of a sprite based on its name (its a simple call from the hashmap)

I then created this weird Enum class:

package render;

import java.awt.Rectangle;

public enum Anims {

    HEROIDLE("Resources/SpriteSheets/blabla.xml", "one", "two", "three", "four", "three");

    Rectangle.Float[] frames;

    Anims(String path, String... animation)
    {
        frames = new Rectangle.Float[animation.length];
        SheetLoader sL = new SheetLoader(path);

        for(int i=0; i<animation.length; i++)
        {
            frames[i] = sL.getSprite(animation[i]);
        }
    }

    public Rectangle.Float[] getFrames() {return frames;}
}

So basically the way to use this is, to create a new animation I add a new enum with the info from the XML path (where i get the rectangles from), and the sequence of the sprites that form the animation, and if this info exists, the enum is successfuly created. Using this is very easy later, for example if i wanna change the hero animation to idle id just do:

animation.setFrames(Anims.HEROIDLE.getFrames());

The thing i like about this method is, i can forget about worrying about the rectangles. I just make the spritesheet, I send the XML file to the game and i just use the sprite names directly in the enum declaration.

Still this way might not be the best in therms of simplicity or optimization, so if that is the case id like to know different solutions or how it is usually done.

\$\endgroup\$
  • 1
    \$\begingroup\$ open-ended requests for suggestions aren't a good fit for this site. There is no correct answer. Is there some specific problem with your method that you would like to solve? If not, it might be best to focus your attention on the remainder of your game, rather than optimizing for its own sake. \$\endgroup\$ – Seth Battin Dec 28 '13 at 4:49
  • \$\begingroup\$ I think this is a good question, but I think you've posted it poorly. You've included an entire answer in the question (and it's a valid answer), which clutters the question and makes it more of a yes/no question where all you are asking for is for somebody to validate your approach. That's not a good question for this site. \$\endgroup\$ – Josh Dec 30 '13 at 20:29
  • 1
    \$\begingroup\$ I would suggest that you edit the question to remove all of your proposed solution (from "I am at the moment using..." onward). Post that as an answer instead; the community will show you that it is a good answer or not by voting on it, and you can leave the basic part of your question alone, allowing a broader range of better answers than just "yes that's good." \$\endgroup\$ – Josh Dec 30 '13 at 20:30
  • \$\begingroup\$ See also this question, which may be a duplicate. \$\endgroup\$ – Josh Dec 30 '13 at 20:32
1
\$\begingroup\$

I don't see much wrong with your approach. A good sprite graphics pipeline doesn't require any manual definitions for the sprites. How all of this happens is just an implementation detail, but the graphics artist should not need to care about it. Usually they are the most comfortable using Photoshop, so the more you can push to Photoshop of the pipeline, the better. The other tools of the pipeline can be executed by the artists, programmers or automatically, it's up to you.

One problem I see in your approach is this line:

HEROIDLE("Resources/SpriteSheets/blabla.xml", "one", "two", "three", "four", "three");

Here you seem to be specifying the frames in the code. It would be nice if the artists could do this themself. How this would happen depends on the rest of your toolchain and there might not be both easy and userfriendly solution.

Another thing you could improve on is to use code generation for creating the constants in your Anims class. This code generation would be run when the sprite data has changed. The benefit would be removing one more manual step, as manual steps are always more or less error prone. On the other hand depending on the size of the game/engine this might not be worth the effort.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.