Skip to main content

Practical Game Programming: 5. Programming Backgrounds for Video Games

Practical Game Programming
5. Programming Backgrounds for Video Games
  • Show the following:

    Annotations
    Resources
  • Adjust appearance:

    Font
    Font style
    Color Scheme
    Light
    Dark
    Annotation contrast
    Low
    High
    Margins
  • Search within:
    • My Notes + Comments
    • Notifications
    • Privacy
  • Project HomePractical Game Programming
  • Learn more about Manifold

Notes

table of contents
  1. Cover
  2. Acknowledgements
  3. 1. Introduction
    1. 1.1. Introduction to Video Games and Game Programming
    2. 1.2. History of Video Games
    3. 1.3. Types of Video Games
    4. 1.4. Road Map of Game Development
    5. 1.5. Set Up Game Development IDE
    6. 1.6. The Allegro 5 Game Library
      1. Exercises, Homework Questions, and Projects
  4. 2. Essentials of Game Programming with Allegro
    1. 2.1. Common Structure of a C/C++ Program with Allegro 5
    2. 2.2. Handling User Input
    3. 2.3. Collision Detection
    4. 2.4. Adding Sound Effects to Games
    5. 2.5. Timers and Game Timing
    6. 2.6. Using Files for Games
      1. Exercises, Homework Questions, and Projects
  5. 3. Using Graphics in Games
    1. 3.1. Bitmap Fundamentals
    2. 3.2. Bitmaps Creation and Use in Allegro 5
    3. 3.3. Utilizing Bitmaps in a Program
    4. 3.4. Scaling and Rotating Bitmaps
    5. 3.5. Displaying a Section of a Bitmap and the Concept of a Viewport
    6. 3.6. Advanced Graphics Techniques in Allegro 5
    7. 3.7. How Transformations Help with Resolution Independence
    8. 3.8. Antialiasing and Texture Smoothing
    9. 3.9. Shaders
    10. 3.10. Using Premultiplied Alpha in Allegro 5
      1. Exercises, Homework Questions, and Projects
  6. 4. Programming Sprites in Allegro 5
    1. 4.1. The Fundamentals
    2. 4.2. Additional Sprite Handling Routines
    3. 4.3. Allegro Blending Function and Sprites
    4. 4.4. Advanced Sprite Use in Allegro 5
      1. Exercises, Homework Questions, and Projects
  7. 5. Programming Backgrounds for Video Games
    1. 5.1. Types of Backgrounds
    2. 5.2. Creating and Using Static Backgrounds
    3. 5.3. Creating and Using Scrolling Backgrounds
    4. 5.4. Creating and Using Dynamic Backgrounds
    5. 5.5. Creating and Using 3D Backgrounds
      1. Exercises, Homework Questions, and Projects
  8. 6. Game Design and Development Fundamentals
    1. 6.1. Conceive an Amazing Game
    2. 6.2. Transforming a Billion-Dollar Idea into a Detailed Game Design
    3. 6.3. Implement the Game
      1. Exercises, Homework Questions, and Projects
  9. 7. Advanced Topics in Practical Game Programming
    1. 7.1. Multithreading
    2. 7.2. Custom Shaders
    3. 7.3. Networking for Multiplayer Games
    4. 7.4. AI Programming
    5. 7.5. Optimization Techniques
    6. 7.6. Custom GUI Systems
    7. 7.7. Advanced Audio
      1. Exercises, Homework Questions, and Projects
  10. 8. Game Development Projects
    1. 8.1. Action Game: Space Shooter
      1. Exercises, Homework Questions, and Projects
    2. 8.2. Platform Game: Tank War
      1. Exercises, Homework Questions, and Projects
    3. 8.3. Adventure Game
      1. Exercises, Homework Questions, and Projects
    4. 8.4. Arcade Game
      1. Exercises, Homework Questions, and Projects
    5. 8.5. Board Game
      1. Exercises, Homework Questions, and Projects
    6. 8.6. Emulator Game
      1. Exercises, Homework Questions, and Projects
    7. 8.7. Puzzle Game
      1. Exercises, Homework Questions, and Projects
    8. 8.8. Role-Playing Game
      1. Exercises, Homework Questions, and Projects
    9. 8.9. Sports Game
      1. Exercises, Homework Questions, and Projects
    10. 8.10. Strategy Game
      1. Exercises, Homework Questions, and Projects
    11. 8.11. Utilities Game
      1. Exercises, Homework Questions, and Projects
  11. References and Resources

Chapter 5. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 1. Initialize Allegro

    Make sure you have Allegro5 installed and properly set up in your project.

  2. 2. Load the Background Image

    Use Allegro functions to load your background image.

  3. 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. 1. Initialization

    Allegro and the image add-on are initialized.

  2. 2. Loading the Background

    The background image is loaded using al_load_bitmap.

  3. 3. Drawing the Background

    The background is drawn in the game loop using al_draw_bitmap.

  4. 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. 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. 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. 3. Dynamic vs. Static Backgrounds
    • • When is a static background appropriate?
    • • How does adding parallax or animation change performance and player experience?
  4. 4. Resolution Independence
    • • How can developers design static backgrounds for multiple screen sizes and aspect ratios?
    • • What strategies help reduce distortion (stretching, cropping)?
  5. 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. 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. 1. Initialize Allegro

    Ensure Allegro and its add-ons are properly initialized.

  2. 2. Load the Background Image

    Load your background image using Allegro functions.

  3. 3. Implement Scrolling Logic

    Update the background’s position to create the scrolling effect.

  4. 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. 1. Initialization

    Allegro and the image add-on are initialized.

  2. 2. Loading the Background

    The background image is loaded using al_load_bitmap.

  3. 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. 4. Drawing the Background

    The background is drawn twice to ensure seamless scrolling.

  5. 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. 1. How do you add multiple layers for parallax scrolling?
  2. 2. Can you explain bitmap handling in Allegro 5?
  3. 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. 1. Initialize Allegro

    Ensure Allegro and its add-ons are properly initialized.

  2. 2. Load the Background Image

    Load your background image using Allegro functions.

  3. 3. Implement Dynamic Elements

    Add elements that change over time, such as moving objects or changing colours.

  4. 4. Draw the Background

    In your game loop, draw the background and update the dynamic elements.

  5. 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 is an edited photograph of a Christmas tree topper star with the background removed.

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. 1. How can you implement animated sprites in the background?
  2. 2. Can you explain colour-changing effects?
  3. 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. 1. Initialize Allegro and OpenGL

    Ensure Allegro and OpenGL are properly initialized.

  2. 2. Set Up OpenGL

    Configure OpenGL settings for 3D rendering.

  3. 3. Load and Render 3D Models

    Use OpenGL functions to load and render 3D models.

  4. 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. 1. Initialization

    Allegro and OpenGL are initialized.

  2. 2. OpenGL Setup

    OpenGL settings are configured for 3D rendering.

  3. 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. 1. How can lighting models (such as ambient, diffuse, and specular lighting) be incorporated to improve the realism of 3D backgrounds?
  2. 2. What techniques can be used to optimize the performance of complex 3D backgrounds, especially on lower-end hardware?
  3. 3. How could texture mapping be applied to 3D background geometry to enhance visual detail?
  4. 4. In what ways can camera movement and perspective changes contribute to immersion when using 3D backgrounds?
  5. 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. 1. Basic Implementation: Modify the provided static background code to load and display two different static backgrounds for separate game levels.
  2. 2. Resolution Handling: Write a program that adjusts the static background image to fit varying screen resolutions without stretching or distortion.
  3. 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. 4. Performance Analysis: Compare the memory usage of static backgrounds using PNG vs. JPEG formats. Document your findings.

Scrolling Backgrounds (Section 5.3)

  1. 5. Horizontal Scrolling: Create a side-scrolling game prototype where the background scrolls left to right based on player movement.
  2. 6. Parallax Effect: Implement a parallax-scrolling system with three layers (e.g., sky, mountains, and ground) moving at different speeds.
  3. 7. Seamless Wrapping: Modify the provided scrolling code to support vertical scrolling in addition to horizontal movement.
  4. 8. Tile-Based Scrolling: Design a tile-based background system for a top-down RPG map and implement infinite scrolling.
  5. 9. Optimization Challenge: Reduce the CPU usage of a scrolling background by implementing dirty-rectangle rendering.

Dynamic Backgrounds (Section 5.4)

  1. 10. Day-Night Cycle: Create a dynamic background that transitions smoothly between day, sunset, and night using colour interpolation.
  2. 11. Weather System: Simulate rain by animating falling particles over a static background. Use Allegro’s primitives or sprite sheets.
  3. 12. Interactive Background: Design a background where trees sway when the player character approaches them.
  4. 13. Procedural Generation: Generate a randomized starfield background using algorithms to place stars dynamically.
  5. 14. Shader Effects: Integrate a fragment shader to create a water reflection effect on a static lake background.

3D Backgrounds (Section 5.5)

  1. 15. Basic 3D Scene: Use Allegro5’s OpenGL integration to render a simple 3D landscape with static models (e.g., hills, trees).
  2. 16. Dynamic Lighting: Implement a 3D background with a moving light source (e.g., a rotating sun or torch).
  3. 17. Destructible Environment: Create a 3D scene where parts of the background (e.g., walls) can be destroyed by player actions.
  4. 18. Performance Optimization: Apply level of detail (LOD) techniques to a 3D background with distant objects rendered at lower resolution.

Mixed Concepts

  1. 19. Hybrid Backgrounds: Combine a static foreground with a parallax-scrolling middle ground and a dynamic sky background.
  2. 20. Game Jam Project: Build a minigame where the background changes type (static to scrolling to dynamic) based on player progress.
  3. 21. Debugging Task: Fix a provided broken scrolling background code that suffers from flickering and memory leaks.

Theory and Analysis

  1. 22. Case Study: Analyze how Hollow Knight uses parallax scrolling to create depth. Write a report comparing it to the Allegro5 implementation.
  2. 23. Colour Theory: Design a mood chart linking background colour palettes to specific game atmospheres (e.g., horror, adventure).
  3. 24. Performance Trade-Offs: Debate the advantages of procedural generation vs. prerendered backgrounds in open-world games.

Advanced Projects

  1. 25. Particle System: Integrate a particle engine into a dynamic background to simulate fire, smoke, or magic effects.
  2. 26. Audio-Reactive Background: Modify a dynamic background to pulse or change colours in sync with background music.
  3. 27. Multiplayer Sync: Implement a dynamic background (e.g., weather) that synchronizes across two networked players.
  4. 28. Memory Management: Develop a texture streaming system for large 3D backgrounds to load assets dynamically during gameplay.

Creative Challenges

  1. 29. Art Pipeline: Create a toolchain (e.g., Python script) to batch-convert and optimize background assets for Allegro5.
  2. 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.

Annotate

Next Chapter
6. Game Design and Development Fundamentals
PreviousNext
This work is licensed under a Creative Commons License (CC BY-NC-SA 4.0), except where otherwise noted. This license allows users to copy and redistribute the material in any medium or format and to remix, transform, and build upon the material as long as the original source is properly credited, the work is not used for commercial purposes, and the new creation is licensed under the same terms.
Powered by Manifold Scholarship. Learn more at
Opens in new tab or windowmanifoldapp.org