(Visual C++)游戏开发笔记之六——游戏画面绘图(三)透明特效的制作方法

来源:岁月联盟 编辑:exp 时间:2012-04-07

从这节开始我们来讲解制作一般2D游戏画面经常要使用到的绘图特效。

笔记六我们主要介绍“透明”特效的制作方法。


透明效果


由于所有的图文件都是以矩形来储存的,我们也许会需要把一张怪兽图片贴到窗口的背景图上,而这种情况下如果直接进行贴图,结果如下图:

  /


 

这似乎不是我们想要的结果。

 


为了得到透明效果,我们需要运用到BitBlt()贴图函数以及其参数Raster的值来将图片中不必要的部分去掉(又称去背),使得图中的主题可以与背景完美融合。

 


制作透明效果有很多种方法,但是基本上都是利用贴图时不同的Raster运算,通过转换而产生相同的透明效果。在这里先来介绍一种透明运算的方法。

 


我们以图中的恐龙为例子,首先准备一张位图,如下图。

  /


 

图中的左边的图是要去背并贴到背景上的前景图。右边的黑白图称为“屏蔽图”,在透明的过程中会用到它。要把去背的位图与屏蔽图合并成同一张图,透明的时候再按照需要来进行裁切。可以把它分成两张图,但是这样程序必须运行两次图文件加载的操作。

 


有了屏蔽图就可以利用贴图函数来产生透明效果了,所需的贴图步骤如下:

<1>将屏蔽图与背景图做"AND"运算,Raster值为SRCAND,贴到目的地DC中。

<2>将前景图与背景图做"OR"运算,Raster值为SRCPAINT,贴到目的地DC中。

 


为什么经过上面两个操作就能产生透明的效果呢?看下图就理解了:

  /


 

下面具体说明上面两个步骤所产生的图点色彩的变化。

 


1.屏蔽图与背景图做"AND"运算

<1>屏蔽图中的黑色部分与背景图做"AND"运算:

 
<2>屏蔽图中的白色部分与背景图做"AND"运算:

进过这一运算所产生的结果如下图

 
 
2.前景图与背景图做"OR"运算
 
<1>前景图中的彩色部分与图第一步得到的“黑色恐龙”图做"OR"运算:

<2>前景图中的黑色部分与第一步得到的“黑色恐龙”图做"OR"运算:

经过这一运算后所显示的画面就是所需的透明图了,如下图所示:

 
 
下面我们来看看实现上述透明贴图效果的源代码
  

 

[cpp]
#include "stdafx.h"  
//全局变量声明  
HINSTANCE hInst; 
HBITMAP bg,dra;        //声明两个位图对象,分别存储背景图与前景恐龙图  
HDC  mdc;       //声明一个内存DC"mdc",用来暂存位图  
//全局函数声明  
ATOM     MyRegisterClass(HINSTANCE hInstance); 
BOOL     InitInstance(HINSTANCE, int); 
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM); 
void     MyPaint(HDC hdc); 
////****Winmain函数,程序入口点函数**************************************  
int APIENTRY WinMain(HINSTANCE hInstance, 
                     HINSTANCE hPrevInstance, 
                     LPSTR     lpCmdLine, 
                     int       nCmdShow) 

MSG msg; 
MyRegisterClass(hInstance); 
if (!InitInstance (hInstance, nCmdShow))  

return FALSE; 

//消息循环  
while (GetMessage(&msg, NULL, 0, 0))  

TranslateMessage(&msg); 
DispatchMessage(&msg); 

return msg.wParam; 

//****设计一个窗口类,类似填空题,使用窗口结构体*************************  
ATOM MyRegisterClass(HINSTANCE hInstance) 

WNDCLASSEX wcex; 
wcex.cbSize = sizeof(WNDCLASSEX);  
wcex.style   = CS_HREDRAW | CS_VREDRAW; 
wcex.lpfnWndProc    = (WNDPROC)WndProc; 
wcex.cbClsExtra  = 0; 
wcex.cbWndExtra  = 0; 
wcex.hInstance   = hInstance; 
wcex.hIcon   = NULL; 
wcex.hCursor     = NULL; 
wcex.hCursor     = LoadCursor(NULL, IDC_ARROW); 
wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); 
wcex.lpszMenuName   = NULL; 
wcex.lpszClassName  = "canvas"; 
wcex.hIconSm     = NULL; 
return RegisterClassEx(&wcex); 

//****初始化函数*************************************  
// 1.建立与窗口DC兼容的内存DC  
// 2.从文件加载背景图与恐龙图  
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 

HWND hWnd; 
HDC hdc; 
hInst = hInstance; 
hWnd = CreateWindow("canvas", "绘图窗口" , WS_OVERLAPPEDWINDOW, 
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 
if (!hWnd) 

return FALSE; 

MoveWindow(hWnd,10,10,600,450,true); 
ShowWindow(hWnd, nCmdShow); 
UpdateWindow(hWnd); 
hdc = GetDC(hWnd);                     //获得窗口DC  
mdc = CreateCompatibleDC(hdc);           //创建与窗口兼容的内存DC(mdc)  
bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,600,450,LR_LOADFROMFILE);  
//J加载背景图到bg中  
dra = (HBITMAP)LoadImage(NULL,"dra.bmp",IMAGE_BITMAP,170,99,LR_LOADFROMFILE);  
//加载恐龙图到dra中  
MyPaint(hdc); 
ReleaseDC(hWnd,hdc); 
return TRUE; 

//****自定义绘图函数*********************************  
//透明贴图  
void MyPaint(HDC hdc) 

SelectObject(mdc,bg); 
BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY);    //先将背景图贴到显示窗口中  
SelectObject(mdc,dra);                      //选用恐龙图到"mdc"中  
BitBlt(hdc,280,320,85,99,mdc,85,0,SRCAND);//进行制作贴图的第一步骤,即将屏蔽图与背景图做"AND"运算,屏蔽图在整张恐龙图中,最左上角起始位置点得坐标为(85,0),BitBlt()函数中最后一个Raster参数值设置为SRCAND。  
BitBlt(hdc,280,320,85,99,mdc,0,0,SRCPAINT);//进行制作透明贴图的第二步骤,即将前景图与背景图做"OR"运算,前景图在整张恐龙图中,最左上角起始位置的坐标为(0,0),BitBlt()函数最后一个参数值设置为SRCPAINT。  

//****消息处理函数**********************************  
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 

PAINTSTRUCT ps; 
HDC hdc; 
switch (message) 

case WM_PAINT:   //窗口重绘消息  
hdc = BeginPaint(hWnd, &ps); 
MyPaint(hdc); 
EndPaint(hWnd, &ps); 
break; 
case WM_DESTROY:     //窗口结束消息  
DeleteDC(mdc); 
DeleteObject(bg); 
DeleteObject(dra); 
PostQuitMessage(0); 
break; 
default:     //其他消息  
return DefWindowProc(hWnd, message, wParam, lParam); 
   } 
   return 0; 

#include "stdafx.h"
//全局变量声明
HINSTANCE hInst;
HBITMAP bg,dra;        //声明两个位图对象,分别存储背景图与前景恐龙图
HDC  mdc;       //声明一个内存DC"mdc",用来暂存位图
//全局函数声明
ATOM  MyRegisterClass(HINSTANCE hInstance);
BOOL  InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void  MyPaint(HDC hdc);
////****Winmain函数,程序入口点函数**************************************
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
MSG msg;
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
//消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//****设计一个窗口类,类似填空题,使用窗口结构体*************************
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style  = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra  = 0;
wcex.cbWndExtra  = 0;
wcex.hInstance  = hInstance;
wcex.hIcon  = NULL;
wcex.hCursor  = NULL;
wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "canvas";
wcex.hIconSm  = NULL;
return RegisterClassEx(&wcex);
}
//****初始化函数*************************************
// 1.建立与窗口DC兼容的内存DC
// 2.从文件加载背景图与恐龙图
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
HDC hdc;
hInst = hInstance;
hWnd = CreateWindow("canvas", "绘图窗口" , WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
MoveWindow(hWnd,10,10,600,450,true);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
hdc = GetDC(hWnd);                     //获得窗口DC
mdc = CreateCompatibleDC(hdc);           //创建与窗口兼容的内存DC(mdc)
bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,600,450,LR_LOADFROMFILE);
//J加载背景图到bg中
dra = (HBITMAP)LoadImage(NULL,"dra.bmp",IMAGE_BITMAP,170,99,LR_LOADFROMFILE);
//加载恐龙图到dra中
MyPaint(hdc);
ReleaseDC(hWnd,hdc);
return TRUE;
}
//****自定义绘图函数*********************************
//透明贴图
void MyPaint(HDC hdc)
{
SelectObject(mdc,bg);
BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY);    //先将背景图贴到显示窗口中
SelectObject(mdc,dra);                      //选用恐龙图到"mdc"中
BitBlt(hdc,280,320,85,99,mdc,85,0,SRCAND);//进行制作贴图的第一步骤,即将屏蔽图与背景图做"AND"运算,屏蔽图在整张恐龙图中,最左上角起始位置点得坐标为(85,0),BitBlt()函数中最后一个Raster参数值设置为SRCAND。
BitBlt(hdc,280,320,85,99,mdc,0,0,SRCPAINT);//进行制作透明贴图的第二步骤,即将前景图与背景图做"OR"运算,前景图在整张恐龙图中,最左上角起始位置的坐标为(0,0),BitBlt()函数最后一个参数值设置为SRCPAINT。
}
//****消息处理函数**********************************
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:  //窗口重绘消息
hdc = BeginPaint(hWnd, &ps);
MyPaint(hdc);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:  //窗口结束消息
DeleteDC(mdc);
DeleteObject(bg);
DeleteObject(dra);
PostQuitMessage(0);
break;
default:  //其他消息
return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

 

 


最后程序的运行结果为:

  /


 

通过BitBlt()贴图函数及Raster运算值的设定,很简单地就做出了想要的透明效果,这种方法在设计2D游戏的一些画面内容时使用相当频繁。


最后我说明一个非常关键的问题(多谢yao050421103 的提醒),而这点由恰恰依赖于美工设计师们。我们在准备位图资源的时候,前景图部分绝对不能包含背景图的颜色,否则,就不会得到我们预期的结果。


还要指出的一个地方是,前景图的需要还原为背景色的部分一定要为黑色(多谢infoworld的指出)。


笔记六到这里就结束了。

本节源代码请点击这里下载:【Visual C++】Code_Note_6http://download.csdn.net/detail/zhmxy555/4175236 


请大家继续关注【Visual C++】游戏开发笔记系列。

非常希望能与大家一起交流,共同学习和进步。

最后,谢谢大家的支持~~~

 

 

摘自  枫落★流年