使用Unity3d做异形窗口
项目马上上线,因为之前的登录器是使用VS2010的MFC做的,在很多电脑上会提示缺失mfcXXXX.dll,中间找寻这种解决方案,最后确定将vcredist2010_x86和我的程序打包到安装包里面,每次安装的时候默认先安装vcredist2010_x86。
由此,经常被杀毒软件阻止,而且还有x64 or x86的区别。
同时,甲方想要一个精灵,类似于QQ宠物,于是PL决定使用精灵模型+异形窗口做一个桌面宠物。于是,异形窗口成了此物的基础。
首先,我们需要了解的是,异形窗口是什么。简单来说,即将窗口形状变成由一张不规则(一般由透明色决定)图片所决定的形状。
然后,需要知道的是窗口的形状是由什么决定。在MSDN上我发现,普通的窗口形状由一个简单的Rectangular Region决定。经查询之后,要实现异形窗口主要要靠这几个函数:
a. 创建一个矩形区域
//The CreateRectRgn function creates a rectangular region.
HRGN CreateRectRgn( int nLeftRect, // x-coordinate of upper-left corner int nTopRect, // y-coordinate of upper-left corner int nRightRect, // x-coordinate of lower-right corner int nBottomRect // y-coordinate of lower-right corner );
b. 结合n个矩形区域
//The CombineRgn function combines two regions and stores the result in a third region. //The two regions are combined according to the specified mode. int CombineRgn( HRGN hrgnDest, // handle to destination region HRGN hrgnSrc1, // handle to source region HRGN hrgnSrc2, // handle to source region int fnCombineMode // region combining mode );
c. 将窗口显示出来
//The SetWindowRgn function sets the window region of a window. //The window region determines the area within the window where the system permits drawing. //The system does not display any portion of a window that lies outside of the window region int SetWindowRgn( HWND hWnd, // handle to window HRGN hRgn, // handle to region BOOL bRedraw // window redraw option );
d. 去掉窗口标题栏(主要窗口截图只能截到窗口边框内)
//This function changes an attribute of the specified window. //SetWindowLong also sets a 32-bit (LONG) value at the specified offset into the extra window memory of a window.
LONG SetWindowLong( HWND hWnd, int nIndex, LONG dwNewLong );
最后,在unity里面调用user32.dll 和 Gdi32.dll 两个动态链接库,结合unity界面截图,得到最后的处理结果。贴上部分处理代码:
1 /// <summary> 2 /// use the screen shot image to cut the window. 3 /// </summary> 4 private void updateWindow() { 5 System.IntPtr hRgn = WinAPI.CreateRectRgn(0, 0, 0, 0); 6 7 Debug.Log(" width: " + winTex2d.width + " height: " + winTex2d.height); 8 9 for (int h = 0; h < winTex2d.height; ++h) {//int h = tex2d.height - 1; h > tex2d.height/2; --h 10 //Debug.Log(tex2d.GetPixel(h, h)); 11 int w = 0; 12 do { 13 while (w < winTex2d.width && (winTex2d.GetPixel(w, winTex2d.height - h) == Color.clear 14 || winTex2d.GetPixel(w, h) == Color.black 15 )) { 16 ++w; 17 //Debug.Log(tex2d.GetPixel(w, h)); 18 } 19 int iLeftX = w; 20 while (w < winTex2d.width && (winTex2d.GetPixel(w, winTex2d.height - h) != Color.clear 21 //|| tex2d.GetPixel(w, h) != Color.black 22 )) { 23 ++w; 24 //Debug.Log(tex2d.GetPixel(w, h)); 25 } 26 //Debug.Log("ileftX: " + iLeftX + "w: " + w + h); 27 28 WinAPI.CombineRgn(hRgn, hRgn, WinAPI.CreateRectRgn(iLeftX, h, w, h + 1), 2); 29 }//end do{} 30 while (w < winTex2d.width); 31 }//end for(h); 32 33 Debug.Log("setWindowRgn: " + WinAPI.SetWindowRgn(WinAPI.GetActiveWindow(), hRgn, true)); 34 }//end updateWindow();
PS:这种处理方式当图形透明区域特别多的时候,会出现整个处理过程有些卡顿,后期可能要加入线程进行处理。
感谢您的阅读,如果您有好的建议,望不吝赐教!