C Language Animation: Meteor Shower

Using C language to create a meteor shower animation, the effect is as follows:

Core Steps:

1. First, set up the drawing window with a size of 640×480, and set the center origin coordinates to (320, 240):

2. Randomly generate stars and meteors within the window. The colors of the stars are random to enhance visual appeal, while meteors are set to white;

3. Set the speed offsets for stars and meteors, ensuring that stars move slowly (or remain stationary), while meteors fly faster. If any star goes beyond the window boundaries, it should be reinitialized;

4. Continuously update the x and y coordinates of the meteors and stars to make them move.

The number of stars is 50, and the number of meteors is 200, defined using macros:

#define MAX_STAR_NUMS 50 // Number of stars
#define MAX_METEOR_NUMS 200 // Number of meteors

The randomly generated stars and meteors must ensure they fall within the window and have randomness. Since the center origin coordinates are (320, 240), the following calculations for the x and y coordinates will ensure that the stars and meteors fall within the window:

#define RAND_COORDINATE_X ((rand() % 640) - (640 / 2))
#define RAND_COORDINATE_Y ((rand() % 480) - (480 / 2))

Required header files, structures, and related macros:

#include "stdio.h"
#include "graphics.h"
#include "time.h"
#include "conio.h"
#include "windows.h"

/********** All calculation parameters for stars and meteors **********/
#define MAX_STAR_NUMS 50 // Number of stars
#define MAX_METEOR_NUMS 200 // Number of meteors

// Speed offsets and adjustments for star movement
#define STAR_SPEED_OFFSET 2
#define STAR_SPEED_ADJ 1

// Speed offsets and adjustments for meteor flight
#define METEOR_SPEED_OFFSET 16
#define METEOR_SPEED_ADJ 5

// Drawing window dimensions
#define GRAPH_WIDTH 640 // Width of the drawing window
#define GRAPH_HEIGHT 480 // Height of the drawing window

// Random color
#define RANDOM_COLOUR (rand() % 256)
// Meteor color (white)
#define METEOR_COLOUR (RGB(255, 255, 255))

// Center point of the drawing window
#define CENTER_CIRCLE_X 320 // Origin X coordinate
#define CENTER_CIRCLE_Y 240 // Origin Y coordinate

// Generate random coordinates
#define RAND_COORDINATE_X (((rand()) % (GRAPH_WIDTH)) - ((GRAPH_WIDTH) >> 1))
#define RAND_COORDINATE_Y (((rand()) % (GRAPH_HEIGHT)) - ((GRAPH_HEIGHT) >> 1))

// Planet structure: stars and meteors share the same structure
typedef struct StarMeteor
{
    int x; // x coordinate
    int y; // y coordinate
    int speed; // movement speed
    int colour; // color
} TPlanet;

TPlanet gtStar[MAX_STAR_NUMS] = {0};
TPlanet gtMeteor[MAX_METEOR_NUMS] = {0};

Initialize stars and meteors:

// Initialize stars
void InitStar(TPlanet *ptStar)
{
    if (!ptStar) return;

    ptStar->x = RAND_COORDINATE_X;
    ptStar->y = RAND_COORDINATE_Y;
    ptStar->speed = (rand() % STAR_SPEED_OFFSET) + STAR_SPEED_ADJ;
    // Random star color to enhance visual appeal
    ptStar->colour = RGB(RANDOM_COLOUR, RANDOM_COLOUR, RANDOM_COLOUR);
}

// Initialize meteors
void InitMeteor(TPlanet* ptMeteor)
{
    if (!ptMeteor) return;

    ptMeteor->x = RAND_COORDINATE_X;
    ptMeteor->y = RAND_COORDINATE_Y;
    ptMeteor->speed = (rand() % METEOR_SPEED_OFFSET) + METEOR_SPEED_ADJ;
    // Set meteor color to white
    ptMeteor->colour = METEOR_COLOUR;
}
// Initialize all planets: stars + meteors
void InitAllPlanet()
{
    for (int i = 0; i < MAX_STAR_NUMS; i++)
    {
        InitStar(&gtStar[i]);
    }

    for (int i = 0; i < MAX_METEOR_NUMS; i++)
    {
        InitMeteor(&gtMeteor[i]);
    }
}

Real-time update of stars and meteors to make them move:

// Draw and update star positions
void UpdateStar()
{
    for (int i = 0; i < MAX_STAR_NUMS; i++)
    {
        // Draw small circles as stars, refresh movement position
        gtStar[i].x += gtStar[i].speed;
        setfillcolor(gtStar[i].colour);
        solidcircle(gtStar[i].x, gtStar[i].y, 2);

        // Reinitialize stars that go out of bounds
        if (GRAPH_WIDTH < gtStar[i].x)
        {
            InitStar(&gtStar[i]);
        }
    }
}

// Draw and update meteor positions
void UpdateMeteor()
{
    for (int i = 0; i < MAX_METEOR_NUMS; i++)
    {
        // Move meteors and update positions
        gtMeteor[i].x = gtMeteor[i].x + (gtMeteor[i].speed / 2);
        gtMeteor[i].y = gtMeteor[i].y + (gtMeteor[i].speed / 2);

        // Draw small circles as meteors
        setfillcolor(gtMeteor[i].colour);
        solidcircle(gtMeteor[i].x, gtMeteor[i].y, 2);

        // Reinitialize meteors that go out of bounds
        if (GRAPH_WIDTH < gtMeteor[i].x || GRAPH_HEIGHT < gtMeteor[i].y)
        {
            InitMeteor(&gtMeteor[i]);
        }
    }
}

Main function:

int main()
{ 
    initgraph(GRAPH_WIDTH, GRAPH_HEIGHT);
    setorigin(CENTER_CIRCLE_X, CENTER_CIRCLE_Y);

    srand((unsigned int)time(NULL));

    // Initialize stars and meteors
    InitAllPlanet();

    BeginBatchDraw();
    for (;;)
    {
        cleardevice();

        // Draw and move stars and meteors
        UpdateStar();
        UpdateMeteor();

        // Delay 5ms for refresh
        Sleep(5);

        FlushBatchDraw();
    }
    EndBatchDraw();

    closegraph();

    return 0;
}

Debug environment: Windows Visual Studio 2022

The above code is quite simple; it randomly generates coordinates and uses the EasyX graphics library to draw solid small circles as stars and meteors, and finally updates the screen in real-time. The running effect of this example is average; if some background images are loaded to match and switch, the effect will be better. The key is to learn this drawing approach; scenes with snowflakes falling from the sky are also implemented using this principle, carefully designing the offsets, coordinate positions, and randomness calculations to make the effect more realistic.

Leave a Comment