3
\$\begingroup\$

For educational purposes, I'm developing a 2d game without APIs or libraries. Currently I'm stuck with a sprite drawing problem.

To store sprites for drawing (assuming I have the sprites I need all in one sprite sheet), is it better to

  1. store just the coordinates for each sprite for each entity, and then draw only the corresponding part of the sprite sheet when the entity should draw itself, or

  2. store actual parts of the sprite sheet in each entity and the draw each the cropped sprite?

Another thing I'd like to learn is how to handle sprites with different sizes. See Zelda, for example: Link's walking sprites are all about the same size, regardless of his position or orientation. But when he attacks, the sprites include his sword and depending on his orientation, some sprites are wider and some longer.

Is there a good way to position and align sprites with different sizes relative to a part of the character—their foot, for example? Should I store some sort of offset for each sprite along with the sprites?

\$\endgroup\$
  • \$\begingroup\$ Without any libraries?! I think you will probably have much bigger problems than the ones you've brought up here - good luck! \$\endgroup\$ – rlms Jul 1 '14 at 22:07
  • \$\begingroup\$ @sweeneyrod hahahaha thanks! I've faced a lot of issues so far, like collision detection and I was able to bypass them. This issue with sprites however really got me stuck. \$\endgroup\$ – Tinadm Jul 2 '14 at 0:14
  • \$\begingroup\$ It will surely be a great learning experience to you. +1 to that! \$\endgroup\$ – glampert Jul 2 '14 at 1:10
4
\$\begingroup\$

I assume you will be using some modern rendering API to draw to the screen, such as OpenGL or D3D. You will certainly want to batch sprites as much as possible and use sprite sheets to reduce the number of textures. A sprite sheet is nothing more than a Texture Atlas (also read this). Once you have a Texture Atlas up and running, it will be up to you how you assign sprite frames/images to them. On common practice is, for example, to use one altas per game character, one for static elements, one for background elements, etc.

To each sprite you can assign a reference to the altas where its frames are and a list of locations inside the altas where the frames are. Instead of atlas locations you can actually store raw texture coordinates (UVs).

OK, so suppose you have a sprite sheet/texture atlas like this one:

enter image description here

You will also need some extra info about the rectangles of each sprite frame (each one of those cats is a sprite frame). You can use anything from a txt file with (x,y,w,h) sets on it, from a more complex XML file. But the basic thing you need to specify is the rectangles in image space where the frames are.

So your atlas class would look something like:

class TextureAtlas {

    LoadTexture(string filename);  // Loads the image itself
    LoadMetadata(string filename); // Loads the metadata describing the atlas nodes/sprite frames

    Region GetRegion(int index);

    Texture  texture; // The actual image. Immutable
    Region[] nodes;   // The nodes/frames. Each is a rectangle inside the 'texture' image
}

Once you have something like that you can define a sprite with:

class SpriteFrame {

    // The region inside the atlas texture
    // where this frame resides; As texture coords, ready for rendering.
    float u0, v0;
    float u1, v1;

    const TextureAtlas * atlas; // Just a pointer/ref, not the actual object.
                                // This allows you to share a texture with many sprites.
}

class Sprite {

    // And a sprite is just a collection of frames.
    SpriteFrame[] frames;
}

Then you would use such setup in much this way:

TextureAtlas catAtlas;
catAtlas.LoadTexture("runningcat.png");
catAtlas.LoadMetadata("runningcat.meta");

Sprite catSprite1 = new Sprite(catAtlas);
Sprite catSprite2 = new Sprite(catAtlas);
...

The constructor of Sprite still need to go thru every sprite frame and get the UVs from the atlas to set them up. Something like:

for each SpriteFrame frame in Sprite
{
    Region region = atlas.GetRegion(i);
    frame.u0 = region.x / float(atlas.Width());
    frame.v0 = region.y / float(atlas.Height());
    frame.u1 = (region.x + frame.w) / float(atlas.Width());
    frame.v1 = (region.y + frame.h) / float(atlas.Height());
}

And finally, the rendering would be very simple and efficient:

func DrawSprite(Sprite s)
{
    // Apply/bind the atlas texture once
    s.atlas.Apply();

    for each SpriteFrame frame
    {
        // Send down the frame texture coords (UVs)
        // and geometry vertexes to the rendering API.
        // The texture is already set and ready to be used.
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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