Monday, December 5, 2011

Windows Console Game: Painter's Algorithm


The previous post in this series was on a wonderful bag of tricks used to set a custom color palette (awesome!) and change the font and font size. In this post we'll be going over the painter's algorithm, and how to actually make use of it in your game.


The painter's algorithm is a very simple priority paradigm which dictates when you place what image onto the screen.


For example suppose you have three different images; the sky, a cloud, and  the sun. You want to display these images, but you don't want the sun overlapping a cloud (physically impossible), or even worse have the blue sky overlap everything. To solve this you simply draw images onto the screen starting with the one that is going to be furthest back in terms of depth, which would be the sky.


Example of drawing sky, then sun, then cloud. Correct.


Example of drawing sky, then cloud, then sun. Wrong.


If we recall the article on event handling, the article actually taught a method for producing and writing images to the screen by using header files to hold image data and a function to write images to an array (or buffer) of CHAR_INFO structures. Using the same system, you can create images in header files and then write them to the screen in the correct order so that images overlap each other at the proper times. The way you go about this is to have your array of CHAR_INFO structures for writing images to. Then once you've manipulated the array (or buffer) all you want, you then pass it to the WriteConsoleOutput function and place it onto the screen in one fell swoop.


Here's a screenshot of a demo program I wrote just for this specific purpose!
Rendering of images with transparency using painter's algorithm.
Note: Read this article for info on resizing font, this will allow
you to have square characters like in the image above (8x8).


Building from our knowledge gained in the previous posts, I've put together a nice demo that displays the images above, and lets you draw on the screen with the left and right clicks of the mouse. Lets examine some of the code:


#ifndef FILESUNH
#define FILESUNH


/* A SUN background! */


#define SUNW 15
#define SUNH 15


typedef struct
{
  int width;
  int height;
  int chars[SUNW * SUNH];
  int colors[SUNW * SUNH];
} _SUN;


_SUN SUN = 
{
  SUNW,
  SUNH,
  {
    255,255,255,255,255,255,255,177,255,255,255,255,255,255,255,
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
    255,255,255,177,255,255,255,177,255,255,255,177,255,255,255,
    255,255,255,255,177,255,255,255,255,255,177,255,255,255,255,
    255,255,255,255,255,255,177,177,177,255,255,255,255,255,255,
    255,255,255,255,255,177,178,219,178,177,255,255,255,255,255,
    255,255,255,255,177,178,219,219,219,178,177,255,255,255,255,
    177,255,177,255,177,219, 94,219, 94,219,177,255,177,255,177,
    255,255,255,255,177,178,219,126,219,178,177,255,255,255,255,
    255,255,255,255,255,177,178,219,178,177,255,255,255,255,255,
    255,255,255,255,255,255,177,177,177,255,255,255,255,255,255,
    255,255,255,255,177,255,255,255,255,255,177,255,255,255,255,
    255,255,255,177,255,255,255,177,255,255,255,177,255,255,255,
    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
    255,255,255,255,255,255,255,177,255,255,255,255,255,255,255,
  },
  {
      0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0, 62,  0,  0,  0, 62,  0,  0,  0, 62,  0,  0,  0,
      0,  0,  0,  0, 62,  0,  0,  0,  0,  0, 62,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0, 62, 62, 62,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0, 62, 62, 14, 62, 62,  0,  0,  0,  0,  0,
      0,  0,  0,  0, 62, 62, 14, 14, 14, 62, 62,  0,  0,  0,  0,
     62,  0, 62,  0, 62, 14,224, 14,224, 14, 62,  0, 62,  0, 62,
      0,  0,  0,  0, 62, 62, 14,224, 14, 62, 62,  0,  0,  0,  0,
      0,  0,  0,  0,  0, 62, 62, 14, 62, 62,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0, 62, 62, 62,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0, 62,  0,  0,  0,  0,  0, 62,  0,  0,  0,  0,
      0,  0,  0, 62,  0,  0,  0, 62,  0,  0,  0, 62,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0,  0,  0,  0,  0,
  }
};


#endif /* FILESUNH */

The above code is the header file for the image of the sun. As shown in this article, you can easily create new images! The most interesting thing about this image is the 255 character code in the chars array. This code is actually used for transparency. I modified our old writeImageToBuffer code to account for transparency by adding in a single if statement. If the loop finds a character of 255, it does nothing and skips the character. This will leave whatever character was already written in that particular location alone, thus creating transparency! Take a look at the new writeImageToBuffer function:


/* Writes an image to the buffer */
void writeImageToBuffer(CHAR_INFO buffer[], int chars[], int colors[], int imageBUFFERWIDTH, int imageBUFFERHEIGHT, int xoffset, int yoffset)
{
  int x, y;
  
  /* Keep xoffset and yoffset within the screen's boundaries */
  boundCheck(&xoffset, &yoffset);
  
  for (y = 0; y < imageBUFFERHEIGHT; ++y)
  {
    for (x = 0; x < imageBUFFERWIDTH; ++x)
    {
      if (chars[x + imageBUFFERWIDTH * y] != (unsigned char)255)
      {
        buffer[(x + xoffset) + BUFFERWIDTH * (y + yoffset)].Char.AsciiChar =
               chars[x + imageBUFFERWIDTH * y];
        buffer[(x + xoffset) + BUFFERWIDTH * (y + yoffset)].Attributes =
               colors[x + imageBUFFERWIDTH * y];
      }
    }
  }
  return;
}

As I said, there's simply a new if statement to make sure that the character 255 is not written onto the screen. 255 in ASCII is a character you'll more than likely find pretty useless, as it just fills the whole space with foreground, though you have the exact same thing with 219 without any nasty side affects that 255 can ensue on your code. Basically 255 is the perfect ASCII index for transparency as it's pretty much useless as anything else.


The last thing to show in this demo is the order in which you actually write images onto the buffer, being sky sun and cloud. This is really simple as shown here:


writeImageToBuffer(consoleBuffer, SKY.chars, SKY.colors, SKY.width, SKY.height, 0, 0);
writeImageToBuffer(consoleBuffer, SUN.chars, SUN.colors, SUN.width, SUN.height, 10, 5);
writeImageToBuffer(consoleBuffer, CLOUD.chars, CLOUD.colors, CLOUD.width, CLOUD.height, 15, 11);

And there you have it! By ensuring that you write images in the correct order in terms of depth, you overwrite images farther back with images closer up creating a proper sense of depth to the viewer.


The next post in this series is on a more flexible image structure format to allow a variable sized structure.


Series on creating a Windows Console game:


5 comments:

  1. please sir, can I have some more (guides)? :)

    ReplyDelete
  2. hey what software does digipen have standard on their pcs? I know they use 3ds max and visual studio right? for 2d graphics do they use photoshop? do they use microsoft office?

    ReplyDelete
  3. There's a lot of different software. I don't know much about which programs the art students use, though there is Borland, GNU and Microsoft compilers, along with the VS IDE. I believe they use both 2008 and 2010 editions. Most computers use Open Office. There's definitely Adobe Photoshop, and probably Gimp too.

    ReplyDelete
  4. thanks for the response. I'll be attending Digipen in fall 2012 for RTIS. I have some more questions if you don't mind: When you are making games what is their stance on using art/sound/music assets or ideas from other games? Do you have to 100% make everything yourself or are you allowed to use at least an idea of lets say a character like Megaman? I'm asking since I am terribly uncreative and I love programming and math but not very design oriented. How is ProjectFUN? I am also currently looking at apartments, do you have any advice for that? I read through everything I could find about apartments in the area including Digipen's guide and others and have a good idea about which ones are good but I'd always to hear more advice.

    ReplyDelete
  5. As for games DigiPen owns the rights to everything you create for school. This means that you cannot use anything from anyone except DigiPen students. Ideas however are not copyrighted. Just this last semester someone made a Pokemon clone in C. ProjectFun is okay, but pretty buggy as of late. I hated using it. As for apartments, be sure to try to get a cheap one near the school with a good amount of roommates and start looking early!

    I myself don't like Shadowbrook or Trailwood :P

    ReplyDelete

Note: Only a member of this blog may post a comment.