WinAPI moving bitmap problem.

Hello, im trying to make a simple sprite of a cop (badly drawn in paint) to move around, and as it's my first time with moving bitmap, i "binded" it to left mouse button.

Problem is, program generates no error, and i spent ages looking at it and just cannot see what's wrong.

Also i surfed net long and there is absolutely no good WinAPI tutorials (not talking about something that explains Bitmaps fully) but im getting to it.


Big thanks to anyone to find problem !


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <windows.h>

WORD nypd_x;
WORD nypd_y = 100;
const WORD ID_TIMER = 1;



HDC hdc, hdcNowy;
HBITMAP hbmObraz, hbmOld;
BITMAP bmInfo;
RECT rcNypd;

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

        SetTimer (hwnd, ID_TIMER, 1, NULL);
        if (SetTimer (hwnd, ID_TIMER, 100, NULL) == 0)
            MessageBox (hwnd, "Timer error.", "Error!", MB_ICONSTOP);
        hdc = GetDC( hwnd );
        hbmObraz =( HBITMAP ) LoadImage( NULL, "nypd_idle.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
        hdcNowy = CreateCompatibleDC( hdc );
        SelectObject( hdcNowy, hbmObraz );
        hbmOld =( HBITMAP ) SelectObject( hdcNowy, hbmObraz );
        GetObject( hbmObraz, sizeof( bmInfo ), & bmInfo );
        BitBlt( hdc, 100, nypd_y, bmInfo.bmWidth, bmInfo.bmHeight, hdcNowy, 0, 0, SRCCOPY );
        ReleaseDC ( hwnd, hdc );
        SelectObject( hdcNowy, hbmOld );
        DeleteDC( hdcNowy );


    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_DESTROY:
            KillTimer (hwnd, ID_TIMER);
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        case WM_LBUTTONDOWN:
            nypd_y =+ 1;
            break;
        case WM_TIMER:
            hdc = GetDC (hwnd);
            BitBlt( hdc, 100, nypd_y, bmInfo.bmWidth, bmInfo.bmHeight, hdcNowy, 0, 0, SRCCOPY );
            ReleaseDC ( hwnd, hdc );

            break;


        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}



The sprite:

http://puu.sh/2yYWj
Last edited on
Call GDI functions like BitBlt in WM_PAINT message.
I can't seem to get this right.
The WM_PAINT and WM_TIMER i don't seem to set those properly.
There is a ton of mistakes beside this one too, propably, but hell.

Anyone got idea what's wrong ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <windows.h>

using namespace std;

WORD nypd_x;
WORD nypd_y = 100;
const WORD ID_TIMER = 1;



HDC hdc, hdcNowy;
HBITMAP hbmObraz, hbmOld;
BITMAP bmInfo;
HWND g_hwnd;
RECT rcNypd;



/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

        g_hwnd = hwnd;
        SetTimer (hwnd, ID_TIMER, 1, NULL);
        if (SetTimer (hwnd, ID_TIMER, 100, NULL) == 0)
            MessageBox (hwnd, "Timer error.", "Error!", MB_ICONSTOP);
        hdc = GetDC( hwnd );
        hbmObraz =( HBITMAP ) LoadImage( NULL, "nypd_idle.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
        hdcNowy = CreateCompatibleDC( hdc );
        SelectObject( hdcNowy, hbmObraz );
        hbmOld =( HBITMAP ) SelectObject( hdcNowy, hbmObraz );
        GetObject( hbmObraz, sizeof( bmInfo ), & bmInfo );
        ReleaseDC ( hwnd, hdc );
        SelectObject( hdcNowy, hbmOld );
        DeleteDC( hdcNowy );


    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    bool lbutton_down = false;
    switch (message)                  /* handle the messages */
    {
        case WM_DESTROY:
            KillTimer (hwnd, ID_TIMER);
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        case WM_LBUTTONDOWN:
            lbutton_down = true;
            nypd_y =+ 1;
            break;
        case WM_LBUTTONUP:
            lbutton_down = false;
            break;
        case WM_TIMER:
            hdc = GetDC (hwnd);
            if(lbutton_down == true)
            {
                MessageBox(hwnd, "nigga", "nigga", MB_OK);
                InvalidateRect(hwnd, & rcNypd, false );
                UpdateWindow(hwnd);
                ReleaseDC ( hwnd, hdc );
            }
            break;
        case WM_PAINT:
{
            PAINTSTRUCT ps; // deklaracja struktury
            HDC hdc = BeginPaint( hwnd, & ps );
            hdc = GetDC (hwnd);
            SetRect( & rcNypd, 5, 5, 5 + bmInfo.bmWidth, 5 + bmInfo.bmHeight );
            BitBlt( hdc, 100, nypd_y, bmInfo.bmWidth, bmInfo.bmHeight, hdcNowy, 0, 0, SRCCOPY );
            EndPaint( hwnd, & ps ); // zwalniamy hdc
            break;
}
break;


        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}
You don't tell us what the problem is.

"It's not working" doesn't help us.

What behavior are you expecting, and what behavior are you getting?
Sorry, but i explained in post above, that i do not know how to combine WM_PAINT and WM_TIMER at the same time.
Errr... you don't.

WM_PAINT happens when the screen needs to be redrawn.
WM_TIMER happens when a timer you set triggers.

You wouldn't do both of them at the same time because they're 2 different things.

Not sure I understand the problem =x
Timers are used so that program executes things all the time, right ?
Get player input, simulate physics, render and stuff.
I dont know how can i "render" the bitmap with WM_PAINT when WM_TIMER is called.
Timers are used so that program executes things all the time, right ?


No. They can be used that way, but I'd say that's a misuse of them.

It sounds like what you want is a game loop. (Though if you're writing a game... using WinAPI is not what I'd recommend... see below).

Your program already executes things "all the time". The problem is you are handing off control to the "GetMessage" function which means you will only be running when there's a message. To get a proper game loop... do not use GetMessage. Instead use the non-blocking PeekMessage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// in WinMain after all your setup is done
while( game_is_running )
{
    // process all incoming messages
    while( PeekMessage( &msg, wnd, 0, 0, PM_REMOVE ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    // the above loop will happen virtually instantaneously... meaning you can put game
    //  logic and other stuff here
    doGameLogic();

    // at this point, you can draw to the screen ( you don't need to use WM_PAINT for that,
    //  you can just use GetDC()/ReleaseDC() )
    // but you also do not want the game to run too fast.  So you'll need to regulate the
    //  framerate to run at 60 FPS (or whatever framerate you want).  This can be done by
    //  keeping an eye on the time (GetTickCount()) and Sleep()ing until 1/60th of a second
    //  has passed.
    waitForNextFrame();
}


This is how games are typically done.



But as I mentioned before... using WinAPI is not my first recommendation for this. I strongly suggest you get a lib designed for this... like SFML. For a few reasons:

1) SFML is crossplatform
2) SFML is much, much, much easier
3) SFML gets much better performance
4) SFML can do a lot more out of the box (loading pngs, alpha blending, transparency, flipping, rotating, color tinting, audio output, gamepad input, etc)
5) SFML can vsync and/or has built in framerate regulation, so you can just say setFrameLimit(60); and it will automatically limit your program to run at 60 FPS without you having to worry about the details.


I have a step-by-step guide to getting SFML set up in VS2012 here:
http://cplusplus.com/forum/beginner/95295/#msg511542

I strongly recommend it if you want to make your life easier.
Thank you, i will check SFML out, i already tried it but couldn't get it to work. I will see what can i do.
Topic archived. No new replies allowed.