windows通过StretchBlt实现动态背景移动

本文介绍了如何使用Windows GDI通过StretchBlt函数来实现动态背景移动的效果,特别是针对游戏背景的处理。文中提到将静态图片分为两部分,通过不断移动实现背景的连续移动,并提供了具体的代码示例。同时,也强调了在使用Windows API编程时需要注意内存泄漏和对象清理问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

游戏背景通常是不断移动的,比较简单的做法是用一张静态的图片,分隔成两部分,先移动右边部分,再把左边部分接到右边部分末尾,如此循环即可。原图如下:


上面是一张带有刻度的图片,里面包含四种不同的背景颜色,我们现在要实现取第一张图,并且让它不断的从右向左移动。

具体实现代码如下:

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对象是否合理的清除。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值