I am trying to animate a bitmap in a window. I am able to load the bitmap in random locations multiple times, but I would like to have the previous instance of the bitmap erased each redraw, as I would like it to be a smoother animation instead of thousands of bitmaps on the screen.

I also do not quite understand how to change the random drawing of the bitmap below into a smooth linear animation.

if (rect.right > 0)
    {
        _sleep(50);
        x = rand() % (rect.right - rect.left);
        y = rand() % (rect.bottom - rect.top);
        DrawBitmap(global_hdc, "asteroid.bmp", x, y);
    }
#include <windows.h>
#include <winuser.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define APPTITLE "Game Loop"

//function prototypes
LRESULT CALLBACK WinProc(HWND,UINT,WPARAM,LPARAM);
ATOM MyRegisterClass(HINSTANCE);
BOOL InitInstance(HINSTANCE,int);
void DrawBitmap(HDC,char*,int,int);
void Game_Init();
void Game_Run();
void Game_End();


//local variables
HWND global_hwnd;
HDC global_hdc;


//the window event callback function
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    global_hwnd = hWnd;
    global_hdc = GetDC(hWnd);

    switch (message) 
    {
        case WM_DESTROY:
            Game_End();
            PostQuitMessage(0);
            break;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

//helper function to set up the window properties
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    //create the window class structure
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX); 

    //fill the struct with info
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = (WNDPROC)WinProc;
    wc.cbClsExtra     = 0;
    wc.cbWndExtra     = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = APPTITLE;
    wc.hIconSm       = NULL;

    //set up the window with the class info
    return RegisterClassEx(&wc);

}

//helper function to create the window and refresh it
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;

    //create a new window
    hWnd = CreateWindow(
       APPTITLE,              //window class
       APPTITLE,              //title bar
       WS_OVERLAPPEDWINDOW,   //window style
       CW_USEDEFAULT,         //x position of window
       CW_USEDEFAULT,         //y position of window
       500,                   //width of the window
       400,                   //height of the window
       NULL,                  //parent window
       NULL,                  //menu
       hInstance,             //application instance
       NULL);                 //window parameters

    //was there an error creating the window?
    if (!hWnd)
      return FALSE;

    //display the window
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}


//entry point for a Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR     lpCmdLine,
                   int       nCmdShow)
{
    int done = 0;
    MSG msg;

    // register the class
    MyRegisterClass(hInstance);


    // initialize application
    if (!InitInstance (hInstance, nCmdShow)) 
        return FALSE;

    //initialize the game
    Game_Init();

    // main message loop
    while (!done)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
        {
            //look for quit message
            if (msg.message == WM_QUIT)
                done = 1;

            //decode and pass messages on to WndProc
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        
        //process game loop
        Game_Run();
    }

    return msg.wParam;
}

void DrawBitmap(HDC hdcDest, char *filename, int x, int y)
{
    HBITMAP image;
    BITMAP bm;
    HDC hdcMem;

    //load the bitmap image
    image = (HBITMAP)LoadImage(0,"asteroid.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);

    //read the bitmap's properties
    GetObject(image, sizeof(BITMAP), &bm);

    //create a device context for the bitmap
    hdcMem = CreateCompatibleDC(global_hdc);
    SelectObject(hdcMem, image);

    //draw the bitmap to the window (bit block transfer)
    BitBlt( 
        global_hdc,              //destination device context
        x, y,                    //x,y location on destination
        bm.bmWidth, bm.bmHeight, //width,height of source bitmap
        hdcMem,                  //source bitmap device context
        0, 0,                    //start x,y on source bitmap
        SRCCOPY);                //blit method

    //delete the device context and bitmap
    DeleteDC(hdcMem);
    DeleteObject((HBITMAP)image);
}


void Game_Init()
{
    //initialize the game...
    //load bitmaps, meshes, textures, sounds, etc.

    srand(time(NULL));
}

void Game_Run()
{
    //this is called once every frame
    //do not include your own loop here!
    
    int x = 0, y = 0;
    RECT rect;
    GetClientRect(global_hwnd, &rect);

    if (rect.right > 0)
    {
        
        x = rand() % (rect.right - rect.left);
        y = rand() % (rect.bottom - rect.top);
        DrawBitmap(global_hdc, "asteroid.bmp", x, y);
    }
}

void Game_End()
{

}

part one
Don't load "asteroid.bmp" from file every time you call DrawBitmap(). That's a huge slowdown. Load the bitmap first, (say in Game_Init()) and have DrawBitmap() take a handle to the bitmap to draw instead of a filename.

Don't forget to use DeleteObject() before terminating to get rid of the bitmap.

part two
To animate the asteroid moving about smoothly, you need:

1. a direction to travel. This should be (x, y) pixel offsets. Use floats, and round to the nearest integer before mapping the image. (If you use only integers your asteroid is limited in how fast or slow it travels.)

2. (possibly) a time to travel that distance. In the original asteroids game, the rocks only changed direction when impacted (hit by another rock or by the ship's weapon fire), so the time the asteroid traveled its direction was infinite, hence unnecessary to bother tracking. However, if you want it randomly changing direction, when time is up calculate a new direction to travel.

Next, you need to calculate the (x, y) direction --randomly if so desired-- then begin moving the asteroid's position by adding the (x, y) offset each frame. All you need to track is the previous asteroid location. You know the bitmap's width and height, so use FillRect() to blacken the previous image, update the asteroid's location, then BitBlt() the asteroid to its new location. Consider how you want it to react when it reaches the edge of the client area. Do you want it to bounce off? Do you want it to disappear? Wrap around?

Keep in mind you only calculate a new (x, y) direction when certain events occur, not every frame. You do, however, move the bitmap by adding the direction to the bitmap's current location each frame.

part three
Use double-buffering. Look up SetPixelFormat() for more information. Once you have updated everything for the frame, then SwapBuffers().

Hope this helps.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.