游戏背景通常是不断移动的,比较简单的做法是用一张静态的图片,分隔成两部分,先移动右边部分,再把左边部分接到右边部分末尾,如此循环即可。原图如下:
上面是一张带有刻度的图片,里面包含四种不同的背景颜色,我们现在要实现取第一张图,并且让它不断的从右向左移动。
具体实现代码如下:
include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Bounce") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Bouncing Ball"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0)){
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
void DrawRollStretch(HDC hdc,HBITMAP h_bit_map_,HDC hdc_src,HBITMAP hmapnull,int x_times,int y_times,int id)
{
const int ROW = 1;
const int COL = 4;
static int x_separate_ = 0; //分隔出需要移动的部分
BITMAP bm;
int width,height;
int x_speed = 1;
int x_pos=0,y_pos=0;
SelectObject(hdc_src,hmapnull);
GetObject(h_bit_map_,sizeof(BITMAP),&bm);
width=bm.bmWidth/ROW; //256
height=bm.bmHeight/COL; //192
SelectObject(hdc_src,h_bit_map_);
//右边部分
StretchBlt(hdc,
x_pos,y_pos, //当前位置
(width-x_separate_)*x_times,height*y_times, // x_times,y_times代表横向、纵向缩放倍数
hdc_src,
x_separate_,id*height, // 右边部分的坐标
width-x_separate_,height, // 右边部分的宽高
SRCCOPY);
//左边部分
StretchBlt(hdc,
x_pos+(width-x_separate_)*x_times,y_pos,
x_separate_*x_times,height*y_times,
hdc_src,
0,id*height,
x_separate_,height,
SRCCOPY);
x_separate_+=x_speed;
x_separate_%=width;
if(x_separate_<0){
x_separate_=width;
}
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInstance ;
static HBITMAP h_bit_map_;
HDC hdc_src_;
HBITMAP hmapnull_;
HDC hdc;
PAINTSTRUCT ps;
switch (iMsg){
case WM_CREATE:
hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
h_bit_map_ = LoadBitmap(hInstance,TEXT("MAP_SKY"));
SetTimer(hwnd,ID_TIMER,40,NULL);
return 0 ;
case WM_PAINT:
hdc = BeginPaint(hwnd,&ps) ;
hdc_src_ = CreateCompatibleDC(hdc);
hmapnull_=CreateCompatibleBitmap(hdc,256,192);
DrawRollStretch(hdc,h_bit_map_,hdc_src_,hmapnull_,2,2,0);
EndPaint(hwnd,&ps);
DeleteObject(hdc_src_);
DeleteObject(hmapnull_);
return 0 ;
case WM_TIMER:
InvalidateRect(hwnd,NULL,FALSE);
return 0;
case WM_DESTROY:
KillTimer(hwnd,ID_TIMER);
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
windows GDI非常复杂,上面虽然把重要代码作了注解,但是还是有些潜在的问题没有讲,比如CreateCompatibleDC、CreateCompatibleBitmap创建以后,要及时删除,不然内存会泄漏得相当快。也就是说在使用windows API编程时,不只是要注意C/C++的内存泄漏,还要注意windows API对象是否合理的清除。