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:


Thursday, December 1, 2011

Windows Console Game: Set Custom Color Palette and Font Size

Last post the topic was of event handling, this article is going to cover a treat! I have recently been graced with a great sample source code of a program that can alter the windows console font, color palette, and font size. If you've ever searched for methods of changing these aspects, if you're like me, you'd have found a whole lot of nothing. Luckily, like I mentioned, someone gave me some sample source code that I'd like to share.


The source code, as I was told and will now tell you, is filled with a whole lot of a whole lot of ugliness, undocumented Windows functions, and magic. Despite these scary facts this source code is pretty easy to use and grants access to some very nice to have features, that can make the creating of a console game much prettier! For example say you are creating an ASCII game in the Windows Console and realize that you're stuck with the default key size of 8x12 pixels. This sucks. Now constructing square images is going to be a gigantic pain! It would be wonderful if you could have all of your characters simply be squares! Well, there's a nice 8x8 Terminal font that is available to use.


Here are the source files:
I've modified the files so that they should compile cleanly in Visual Studio 2010, and in GCC 4.X.X. Once compiled the exe should run a short demo displaying a few different things, including modifying the console's color palette and setting it to the default colors, changing the font type, and changing the font size.

This all works by using a few functions from kernel32.dll, some of which are undocumented and thus very difficult to use. I don't know exactly how everything works, and a good amount of it I don't understand whatsoever - though this is to be expected when messing with things that don't have documentation. The goal is for me to explain just what is necessary to understand the idea of what's going on so you can apply these desired features to your own console application.

Lets start off by explaining the main function.


int main(void)
{
    /* Setting the color palette to the default colors
    /* Browse MSDN for COLORREF to learn more about these RGB values */
  COLORREF palette[16] = 
  {
    0x00000000, 0x00800000, 0x00008000, 0x00808000,
    0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0,
    0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00,
    0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
  };
    /* Search MSDN for the RGB macro to easily generate COLORREF values */

  SetConsolePalette(palette, 6, 6, L"");
  printf("Small default font.\n");
  Sleep(2000);
  
  SetConsolePalette(palette, 1, 2, L"Lucida Console");
  printf("Too tiny to read Lucida!\n");
  Sleep(2000);
  
  if (windowsVersionTest())
  {
    SetConsolePalette(palette, 8, 12, L"Consolas");
    printf("More of a normal Consolas size.\n");
  }
  else
  {
    SetConsolePalette(palette, 8, 12, L"Lucidas");
    printf("More of a normal Lucidas size.\n");
  }
  Sleep(2000);
  
  SetConsolePalette(palette, 43, 72, L"Lucida Console");
  printf("Huge Lucida!\n");
  
  getchar();

  return 0;
}

This function has an array of hex numbers at the top. These numbers correspond to the color palette in the Windows Console's properties:




The eight colors above correspond to the eight elements of the palette array. You can change them to any RBG value, and you can test out different values like in the screenshot above, by messing with the Red Blue and Green color value fields. Just be sure to leave the left two digits in your hex values as 0, as only the first six digits are used.


SetConsolePalette is a wrapper function that sets a console's palette, console font size, and console font. The first parameter is a color palette, the second is the font's x dimension, third is the font's y dimension, and last is the name of the font you wish to switch to in the form of a wide character literal. Available fonts are Terminal, Lucidas, and Consolas by default. In order to have more available, you have to make them! Making fonts is definitely outside of the scope of this article, however. It should be noted that Consolas is only available in Vista/7 versions of Windows, and not available in XP. If you want to use the Terminal font, on lines 109 and 139 you must set the value of the 0 to 0x30. Which line you modify depends on your OS. Just modify both of those lines if you don't know which corresponds to one you need.


That is about all the info you really need to simply use the source code! If you want to try to learn more, study the comments throughout the source code and/or ask me questions in the comments section. You can also try googling bits of the source code and see what you come up with. Googling CONSOLE_INFO came up with a lot of variations of the source I've posted, with some differing comments throughout the source files.


I haven't created my own custom font yet, though I did find a nice tool here for editing and developing raster fonts. In the near future I'll probably create my own custom font for the console game(s) I'm creating as school projects. The next article in this series is on painter's algorithm.


Series on creating a Windows Console game: