Skip to content

Resolution independence

Donald Smith edited this page Dec 23, 2018 · 5 revisions

Designing your game to run at any resolution is useful for a couple of reasons:

  • Quite often nowadays, ALLEGRO_FULLSCREEN display modes - that is, those that actually alter the resolution of the user's display to accommodate your game - simply aren't possible with Allegro. There are various issues preventing them.
  • Minimal work is required to scale your graphics up to larger displays, or down to an arbitrarily sized window.

Back in the days of DOS gaming and CRT monitors, resolution changes were much more common and expected given the lack of video memory. Nowadays, users expect to run your game at native resolution.

There are two ways of dealing with this:

  • Choose the low-res, pixelated look. Design your graphics as if the user's screen was natively eg. 640x480 pixels, and shamelessly scale up your graphics to fill the screen.
  • Use vector graphics. This means you'll effectively draw the majority of your graphics as primitives rather than sprites - and rather than thinking in positioning in terms of pixels, you'll tend to think in terms of "fractions of the display". You'll also need to consider that different displays have different aspect ratios.

Allegro transforms make both options easy. Transforms are a set-it-and-forget-it solution. Once a transform is in place, you simply start drawing graphics and let Allegro and your video hardware figure out the scaling.

Example program: How to Create and Use a Transformed Display

/* 
 * This tutorial uses four included headers. The first,
 * stdio, is used for standard input-output and is necessary
 * for any printf functions to work.
 */
#include <stdio.h>

/*
 * The next three headers are for allegro and it's addons.
 * Allegro, allegro_color, and allegro_font will be used.
 */
#include <allegro5/allegro.h>
#include <allegro5/allegro_color.h>
#include <allegro5/allegro_font.h>


/*
 * Here are the "low-res" width and height that allegro
 * will be emulating. 320x240 used to be a common resolution
 * among old DOS games. Here, we use 480x240, because it
 * scales better on modern widescreen 16:9 monitors.
 *
 * Experimenting with these numbers will drastically affect
 * the final "look-n-feel" of your game. For example, if
 * you want that early eighties Sierra AGI engine style,
 * you might changes these numbers to 160x200.
 */
#define SCALED_WIDTH		    480
#define SCALED_HEIGHT		    240


/*
 * Every C program requires a main function as a point
 * of entry. Note that we take no command line
 * arguments as parameters.
 */
int main(void)
{

	/*
	 * We need a couple of pointers. One will store the
	 * memory address of the font we will use to test
	 * our scaled resolution, and one will store the
	 * address to the display. 
	 */
	ALLEGRO_FONT *f = NULL;
	ALLEGRO_DISPLAY *d = NULL;

	/* 
	 * Modern video hardware doesn't "think" in 2D ---
	 * your video hardware is a very sophisticated
	 * 3D calculating machine. To translate 2D images
	 * into 3D vectors the hardware can consume, we
	 * use a transform. The mathematics is mostly
	 * done behind the scenes. Allegro does the
	 * scaling stuff for us.
	 */
	ALLEGRO_TRANSFORM t;

	/*
	 * Load and check allegro initailization. It is
	 * good practice to check allegro before trying
	 * to use it, but chances are if it is installed 
	 * correctly, it will work. If it does not, fire
	 * an error message to the console with fprintf
	 * and exit with an error code.
	 */
	if (!al_init()) {
		fprintf(stderr, "Cannot load allegro!");
		return -1;
	}

	/*
	 * Assuming allegro loads correctly, we can proceed
	 * with creating a ALLEGRO_DISPLAY, or the interface
	 * we use to talk to the video hardware. Most resolution
	 * independant games will run in full-screen mode by
	 * default. Because most monitors don't actually change
	 * resolutions, we emulate the "full screen" effect
	 * using big a window the size of the screen.
	 */
	al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW);

	/*
	 * Now we finally create the display. Because we
	 * passed ALLEGRO_FULLSCREEN_WINDOW as a display
	 * flag, al_create_display will ignore the width
	 * and height arguments --- they will only become
	 * important if we toggle into a windowed display.
	 * If we never toggled out of full-screen mode,
	 * we could call this function with the arguments
	 * 0,0 and it would not matter.
	 */
	d = al_create_display(SCALED_WIDTH, SCALED_HEIGHT);

	/* 
	 * Again, we must check that the display was created
	 * successfully. If display creation failed, send error
	 * to the console and exit with error code.
	 */
	if (!d) {
		fprintf(stderr, "Cannot create allegro display!");
		return -1;
	}

	/*
	 * To test our working transformed display, we will
	 * print "Hello World!" to the screen. This means
	 * we have to load the allegro font addon, plus
	 * a font. For the font, we shall use the allegro
	 * built-in font, with nice big blocky pixels. We will
	 * also check both the addon and font for errors
	 * before continuing. 
	 */
	if (!al_init_font_addon()) {
		fprintf(stderr, "Cannot set up allegro font addon!");
		al_destroy_display(d);
		return -1;
	}
	f = al_create_builtin_font();
	if (!f) {
		fprintf(stderr, "Cannot create builtin font!");
		al_destroy_display(d);
		return -1;
	}

	/*
	 * To scale up the resolution, we need to variables to
	 * store the scale factor. To get the number we need,
	 * we divide the actual resolution value by the "fake"
	 * value that gives us the blocky fat pixels we want.
	 * We also calculate this value as a float for greater
	 * accuracy.
	 */
	const float scale_factor_x = ((float)al_get_display_width(d)) / SCALED_WIDTH;
	const float scale_factor_y = ((float)al_get_display_height(d)) / SCALED_HEIGHT;

	/*
	 * Now that we have the scaling factor numbers, we can
	 * apply them to the transform. First, we need to tell
	 * Allegro to use the transform held in variable 't'. Next,
	 * we need to set the scale values for this transform.
	 * We do this with the al_scale_transform function. Finally,
	 * once we're through making changes to this transform, we
	 * have to tell Allegro to use it. This is the last time
	 * we need to touch the transform in this code.
	 */
	al_identity_transform(&t);
	al_scale_transform(&t, scale_factor_x, scale_factor_y);
	al_use_transform(&t);

	/*
	 * It's worth noting that at this point the resolution is
	 * set. Bitmaps will now draw in nice fat "low-res" blocky
	 * pixels. We don't have to do anything else to the
	 * transform matrix, unless we're doing something truly
	 * and deeply weird.
	 */

	/*
	 * Clear the background screen color to black. It's handy
	 * to use the Allegro color function al_color_name to
	 * get the color we want. Next, we draw the text string
	 * "Hello World!" using our font we created earlier.
	 */
	al_clear_to_color(al_color_name("black"));
	al_draw_text(f, al_color_name("white"), 1, 1, ALLEGRO_ALIGN_LEFT, "Hello world!");

	/*
	 * Nothing will show on the screen until we call
	 * al_flip_display. This tells Allegro to 'flip' the
	 * screen over to the video buffer we've thus far been
	 * drawing to.
	 */
	al_flip_display();

	/* 
	 * Wait 5 seconds, so the program doesn't exit before
	 * we can see the text.
	 */
	al_rest(5.0);

	/*
	 * We've used our transformed display successfully. Now,
	 * we close the program, by destroying the resources
	 * we've used. We don't have to manually turn off the
	 * font addon, or Allegro itself, as they will shut
	 * themselves off automatically at program exit.
	 */
	al_destroy_font(f);
	al_destroy_display(d);

	/* 
	 * Program ran successfully, so we return error code
	 * 0 to the operating system.
	 */
	return 0;

}

Note that the above program will not scale primitives --- i.e., circles, lines, rectangles, etc. To scale primitives with a "fat pixel" look, it will be necessary to draw first to a smaller video bitmap, then draw a scaled copy of the bitmap to the screen. Using transforms is ultimately preferable, as transforms will be quite a bit faster than using a buffer bitmap. Changing the target draw bitmap (or display) is quite slow, and to be avoided when possible.

You can’t perform that action at this time.