Extended Graphical Templates for Sprite Management

Published July 12, 2000 by TANSTAAFL, posted by Myopic Rhino
Do you see issues with this article? Let us know.
Advertisement
[size="5"]What are graphical templates?

Although you may be unfamiliar with the term, chances are you've seen a graphical template; especially if you've read some of the more popular game programming books. This is what one looks like:

pazera1.gif



[size="5"]What's wrong with this?

At one time, I used these to manage tilesets. However, in almost all cases, there wasn't enough information in the image, and I'd have to either hardcode the width of the tiles, or save cell information in another file. Needless to say, it was an unwieldy solution. What I wanted to do was to be able to provide all of the information about the sprites in the template itself, such as the size of the cell, which more often than not did not take up the entire cell, and centering or reference points so that I could supply my sprite blitting function with just a (x,y) pair, and have the sprite blit properly in relation to that point. Also, I wanted to supply transparency information, and cell size so that I could just load the tileset, and go.


[size="5"]What I did to fix it ...

So, I came up with a way to keep track of all of this information graphically. The first thing I did was to make the pixels where the white lines cross black. Actually, it can be any color. This color is also used as the transparent color key (magenta (RGB(255,0,255)) is a favorite of mine). What these black(or whatever) dots allow us to do is determine the width and height of the cells. In fact, if we really wanted, we could have more than one width or height within the same tileset... I'd like to see you do that without a lot of pain in a standard template.

The next thing I added was a reference point (I call it an "anchor") on the cell walls (the x goes on the top, the y on the left). This can be used for a center, or to reference where the foot of a character is, or the base line of a font. With this, you can just pass an (x,y) pair to your blitting function, and the (x,y) point will correspond to the anchoring point in the image.

Still, this wasn't enough for me. Most of the time, my sprites didn't take up the entire cell. Depending on your blitting function, and whether or not you make heavy use of transparent color keys, many times you either have to blit twice to the same pixel, or you have to test all of the pixels in the source against the color key. This isn't terribly inefficient by any means, but why should we force ourselves to do this if we don't have to? So, what I did was I put a different color to specify the extent of the sprite on the template walls (x range on the top, y range on the left). This way, my loader could determine the size, and store it in a RECT, and then use some code to only blit that RECT.

Of course, this lead to a problem. Sometimes the reference point is within the width or height, sometimes not. To fix this, I added one more control color to differentiate between anchors within the image rect, and outside the image rect.

Here's an example of an extended template, with a few sprites from spritelib:

pazera2.gif


And here's the same image, blown up to 400%

pazera3.gif


As you can see, the green sections make up the rectangles into which the sprites fit. The blue dots are the y reference, which is one pixel below the feet of the characters, and the cyan dots are the centering reference dots for x. The reason that the y anchors are one pixel below the feet of the characters is so that I can align the sprites on the topmost pixel of whatever they are standing on.

In the upper right corner, along the right side, there are a series of 5 dots. These are for the control colors, so that you can use whatever colors you like.


[size="5"]How to load in images that use the extended template ...

Initially upon loading, you have to load in the control colors from the rightmost pixel column. I use the order Transparent, Border, Outside Anchor, Inside, Inside Anchor. This is just a personal preference, order-wise.

Next, you scan the top row, and leftmost column of pixels, and parse out the cell widths and heights.

After that, you scan the top and left wall of each cell for the anchors and sprite rectangles. Then you're ready to go. The coordinates for the RECTs and anchors should be in relation to the tileset image as a whole. You've got all the information you need to be able to blit your tiles.

Now, if you're using GDI, your work isn't quite done. You'll have to scan for transparent pixels, and replace them with black (if they aren't already), and also make a bitmask, but since all the transparent pixels are there in the image, you can go ahead and do that while scanning for transparent pixels. The beautiful thing is that you don't have to scan the entire image, you can just scan within the sprite rectangles.

If you're using DirectX, then your job is simple, just set the source color key to the transparent control color (be sure to take into account the pixel format) and go.


[size="5"]Blitting a sprite from an extended template ...

You'll probably want to keep all of this information in a class or a struct, like so:

//GDI tileset
struct GDITileset
{
HDC hdcImage;//contains the actual image
HDC hdcBitMask;//contains the bitmask
LPRECT rcRectList;//list of sprite rectangles
LPPOINT ptAnchorList;//list of anchors
DWORD dwSpriteCount;//number of sprites
};

//ddraw tileset
struct DDTileset
{
LPDIRECTDRAWSURFACE7 lpddsTileset;//surface containing tileset
LPRECT rcRectList;//list of sprite rectangles
LPPOINT ptAnchorList;//list of anchors
DWORD dwSpriteCount;//number of sprites
};
Regardless of using GDI or DDraw, the way to find out where to blit to is the same.

Your blit function will most likely resemble one of the following:

bool BlitSpriteGDI(HDC hdcDest, GDITileset* pTileset,
DWORD dwSpriteIndex, int xDest, int yDest);
bool BlitSpriteDD(LPDIRECTDRAWSURFACE7 lpddsDest, DDTileset* pTileset,
DWORD dwSpriteIndex, int xDest, int yDest);
Within either of these functions, you first check to see if dwSpriteIndex is a valid sprite by comparing it to pTileset->dwSpriteCount. If it isn't valid, return false.

if(dwSpriteIndex>=pTileset->dwSpriteCount)
return(false);
Next, we pull out the source RECT.

RECT rcSrc;
CopyRect(&rcSrc,&pTileset->rcRectList[dwSpriteIndex]);
Now, we calculate the destination RECT:

RECT rcDest;

CopyRect(&rcDest,&rcSrc); //start by copying the source rect
OffsetRect(&rcDest,-pTileset->ptAnchorList[dwSpriteIndex].x, -
pTileset->ptAnchorList[dwSpriteIndex].y); //subtract the anchor
OffsetRect(&rcDest,rcSrc.left,rcSrc.top); //add the upper left corner of the source rect

Lastly, the actual blit. With GDI, first the bitmask with SRCAND, then the image with either SRCPAINT or SRCINVERT. With DDraw, just use Blt.

And that's about all there is to blitting from an extended template.


[size="5"]Other uses for extended templates...

I use extended templates for almost ALL of my 2d graphics. Sprites, animation sequences, cursors, tiles, fonts, you name it.

The great thing about using these templates for animation sequences is that you can move the anchor points for a sprite without moving the image. This makes lining up cells really easy.

Bitmapped fonts are a breeze, since you have the width of the sprite within the struct. I usually use a little VB utility I wrote to take a font and make an extended template with it. I only include characters 32 through 127, and then I have the program scan the image to set up the width and height and the anchors. (for the space, I just copy the width and x anchor from the exclamation point).

Using extended templates for cursors is really cool. You can use the anchor point as the point that you are on. This is especially useful in fullscreen DirectDraw applications where you are using DirectInput, and need to do your own cursors, and want to have them animated; extended templates make that management easy.


[size="5"]One more really cool thing ...

One last thing, and I'll stop trying to sell you on extended templates.

Since you have the sprite rectangles, you can programmatically make the entire set smaller. It's a really simple process.

First, you scan through each row, and determine the largest height (has to include the anchor if there are any anchors outside of the image itself). Next, you scan each column, and do the same thing for width.

Now, you can create a new template, and use the information from the scan as the cell widths and heights, and then reposition the sprites based on this information. Voila! you have a smaller image, thus decreasing load time and scan time.


[size="5"]So, what CAN'T extended templates do for you?

Well, there's one thing I'd like these templates to do that they currently cannot, and that is store strings naming the sprites. Sigh. Bitmaps can only be extended so far. If anybody has any ideas, though, let me know.

[email="dtmsoftware@acronet.net"]Ernest Pazera[/email]
Cancel Save
0 Likes 0 Comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement