Image optimizations

February 22nd, 2011 - 12:08 pm

It’s usually a bad idea to start optimizing your program too early, but I’d been running into some performance issues with my Hostile Takeover engine. The game would slow down considerably when there were many sprites to draw to the screen. The issue was most noticeable when there were many characters on the screen at once, since each character consists of about 14-16 subsprites that are all individually drawn to the screen:

The 14 subsprites of a character

Until now, the subsprites for a single character frame were mostly gathered on a single texture atlas, but there were still some instances where the subsprites were spread out over several atlases – the weapon subsprites, for instance. When a character carrying a weapon was drawn to the screen, the code would first draw all the subsprites “behind” the weapon subsprite, then switch over to the texture altas with the weapon and draw that, and then switching back to the original texture atlas to draw the remaining character subsprites “in front of” the weapon subsprite. Switching texture altases like that is expensive, as the GPU has to upload the new texture altas data.

I had originally just planned on gathering all the subsprites that make up a character frame on one texture atlas, so that the code wouldn’t need to switch back and forth between different atlases when drawing a single character. I did that, and I did get a slight performance increase, but it wasn’t enough. So, what I’ve done now is decrease the image quality of the sprites themselves.

Until now, the sprites were all RGBA8888. This means that for each color (RGB) and the alpha value (A), 8 bits of memory would be used, making the images 32-bit (8×4). This allows for 16,777,216 different colors. This also takes up a lot of memory, though, which again means that the GPU has to push a lot of data for each sprite that needs drawing.

The sprites are now instead 16-bit or RGBA4444. This immediately halves the amount of data and memory used, and provides a pretty significant performance boost. 16-bit “only” allows for 4,096 different colors, though. Compared to the 16,777,216 in 32-bit, you’d think that the difference in quality would be pretty severe. I don’t think it is, though. See for yourself:

RGBA8888 screenshot
RGBA8888 (click to enlarge)

RGBA4444 screenshot
RGBA4444 (click to enlarge)

At a quick glance, I almost can tell the difference. But if you look closely, you’ll notice that there’s a slight grain to the RGBA4444 image. This is because dithering is used to simulate a higher number of colors. I actually kinda like this grain effect. It adds a texture and feel to the images that I can’t quite put my finger on. So I think I’ll stick with this. Increased performance, lower memory use and a grain effect that I kinda like. It’s a win-win!

What do you think?

Development Video #10

February 1st, 2011 - 8:49 pm

At last, the first development video of 2011. The new additions took much longer to implement than I had anticipated, but they were also the parts of the isometric engine that I had most feared programming, so it’s a relief that they’re now done and working as I had hoped.

Watch it in HD!

  • Walls have been added, so I can now start thinking about buildings and other constructions.
  • When the player is behind a wall, it will become partly transparent to show the hidden area.
  • Walls will block light sources, so shadowcasting has been implemented.

Getting the wall sections to link up properly and implementing the transparency/x-ray effect was difficult, but not as difficult as I had feared. Shadowcasting was the opposite. My method is based on the recursive shadowcasting algorithm described here, but there were two major things that had to be changed for that algorithm to work in my engine… and those changes took about three weeks to get working.

First of all, the recursive shadowcasting algorithm is designed for a grid where an entire tile will cast a shadow – it’s often used in non-graphical roguelikes, for example, where a wall is represented by a ‘#’ that occupies a full tile. But in my engine, walls are thin and between the tiles. So the calculations for the shadows had to be redone.

The other major change is that I wanted the shadows to have a soft edge instead of the hard edge between shadow and non-shadow that the default algorithm results in. This meant that I had to keep track of how close a tile is to either a beginning or ending shadow edge and then calculate how much the shadow should affect the tile based on the tile’s proximity to the edge. There are still some minor kinks that need to be ironed out (in rare circumstances tiles that are completely in shadow will be lit), but they shouldn’t be a problem.

The extra time I had to spend on getting this working meant that progress on the shooting animations has been pretty much nil. I’m now back to making sprites for these animations, but some preliminary tests have shown that the 8 directions all the current animations are based on won’t be enough for the shooting animations. When shooting at a character that isn’t at an angle close to any of the 8 directions, it looks really odd – like the player character is shooting miles past the target. I will therefore be doubling the number of directions for the shooting animations from 8 to 16. This should be enough to ensure that it doesn’t look all too odd. But it also means I’ve just doubled the number of sprites required… and the amount of time it’ll take to make them. I’m making the original 8 directions first, though, so it shouldn’t be too long before I can make a video with some shooting going on.