Chapter 5. Programming Backgrounds for Video Games
Backgrounds in video games are essential for setting up the scene and enhancing the visual appeal of the game world.
Backgrounds play the following important roles in video games:
- 1. Setting the Scene
Backgrounds help establish the game’s environment, whether it’s a bustling city, a serene forest, or an alien planet. They provide context and make the game world feel more immersive.
- 2. Enhancing Atmosphere
The design and colour scheme of backgrounds can significantly influence the game’s mood and atmosphere. For example, dark, gloomy backgrounds can create a sense of tension and mystery, while bright, colourful backgrounds can evoke a cheerful and adventurous feeling.
- 3. Guiding the Player
Backgrounds can also serve functional purposes, such as guiding the player’s attention to important areas or providing visual cues for navigation.
5.1. Types of Backgrounds
When programming games, there are four types of backgrounds one may choose to use:
- 1. Static Backgrounds
These are nonmoving images that provide a backdrop for the game. They are commonly used in 2D games and can be highly detailed to create a rich environment.
- 2. Scrolling Backgrounds
Often used in side-scrolling games, these backgrounds move horizontally or vertically to give the illusion of depth and motion. Parallax scrolling, where multiple layers move at different speeds, enhances this effect.
- 3. Dynamic Backgrounds
These backgrounds change in response to game events or player actions. For example, the background might shift from day to night or change weather conditions.
- 4. 3D Backgrounds
In 3D games, backgrounds are part of the 3D environment and can include complex structures and landscapes. These backgrounds are rendered in real time and can interact with the game’s lighting and physics.
5.2. Creating and Using Static Backgrounds
Static backgrounds in video games are nonmoving images that serve as the backdrop for the game’s action. They are commonly used in 2D games, especially in genres such as platformers, puzzle games, arcade shooters, and visual novels.
Static backgrounds remain popular because they are:
- • Simple and efficient, requiring minimal processing power
- • Artistically expressive, allowing detailed, handcrafted environments
- • Easy to integrate, since they are drawn once per frame, before all other objects
- • Ideal for stable scenes, such as menus, overworld maps, and indoor environments
Classic examples include Super Mario Bros., Mega Man, Sonic the Hedgehog, The Legend of Zelda, and many visual novels where the background sets the mood but does not move.
5.2.1. Types of Static Backgrounds
Static backgrounds come in a variety of styles depending on the game’s design, visual direction, and performance needs:
- Full-Screen Static Image: A single image that fills the entire screen
- • Common in puzzle games, RPG towns, menus, and narrative scenes
- • Easiest type to implement
- • Often painted or rendered as a complete scene
- Tiled Backgrounds: Built from smaller repeated tiles (e.g., 16 × 16, 32 × 32, or 64 × 64 pixel tiles)
- • Saves memory compared to large images
- • Provides modularity and easy level creation
- • Common in platformers (Mario, Metroid, Celeste)
- Layered Static Backgrounds (Non-Parallax): Multiple static layers (foreground, mid-ground, background), but none that scroll or move
- • Used for atmospheric depth
- • Allows artists to organize elements logically (e.g., walls, props, sky)
- Static with Selective Animated Elements: A static background with a few animated components (e.g., flickering lights, moving water)
- • Still considered “static backgrounds” because the world behind gameplay does not scroll
- • Often implemented using sprites placed on top of the static image
- • Good compromise between aesthetics and performance
- Room or Scene Backgrounds (Adventure/Visual Novels): Highly detailed illustrations representing rooms, landscapes, or story locations
- • The player character may not move relative to the background
- • Widely used in visual novel engines and point-and-click adventures
- UI-Integrated Backgrounds: Backgrounds forming part of the user interface or menu system
- • Main menus, inventory screens, pause menus
- • Sometimes blurred or darkened gameplay screens
5.2.2. Technical Considerations
Regardless of art style, implementing static backgrounds follows common technical patterns. In Allegro 5 with C++, the basic steps are as listed here. (For brevity, the header section is omitted from some code examples unless it is required.)
- 1. Loading Background Assets
Static backgrounds are typically stored as:
- • PNG (most common, supports transparency)
- • JPG (smaller file size, used for detailed/nontransparent art)
- • BMP (large, rarely used today)
Using Allegro 5:
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
Best practices:
- • Keep background resolution consistent with your game resolution
- • Compress images appropriately
- • Preload backgrounds during scene initialization to avoid in-game stutter
- 2. Drawing the Background
Static backgrounds should always be drawn before all movable objects.
Basic method:
al_draw_bitmap(background, 0, 0, 0);
For tiled backgrounds:
for (int y = 0; y < SCREEN_H; y += tile_h) {
for (int x = 0; x < SCREEN_W; x += tile_w) {
al_draw_bitmap(tile, x, y, 0);
}
}
- 3. Scaling, Centering, or Fitting Backgrounds
Depending on your display resolution, you may need to:
- • Scale the background to fit
- • Letterbox to maintain aspect ratio
- • Crop and center
Example for scaling:
al_draw_scaled_bitmap(
background, 0, 0, original_w, original_h,
0, 0, SCREEN_W, SCREEN_H, 0);
- 4. Managing Layers
Static backgrounds may consist of multiple layers (foreground, middle ground).
Use separate bitmaps:
al_draw_bitmap(bg_far, 0, 0, 0);
al_draw_bitmap(bg_mid, 0, 0, 0);
al_draw_bitmap(bg_near, 0, 0, 0);
Even without parallax movement, this can create depth and easily accommodate special effects.
- 5. Optimizing for Performance
Because static backgrounds do not change, the following can be used for optimization:
- • Load them once, not every frame
- • Avoid redrawing complex tiles unless necessary
- • Use Allegro’s bitmap flags for speed:
al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
For retro-style pixel art:
al_set_new_bitmap_flags(ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_NEAREST);
Static backgrounds are ideal for levels or scenes where the environment does not need to change or move. They are often used in side-scrolling games, adventure games, and certain RPGs.
Classic games like Super Mario Bros. and The Legend of Zelda use static backgrounds to create immersive worlds without the need for complex animations.
5.2.3. Steps to Take
The next section will show a complete example of implementing a static game background using Allegro in C++. Here are the steps for creating this code:
- 1. Initialize Allegro
Make sure you have Allegro5 installed and properly set up in your project.
- 2. Load the Background Image
Use Allegro functions to load your background image.
- 3. Draw the Background
In your game loop, draw the background image before drawing other game elements.
5.2.4. Code Template and Example
Here’s a simple example template to illustrate the steps shown in the previous section:
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
int main() {
// Initialize Allegro
al_init();
al_init_image_addon();
// Create display
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
// Load background image
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
// Main game loop
while (true) {
// Clear the screen
al_clear_to_color(al_map_rgb(0, 0, 0));
// Draw the background
al_draw_bitmap(background, 0, 0, 0);
// Flip the display
al_flip_display();
// Add your game logic here
// Break the loop if the display is closed
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
ALLEGRO_EVENT event;
al_wait_for_event(event_queue, &event);
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
break;
}
}
// Clean up
al_destroy_bitmap(background);
al_destroy_display(display);
return 0;
}
The main elements from this template will be utilized in the full code example provided next:
- 1. Initialization
Allegro and the image add-on are initialized.
- 2. Loading the Background
The background image is loaded using al_load_bitmap.
- 3. Drawing the Background
The background is drawn in the game loop using al_draw_bitmap.
- 4. End
Check to see if the Esc key is pressed to exit the example.
When you test the code, make sure to replace background.png with the path to your actual background image file if you don’t locate it in the same directory as your executable code (which is where it expects to find the image currently):
Complete Coding Example
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
int main() {
// Initialize Allegro and image addon
al_init();
al_init_image_addon();
al_install_keyboard(); // Still required to use keyboard input
// Create display
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
if (!display) return—1;
// Load background image
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
if (!background) return —1;
// Create event queue and register sources
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_keyboard_event_source());
// Main loop
bool running = true;
while (running) {
// Draw background
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_bitmap(background, 0, 0, 0);
al_flip_display();
// Wait for and process input events
ALLEGRO_EVENT event;
if (al_wait_for_event_timed(event_queue, &event, 0.01)) {
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
} else if (event.type == ALLEGRO_EVENT_KEY_DOWN &&
event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
}
}
}
// Cleanup
al_destroy_event_queue(event_queue);
al_destroy_bitmap(background);
al_destroy_display(display);
return 0;
5.2.5. Questions for Further Study
- 1. Art Style and Composition
- • How does the choice of static background art style affect gameplay, mood, or readability?
- • When should a developer prefer hand-drawn art over photorealistic backgrounds?
- 2. Memory and Optimization
- • What are the trade-offs between using a single large background image vs. many smaller tiles?
- • How does texture resolution affect GPU memory usage?
- 3. Dynamic vs. Static Backgrounds
- • When is a static background appropriate?
- • How does adding parallax or animation change performance and player experience?
- 4. Resolution Independence
- • How can developers design static backgrounds for multiple screen sizes and aspect ratios?
- • What strategies help reduce distortion (stretching, cropping)?
- 5. Pipeline and Tools
- • What tools are best for creating background art (Photoshop, Krita, Aseprite, Blender)?
- • How do level editors or map editors (Tiled, LDtk) integrate static backgrounds into workflow?
- 6. Transitions and Scene Changes
- • How should a game fade, slide, or transition between static backgrounds?
- • What narrative or pacing effects do transitions create?
5.3. Creating and Using Scrolling Backgrounds
Scrolling backgrounds in video games create the illusion of movement and depth, enhancing the visual experience. Here are some key points about scrolling backgrounds.
5.3.1. Types of Background Scrolling
- Horizontal Scrolling: Common in side-scrolling games like Super Mario Bros., where the background moves horizontally as the player progresses.
- Vertical Scrolling: Seen in games like Space Invaders, where the background moves vertically.
- Parallax Scrolling: Uses multiple layers moving at different speeds to create a sense of depth. This technique is often used in platformers and adventure games.
5.3.2. Technical Considerations
- Tile-Based Scrolling: The background is made up of smaller tiles that are drawn and moved as the player navigates the game world.
- Seamless Textures: For continuous scrolling, seamless or tileable textures are used to ensure the background wraps around without visible seams.
- Performance: Efficient scrolling techniques are crucial for maintaining smooth gameplay. This often involves optimizing the drawing process and managing memory effectively.
- Tools and Libraries: Many game development frameworks and libraries—such as MonoGame, Unity, and Allegro—provide built-in support for implementing scrolling backgrounds.
- Artistic Considerations: The design of scrolling backgrounds should complement the game’s theme and enhance the player’s immersion. Artists often create layered backgrounds to achieve a more dynamic and engaging visual effect.
5.3.3. Steps to Take
Creating a scrolling background in a game using Allegro 5 involves a few steps. Here’s a basic guide to help you get started:
- 1. Initialize Allegro
Ensure Allegro and its add-ons are properly initialized.
- 2. Load the Background Image
Load your background image using Allegro functions.
- 3. Implement Scrolling Logic
Update the background’s position to create the scrolling effect.
- 4. Draw the Background
Draw the background at its updated position in the game loop.
5.3.4. Code Template and Example
Here’s a simple example template to illustrate these steps:
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
int main() {
// Initialize Allegro
al_init();
al_init_image_addon();
// Create display
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
// Load background image
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
// Variables for scrolling
float bg_x = 0;
float scroll_speed = 2.0;
// Main game loop
while (true) {
// Update background position
bg_x—= scroll_speed;
if (bg_x <=—al_get_bitmap_width(background)) {
bg_x = 0;
}
// Clear the screen
al_clear_to_color(al_map_rgb(0, 0, 0));
// Draw the background twice for seamless scrolling
al_draw_bitmap(background, bg_x, 0, 0);
al_draw_bitmap(background, bg_x + al_get_bitmap_width(background), 0, 0);
// Flip the display
al_flip_display();
// Add your game logic here
// Break the loop if the display is closed
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
ALLEGRO_EVENT event;
al_wait_for_event(event_queue, &event);
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
break;
}
}
// Clean up
al_destroy_bitmap(background);
al_destroy_display(display);
return 0;
}
The main elements from this template will be utilized in the full code example provided next:
- 1. Initialization
Allegro and the image add-on are initialized.
- 2. Loading the Background
The background image is loaded using al_load_bitmap.
- 3. Scrolling Logic
The background’s x-coordinate (bg_x) is updated to create the scrolling effect. When the background moves completely off-screen, its position is reset.
- 4. Drawing the Background
The background is drawn twice to ensure seamless scrolling.
- 5. End
Check for a key press of the Esc key to end the example.
This is a basic implementation. You can adjust the scroll_speed variable to control the scrolling speed or add more complex logic for different scrolling effects.
Code Example
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
int main() {
al_init();
al_init_image_addon();
al_install_keyboard();
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
if (!display) return—1;
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
if (!background) return—1;
ALLEGRO_EVENT_QUEUE *queue = al_create_event_queue();
al_register_event_source(queue, al_get_display_event_source(display))
al_register_event_source(queue, al_get_keyboard_event_source());
ALLEGRO_TIMER *timer = al_create_timer(1.0 / 60.0); // 60 fps log
al_register_event_source(queue, al_get_timer_event_source(timer));
al_start_timer(timer);
float bg_x = 0.0f;
const float scroll_speed = 2.0f;
bool running = true;
while (running) {
ALLEGRO_EVENT ev;
al_wait_for_event(queue, &ev);
/*——logic tick——*/
if (ev.type == ALLEGRO_EVENT_TIMER) {
bg_x—= scroll_speed;
if (bg_x <=—al_get_bitmap_width(background))
bg_x = 0;
}
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
running = false;
if (ev.type == ALLEGRO_EVENT_KEY_DOWN &&
ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
running = false;
if (ev.type == ALLEGRO_EVENT_TIMER) {
al_clear_to_color(al_map_rgb(0, 0, 0));
// draw two copies for seamless wrap
int bw = al_get_bitmap_width(background);
al_draw_bitmap(background, bg_x, 0, 0);
al_draw_bitmap(background, bg_x + bw, 0, 0);
al_flip_display();
}
}
al_destroy_timer(timer);
al_destroy_event_queue(queue);
al_destroy_bitmap(background);
al_destroy_display(display);
return 0;
}
5.3.5. Questions for Further Study
- 1. How do you add multiple layers for parallax scrolling?
- 2. Can you explain bitmap handling in Allegro 5?
- 3. What are some common performance tips for scrolling backgrounds?
5.4. Creating and Using Dynamic Backgrounds
Dynamic backgrounds in video games add a layer of immersion and visual interest by changing or reacting to in-game events.
5.4.1. Types of Dynamic Backgrounds
- Animated Backgrounds: These include moving elements like clouds, water, or other environmental effects.
- Interactive Backgrounds: They may change based on player actions or game events, such as day-night cycles or weather changes.
- Procedural Backgrounds: Generated algorithmically, these can create unique and varied environments each time the game is played.
5.4.2. Technical Considerations
- Animation: Use sprite sheets or frame-based animations to create dynamic objects on backgrounds.
- Shaders: Utilize shaders for complex visual effects like water reflections or dynamic lighting.
- Event-Driven Changes: Implement background changes triggered by specific game events, such as entering a new area or completing a level.
- Performance Considerations: Dynamic backgrounds can be resource intensive. Optimize performance by managing memory efficiently and minimizing the number of draw calls.
- Tools and Libraries: Many game development frameworks and engines—such as Unity, Unreal Engine, and Allegro—provide built-in support for creating dynamic backgrounds.
- Artistic Considerations: Dynamic backgrounds should enhance the game’s atmosphere and complement the overall art style. They can be used to convey mood, indicate progress, or provide visual feedback to the player.
5.4.3. Steps to Take
Creating a dynamic background for games in C++ with Allegro 5 can add a lot of visual interest to your game. Here’s a basic guide to get you started:
- 1. Initialize Allegro
Ensure Allegro and its add-ons are properly initialized.
- 2. Load the Background Image
Load your background image using Allegro functions.
- 3. Implement Dynamic Elements
Add elements that change over time, such as moving objects or changing colours.
- 4. Draw the Background
In your game loop, draw the background and update the dynamic elements.
- 5. Cleanup
5.4.4. Code Template and Example
Here is a basic template:
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
int main() {
// Initialize Allegro
al_init();
al_init_image_addon();
al_init_primitives_addon();
// Create display
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
// Load background image
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
// Variables for dynamic elements
float star_x = 400, star_y = 300;
float star_speed = 2.0;
// Main game loop
while (true) {
// Update dynamic elements
star_x += star_speed;
if (star_x > 800) {
star_x = 0;
}
// Clear the screen
al_clear_to_color(al_map_rgb(0, 0, 0));
// Draw the background
al_draw_bitmap(background, 0, 0, 0);
// Draw dynamic elements (e.g., a moving star)
al_draw_filled_circle(star_x, star_y, 5, al_map_rgb(255, 255, 0));
// Flip the display
al_flip_display();
// Add your game logic here
// Break the loop if the display is closed
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
ALLEGRO_EVENT event;
al_wait_for_event(event_queue, &event);
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
break;
}
}
// Clean up
al_destroy_bitmap(background);
al_destroy_display(display);
return 0;
}
For movements here, we will use a random number generator to provide the animation for the circle-star object. We use the event timer to trigger the star’s movement and adjust its position based on the randomly generated values to offset the x and y positions.
Code Example
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#include <cstdlib> // for rand()
#include <ctime> // for seeding rand()
int main() {
// Initialize Allegro and addons
al_init();
al_init_image_addon();
al_init_primitives_addon();
al_install_keyboard();
srand(static_cast<unsigned int>(time(nullptr))); // seed RNG
// Create display
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
if (!display) return—1;
// Load background image
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
if (!background) return—1;
// Initial star position
float star_x = 400, star_y = 300;
// Create event queue
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
al_register_event_source(event_queue, al_get_display_event_source
al_register_event_source(event_queue, al_get_keyboard_event_sourc
// Timer for movement updates
ALLEGRO_TIMER *timer = al_create_timer(1.0 / 60.0);
al_register_event_source(event_queue, al_get_timer_event_source(t
al_start_timer(timer);
bool running = true;
while (running) {
ALLEGRO_EVENT event;
al_wait_for_event(event_queue, &event);
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
} else if (event.type == ALLEGRO_EVENT_KEY_DOWN &&
event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
// Here the timer generates a 'move' event 60 times a sec
} else if (event.type == ALLEGRO_EVENT_TIMER) {
// Randomly move the star a little bit
star_x += (rand() % 7—3); // random move between—3 an
star_y += (rand() % 7—3);
// Keep within bounds
if (star_x < 0) star_x = 0;
if (star_x > 800) star_x = 800;
if (star_y < 0) star_y = 0;
if (star_y > 600) star_y = 600;
// Redraw everything
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_bitmap(background, 0, 0, 0);
al_draw_filled_circle(star_x, star_y, 5, al_map_rgb(255,
al_flip_display();
}
}
// Cleanup
// Cleanup
al_destroy_bitmap(background);
al_destroy_timer(timer);
al_destroy_event_queue(event_queue);
al_destroy_display(display);
return 0;
}
5.4.5. Areas for Completion and Enhancement
Animated Sprites, Livening Up the Scene
In the previous example, we used an Allegro graphics primitive, a filled circle, as the dynamic star object moving around on the background. One can easily replace this with a sprite image (or images of other items) with some simple changes to the code, as seen here. We will utilize the image in figure 5.1 for our star sprite.
In this example, our star sprite is much calmer than our previous “nervous” star circles; it merely glides horizontally across our night sky.
Code Example
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
int main() {
// Initialize Allegro
al_init();
al_init_image_addon();
al_init_primitives_addon();
al_install_keyboard();
Figure 5.1: Star sprite image. Image by Walter Ridgewell.
// Create display
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
if (!display) return—1;
// Load background image and sprite
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
ALLEGRO_BITMAP *star_sprite = al_load_bitmap("star.png");
if (!background || !star_sprite) return—1;
// Get star dimensions
int star_w = al_get_bitmap_width(star_sprite);
int star_h = al_get_bitmap_height(star_sprite);
// Create event queue
ALLEGRO_EVENT_QUEUE *queue = al_create_event_queue();
al_register_event_source(queue, al_get_display_event_source(display));
al_register_event_source(queue, al_get_keyboard_event_source());
//Star initial state
float star_x = 400.0f, star_y = 300.0f;
const float speed = 2.0f;
bool running = true;
while (running) {
// Update dynamic elements
star_x += speed;
if (star_x > 800) star_x =—star_w; // wrap at right edge
// Clear the screen
al_clear_to_color(al_map_rgb(0, 0, 0));
// Draw the background
al_draw_bitmap(background, 0, 0, 0);
// Draw sprite instead of filled circle, centered on star_x, star_y
al_draw_bitmap(star_sprite, star_x—star_w / 2, star_y—star_h / 2, 0);
// Flip the display
al_flip_display();
// Wait for an event (non-blocking for display close)
ALLEGRO_EVENT ev;
while (al_get_next_event(queue, &ev)) {
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
running = false;
else if (ev.type == ALLEGRO_EVENT_KEY_DOWN &&
ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
running = false;
}
// Add a short delay for a basic frame rate control
al_rest(0.01);
}
// Clean up
al_destroy_event_queue(queue);
al_destroy_bitmap(star_sprite);
al_destroy_bitmap(background);
al_destroy_display(display);
return 0;
}
Changing Weather Effects for the Scene
So now we have a sprite-based starry sky; let’s go back and add the primitive graphic, the filled circle we had used as a star previously, and make it represent “snow” to create a wintery snow-falling prenight scene.
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#include <cstdlib>
#include <ctime>
struct Snowflake {
float x, y, speed, drift;
};
int main() {
// Initialize Allegro
al_init();
al_init_image_addon();
al_init_primitives_addon();
al_install_keyboard(); //Enable keyboard input
// Create display
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
al_set_window_title(display, "Star Sprite with Snowfall");
// Load background image and sprite
ALLEGRO_BITMAP *background = al_load_bitmap("background.png");
ALLEGRO_BITMAP *star_sprite = al_load_bitmap("star.png");
// Get star dimensions
int star_width = al_get_bitmap_width(star_sprite);
int star_height = al_get_bitmap_height(star_sprite);
// Star motion state
float star_x = 400, star_y = 300;
float star_speed = 2.0f;
// Create event queue and register sources
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_keyboard_event_source()); //Handle ESC
// Initialize RNG
std::srand(static_cast<unsigned int>(std::time(nullptr)));
// Create snowflakes
const int NUM_SNOWFLAKES = 150;
Snowflake snow[NUM_SNOWFLAKES];
for (int i = 0; i < NUM_SNOWFLAKES; ++i) {
snow[i].x = std::rand() % 800;
snow[i].y = std::rand() % 600;
snow[i].speed = 1 + std::rand() % 3;// Fall speed
snow[i].drift = ((std::rand() % 21)—10) / 40.0f;// Gentle horizontal drift
}
// Main game loop
bool running = true;
while (running) {
// Update star position
star_x += star_speed;
if (star_x > 800) star_x =—star_width;
// Update snowflakes
for (int i = 0; i < NUM_SNOWFLAKES; ++i) {
snow[i].y += snow[i].speed;
snow[i].x += snow[i].drift;
if (snow[i].y > 600) {
snow[i].y = 0;
snow[i].x = std::rand() % 800;
}
if (snow[i].x < 0) snow[i].x += 800;
if (snow[i].x > 800) snow[i].x—= 800;
}
// Clear screen
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_bitmap(background, 0, 0, 0);
// Draw the gradient overlay (blue to dusk)
for (int y = 0; y < 600; ++y) {
float t = 1.0f—(float)y / 599; //inverted the gradient to simulate a sunset
ALLEGRO_COLOR gradient_color = al_map_rgba_f(
(1.0f—t) * 1.0f + t * 0.0f,
(1.0f—t) * 0.2f + t * 0.2f,
(1.0f—t) * 0.2f + t * 1.0f,
0.3f
);
al_draw_filled_rectangle(0, y, 800, y + 1, gradient_color);
}
// Draw star and snowflakes
al_draw_bitmap(star_sprite, star_x—star_width / 2, star_y—star_height / 2, 0);
for (int i = 0; i < NUM_SNOWFLAKES; ++i) {
al_draw_filled_circle(snow[i].x, snow[i].y, 2, al_map_rgba(255, 255, 255, 180));
}
// Flip display
al_flip_display();
// Handle events (ESC and close)
ALLEGRO_EVENT event;
while (al_get_next_event(event_queue, &event)) {
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
} else if (event.type == ALLEGRO_EVENT_KEY_DOWN &&
event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
}
}
al_rest(0.01);
}
// Cleanup
al_destroy_event_queue(event_queue);
al_destroy_bitmap(background);
al_destroy_bitmap(star_sprite);
al_destroy_display(display);
return 0;
}
5.4.6. Questions for Further Study
- 1. How can you implement animated sprites in the background?
- 2. Can you explain colour-changing effects?
- 3. What are some tips for optimizing dynamic backgrounds?
5.5. Creating and Using 3D Backgrounds
3D backgrounds in video games add depth and realism, enhancing the overall gaming experience. 3D backgrounds create a more immersive environment, making players feel like they are part of the game world. This is especially important in genres like first-person shooters, RPGs, and adventure games.
5.5.1. Types of 3D Backgrounds
- Static 3D Models: These are prerendered 3D models that do not change during gameplay. They provide a detailed and realistic backdrop.
- Dynamic 3D Environments: These backgrounds can change in real time based on player actions or game events. Examples include changing weather, day-night cycles, and destructible environments.
- Parallax Scrolling: This technique involves multiple layers of 3D backgrounds moving at different speeds to create a sense of depth.
5.5.2. Technical Considerations
- Performance Considerations: Rendering 3D backgrounds can be resource intensive. Optimizing performance involves techniques like level of detail (LOD), culling, and efficient use of shaders.
- Tools and Engines: Modern game engines like Unity, Unreal Engine, and Godot provide robust tools for creating and managing 3D backgrounds. These engines offer features like real-time lighting, physics, and advanced rendering techniques.
- Artistic Considerations: The design of 3D backgrounds should complement the game’s art style and enhance the atmosphere. Artists often use a combination of textures, lighting, and environmental effects to achieve the desired look.
5.5.3. Steps to Take
Creating a 3D background for games in C++ with Allegro 5 is a bit more complex than working with 2D graphics, as Allegro 5 is primarily a 2D graphics library. However, you can achieve 3D effects by integrating Allegro with OpenGL, which Allegro supports.
- 1. Initialize Allegro and OpenGL
Ensure Allegro and OpenGL are properly initialized.
- 2. Set Up OpenGL
Configure OpenGL settings for 3D rendering.
- 3. Load and Render 3D Models
Use OpenGL functions to load and render 3D models.
- 4. Draw the Background
In your game loop, draw the 3D background using OpenGL.
5.5.4. Code Template and Example
This is a basic implementation template. For more complex 3D backgrounds, you can load and render 3D models using libraries like assimp for model loading and more advanced OpenGL techniques:
#include <allegro5/allegro.h>
#include <allegro5/allegro_opengl.h>
#include <GL/gl.h>
#include <GL/glu.h>
void initOpenGL() {
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
gluPerspective(45.0, 800.0 / 600.0, 1.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
}
void draw3DBackground() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// Draw a simple 3D plane (1 quad or a single square in OpenGL terminology) as the background
glBegin(GL_QUADS);
glColor3f(0.0, 1.0, 0.0); // Green color
glVertex3f(-1.0,—1.0,—1.0);
glVertex3f(1.0,—1.0,—1.0);
glVertex3f(1.0, 1.0,—1.0);
glVertex3f(-1.0, 1.0,—1.0);
glEnd();
}
int main() {
// Initialize Allegro
al_init();
al_set_new_display_flags(ALLEGRO_OPENGL);
ALLEGRO_DISPLAY *display = al_create_display(800, 600);
// Initialize OpenGL
initOpenGL();
// Main game loop
while (true) {
// Draw the 3D background
draw3DBackground();
// Flip the display
al_flip_display();
// Add your game logic here
// Break the loop if the display is closed
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
ALLEGRO_EVENT event;
al_wait_for_event(event_queue, &event);
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
break;
}
}
// Clean up
al_destroy_display(display);
return 0;
}
In this example, there are the following key steps:
- 1. Initialization
Allegro and OpenGL are initialized.
- 2. OpenGL Setup
OpenGL settings are configured for 3D rendering.
- 3. Drawing the Background
A simple 3D cube is drawn as the background.
In the next example, we’ll build on the template to create a simple multicolour rotating cube.
Complete Coding Example
#include <allegro5/allegro.h>
#include <allegro5/allegro_opengl.h>
#include <GL/gl.h>
#include <GL/glu.h>
static float rotation_angle = 0.0f;
void initOpenGL(int w, int h) {
glViewport(0, 0, w, h);
glClearColor(0.06f, 0.06f, 0.08f, 1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (double)w / (double)h, 0.1, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void drawCube(float angleDeg) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Camera
gluLookAt(0.0, 0.0, 5.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0);
// Rotate cube
glRotatef(angleDeg, 0.0f, 1.0f, 0.0f);
glRotatef(angleDeg * 0.6f, 1.0f, 0.0f, 0.0f);
// A unit cube centered at origin, side length 2
glBegin(GL_QUADS);
// Front (z = +1)
glColor3f(1, 0, 0); // red
glVertex3f(-1,—1, 1);
glVertex3f( 1,—1, 1);
glVertex3f( 1, 1, 1);
glVertex3f(-1, 1, 1);
// Back (z =—1)
glColor3f(0, 1, 0); // green
glVertex3f( 1,—1,—1);
glVertex3f(-1,—1,—1);
glVertex3f(-1, 1,—1);
glVertex3f( 1, 1,—1);
// Left (x =—1)
glColor3f(0, 0, 1); // blue
glVertex3f(-1,—1,—1);
glVertex3f(-1,—1, 1);
glVertex3f(-1, 1, 1);
glVertex3f(-1, 1,—1);
// Right (x = +1)
glColor3f(1, 1, 0); // yellow
glVertex3f( 1,—1, 1);
glVertex3f( 1,—1,—1);
glVertex3f( 1, 1,—1);
glVertex3f( 1, 1, 1);
// Top (y = +1)
glColor3f(0, 1, 1); // cyan
glVertex3f(-1, 1, 1);
glVertex3f( 1, 1, 1);
glVertex3f( 1, 1,—1);
glVertex3f(-1, 1,—1);
// Bottom (y =—1)
glColor3f(1, 0, 1); // magenta
glVertex3f(-1,—1,—1);
glVertex3f( 1,—1,—1);
glVertex3f( 1,—1, 1);
glVertex3f(-1,—1, 1);
glEnd();
}
int main() {
al_init();
al_install_keyboard();
// Ask Allegro for an OpenGL display with a depth buffer
al_set_new_display_flags(ALLEGRO_OPENGL);
al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 24, ALLEGRO_REQUIRE);
const int W = 800, H = 600;
ALLEGRO_DISPLAY *display = al_create_display(W, H);
if (!display) return—1;
initOpenGL(W, H);
ALLEGRO_EVENT_QUEUE *queue = al_create_event_queue();
al_register_event_source(queue, al_get_display_event_source(display));
al_register_event_source(queue, al_get_keyboard_event_source());
bool running = true;
double last_time = al_get_time();
while (running) {
// Update rotation
double now = al_get_time();
double dt = now—last_time;
last_time = now;
rotation_angle += 60.0f * (float)dt;
if (rotation_angle >= 360.0f) rotation_angle—= 360.0f;
// Draw
drawCube(rotation_angle);
al_flip_display();
// Process events (non-blocking)
ALLEGRO_EVENT ev;
while (al_get_next_event(queue, &ev)) {
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
} else if (ev.type == ALLEGRO_EVENT_KEY_DOWN &&
ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
}
}
}
al_destroy_event_queue(queue);
al_destroy_display(display);
return 0;
}
5.5.5. Questions for Further Study
- 1. How can lighting models (such as ambient, diffuse, and specular lighting) be incorporated to improve the realism of 3D backgrounds?
- 2. What techniques can be used to optimize the performance of complex 3D backgrounds, especially on lower-end hardware?
- 3. How could texture mapping be applied to 3D background geometry to enhance visual detail?
- 4. In what ways can camera movement and perspective changes contribute to immersion when using 3D backgrounds?
- 5. How might modern shader-based rendering (e.g., using GLSL) be integrated with Allegro and OpenGL to create more advanced visual effects?
Exercises, Homework Questions, and Projects
Static Backgrounds (Section 5.2)
- 1. Basic Implementation: Modify the provided static background code to load and display two different static backgrounds for separate game levels.
- 2. Resolution Handling: Write a program that adjusts the static background image to fit varying screen resolutions without stretching or distortion.
- 3. Artistic Integration: Design a static background for a puzzle game level and implement it in Allegro5. Include at least three interactive UI elements overlaid on the background.
- 4. Performance Analysis: Compare the memory usage of static backgrounds using PNG vs. JPEG formats. Document your findings.
Scrolling Backgrounds (Section 5.3)
- 5. Horizontal Scrolling: Create a side-scrolling game prototype where the background scrolls left to right based on player movement.
- 6. Parallax Effect: Implement a parallax-scrolling system with three layers (e.g., sky, mountains, and ground) moving at different speeds.
- 7. Seamless Wrapping: Modify the provided scrolling code to support vertical scrolling in addition to horizontal movement.
- 8. Tile-Based Scrolling: Design a tile-based background system for a top-down RPG map and implement infinite scrolling.
- 9. Optimization Challenge: Reduce the CPU usage of a scrolling background by implementing dirty-rectangle rendering.
Dynamic Backgrounds (Section 5.4)
- 10. Day-Night Cycle: Create a dynamic background that transitions smoothly between day, sunset, and night using colour interpolation.
- 11. Weather System: Simulate rain by animating falling particles over a static background. Use Allegro’s primitives or sprite sheets.
- 12. Interactive Background: Design a background where trees sway when the player character approaches them.
- 13. Procedural Generation: Generate a randomized starfield background using algorithms to place stars dynamically.
- 14. Shader Effects: Integrate a fragment shader to create a water reflection effect on a static lake background.
3D Backgrounds (Section 5.5)
- 15. Basic 3D Scene: Use Allegro5’s OpenGL integration to render a simple 3D landscape with static models (e.g., hills, trees).
- 16. Dynamic Lighting: Implement a 3D background with a moving light source (e.g., a rotating sun or torch).
- 17. Destructible Environment: Create a 3D scene where parts of the background (e.g., walls) can be destroyed by player actions.
- 18. Performance Optimization: Apply level of detail (LOD) techniques to a 3D background with distant objects rendered at lower resolution.
Mixed Concepts
- 19. Hybrid Backgrounds: Combine a static foreground with a parallax-scrolling middle ground and a dynamic sky background.
- 20. Game Jam Project: Build a minigame where the background changes type (static to scrolling to dynamic) based on player progress.
- 21. Debugging Task: Fix a provided broken scrolling background code that suffers from flickering and memory leaks.
Theory and Analysis
- 22. Case Study: Analyze how Hollow Knight uses parallax scrolling to create depth. Write a report comparing it to the Allegro5 implementation.
- 23. Colour Theory: Design a mood chart linking background colour palettes to specific game atmospheres (e.g., horror, adventure).
- 24. Performance Trade-Offs: Debate the advantages of procedural generation vs. prerendered backgrounds in open-world games.
Advanced Projects
- 25. Particle System: Integrate a particle engine into a dynamic background to simulate fire, smoke, or magic effects.
- 26. Audio-Reactive Background: Modify a dynamic background to pulse or change colours in sync with background music.
- 27. Multiplayer Sync: Implement a dynamic background (e.g., weather) that synchronizes across two networked players.
- 28. Memory Management: Develop a texture streaming system for large 3D backgrounds to load assets dynamically during gameplay.
Creative Challenges
- 29. Art Pipeline: Create a toolchain (e.g., Python script) to batch-convert and optimize background assets for Allegro5.
- 30. Final Project: Build a complete game level with all four background types (static, scrolling, dynamic, 3D) and a write-up explaining your design choices.