我的家園

          我的家園

          本系列文章由zhmxy555編寫,轉載請注明出處。 http://blog.csdn.net/zhmxy555/article/details/7385605

          作者:毛星云    郵箱: happylifemxy@qq.com    歡迎郵件交流編程心得



          “排序貼圖”是源自于物體遠近呈現的一種貼圖概念?;貞浳覀冎肮P記的貼圖思想,先進行距離比較遠的物體的貼圖操作,然后再進行近距離物體的貼圖操作,一旦定出貼圖的順序之后就無法再改變了。


          然而這樣的作法在畫面上物體會彼此遮掩的情況下就會不適用。也許會出現后面的物體反而遮住了前面的物體的這種不協調的畫面。為了避免這種因為貼圖順序而固定而產生的錯誤畫面,必須在每一次窗口重新顯示時動態地重新決定畫面上每一個物體的貼圖順序。

          那么,如何動態決定貼圖順序呢?我們可以采用排序的方式。



          為了演示排序如何運用在貼圖中,我們舉一個例子。假設現在有10只要進行貼圖的小牛圖案,先把它存在一個數組之中,從2D平面的遠近角度來看,Y軸坐標比較小的是比較遠的物體。如果我們以小牛的Y軸坐標(要排序的值被我們稱作鍵值)來對小牛數組由小到大進行排序,最后會使得Y軸坐標小的小牛排在數組的前面,而進行畫面貼圖時則由數組由小到大一個個進行處理,這樣便可實現“遠的物體先貼圖“的目的了。



          這里我們使用氣泡排序(Bubble Sort)作為我們的排序法,因為此方法有程序代碼簡單,排序效率中等,屬于穩定(stable)排序法的特點。這里的穩定排序法的特性,會使得Y軸坐標相同的物體,不必再去考慮它X坐標上的排序。



          下面我們貼出以C/C++寫出的氣泡排序法的代碼,對”pop[ ]“數組的各數據成員的Y值為鍵值來排序,輸出的參數為”n“表示要排序的數組大小:


          	void BubSort(int n)
          	{
          		int i,j;
          		bool f;
          		pop tmp;
          	
          		for(i=0;i<n-1;i++)
          		{
          			f = false;
          			for(j=0;j<n-i-1;j++)
          			{
          				if(pop[j+1].y < pop[j].y)
          				{       //進行數組元素的交換
          					tmp = pop[j+1];
          					pop[j+1] = pop[j];
          					pop[j] = tmp;
          					f = true;
          				}
          			}
          			if(!f)                 //無交換操作則結束循環
          				break;
          		}
          	}


          各種排序法為C/C++中比較核心的知識點,還不太熟悉的朋友,可以參看各種C++的教程進行深入學習。在這里我就不多做介紹了。



          接下來,我們來利用一個范例來演示氣泡排序法在畫面上貼圖的運用,讓動畫能呈現出接近真實的遠近層次效果。這個范例比較有趣,會產生多只恐龍隨機跑動,每次進行畫面貼圖前先完成排序操作,并對恐龍跑動進行貼圖坐標的修正,呈現出比較順暢真實的動畫來。


          廢話這里就不多說了,直接上已經詳細注釋的代碼(這回的代碼量就有些大了,不過我專門注釋得更詳細了些,其實它比之前的代碼還更好懂):


          #include "stdafx.h"
          #include <stdio.h>
          
          //定義一個結構體
          struct dragon        //定義dragon結構,代表畫面上的恐龍,其結構成員x和y為貼圖坐標,dir為目前恐龍的移動方向
          {
          	int x,y;
          	int dir;
          };
          
          //定義常數
          const int draNum = 12;  //定義常數draNum,代表程序在畫面上要出現的恐龍數目,在此設定為12個
          //全局變量定義
          HINSTANCE hInst;
          HBITMAP draPic[4],bg;   //draPic[4]儲存恐龍上下左右移動的連續圖案,bg為存儲背景圖
          HDC		hdc,mdc,bufdc;
          HWND	hWnd;
          DWORD	tPre,tNow;
          int		picNum;
          dragon  dra[draNum];   //按照draNum的值建立數組dra[],產生畫面上出現的恐龍。
          
          
          //全局函數聲明
          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;
          	}
          	GetMessage(&msg,NULL,NULL,NULL);//初始化msg
          	//消息循環
              while( msg.message!=WM_QUIT )
              {
                  if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) )
                  {
                      TranslateMessage( &msg );
                      DispatchMessage( &msg );
                  }
          		else
          		{
          			tNow = GetTickCount();
          			if(tNow-tPre >= 100)
          				MyPaint(hdc);
          		}
              }
          
          	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);
          }
          
          //****初始化函數*************************************
          // 加載位圖并設定各初始值
          BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
          {
          	HBITMAP bmp;
          	hInst = hInstance;
          	int i;
          
          	hWnd = CreateWindow("canvas", "繪圖窗口" , WS_OVERLAPPEDWINDOW,
          		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
          
          	if (!hWnd)
          	{
          		return FALSE;
          	}
          
          	MoveWindow(hWnd,10,10,640,480,true);
          	ShowWindow(hWnd, nCmdShow);
          	UpdateWindow(hWnd);
          
          	hdc = GetDC(hWnd);
          	mdc = CreateCompatibleDC(hdc);
          	bufdc = CreateCompatibleDC(hdc);
          
          	bmp = CreateCompatibleBitmap(hdc,640,480);  //建立一個空位圖并放入mdc中
          	SelectObject(mdc,bmp);
          
          
          	//加載各張恐龍跑動圖及背景圖,這里以0,1,2,3來代表恐龍的上,下,左,右移動
          	draPic[0] = (HBITMAP)LoadImage(NULL,"dra0.bmp",IMAGE_BITMAP,528,188,LR_LOADFROMFILE);
          	draPic[1] = (HBITMAP)LoadImage(NULL,"dra1.bmp",IMAGE_BITMAP,544,164,LR_LOADFROMFILE);
          	draPic[2] = (HBITMAP)LoadImage(NULL,"dra2.bmp",IMAGE_BITMAP,760,198,LR_LOADFROMFILE);
          	draPic[3] = (HBITMAP)LoadImage(NULL,"dra3.bmp",IMAGE_BITMAP,760,198,LR_LOADFROMFILE);
          	bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,480,LR_LOADFROMFILE);
          
          
          	//設定所有恐龍初始的貼圖坐標都為(200,200),初始的移動方向都為向左。
          	for(i=0;i<draNum;i++)
          	{
          		dra[i].dir = 3;    //起始方向
          		dra[i].x = 200;	   //貼圖的起始X坐標
          		dra[i].y = 200;    //貼圖的起始Y坐標
          	}
          
          	MyPaint(hdc);
          
          	return TRUE;
          }
          
          //氣泡排序
          void BubSort(int n)
          {
          	int i,j;
          	bool f;
          	dragon tmp;
          
          	for(i=0;i<n-1;i++)
          	{
          		f = false;
          		for(j=0;j<n-i-1;j++)
          		{
          			if(dra[j+1].y < dra[j].y)
          			{
          				tmp = dra[j+1];
          				dra[j+1] = dra[j];
          				dra[j] = tmp;
          				f = true;
          			}
          		}
          		if(!f)
          			break;
          	}
          }
          
          //****自定義繪圖函數*********************************
          // 1.對窗口中跑動的恐龍進行排序貼圖
          // 2.恐龍貼圖坐標修正
          void MyPaint(HDC hdc)
          {
          	int w,h,i;
          
          	if(picNum == 8)
          		picNum = 0;
          
          	//在mdc中先貼上背景圖
          	SelectObject(bufdc,bg);
          	BitBlt(mdc,0,0,640,480,bufdc,0,0,SRCCOPY);
          
          	BubSort(draNum);    //貼上恐龍圖之前調用BubSort()函數進行排序
          
          
          	//下面這個for循環,按照目前恐龍的移動方向dra[i].dir,選取對應的位圖到bufdc中,并設定截切的大小。每一張要在窗口上出現的恐龍圖案依次先在mdc上進行透明貼圖的操作。
          	for(i=0;i<draNum;i++)
          	{
          		SelectObject(bufdc,draPic[dra[i].dir]);
          		switch(dra[i].dir)
          		{
          			case 0:
          				w = 66;
          				h = 94;
          				break;
          			case 1:
          				w = 68;
          				h = 82;
          				break;
          			case 2:
          				w = 95;
          				h = 99;
          				break;
          			case 3:
          				w = 95;
          				h = 99;
          				break;
          		}
          		BitBlt(mdc,dra[i].x,dra[i].y,w,h,bufdc,picNum*w,h,SRCAND);
          		BitBlt(mdc,dra[i].x,dra[i].y,w,h,bufdc,picNum*w,0,SRCPAINT);
          	}
          
          	//將最后畫面顯示在窗口中
          	BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);
          
          	tPre = GetTickCount();         //記錄此次繪圖時間
          	picNum++;
          
          
          	//下面這個for循環,決定每一只恐龍下一次的移動方向及貼圖坐標
          	for(i=0;i<draNum;i++)
          	{
          		switch(rand()%4)          //隨機數除以4的余數來決定下次移動方向,余數0,1,2,3分別代表上,下,左,右
          		{
          			//case 0里面的代碼,按照目前的移動方向來修正因為各個方向圖案尺寸不一致而產生的貼圖坐標誤差,加入恐龍每次移動的單位量(上,下,左,右每次20個單位)而得到下次新的貼圖坐標
          			case 0:					     //上
          				switch(dra[i].dir)
          				{
          					case 0:	
          						dra[i].y -= 20;
          						break;
          					case 1:
          						dra[i].x += 2;
          						dra[i].y -= 31;
          						break;
          					case 2:	
          						dra[i].x += 14;
          						dra[i].y -= 20;
          						break;
          					case 3:
          						dra[i].x += 14;
          						dra[i].y -= 20;
          						break;
          				}
          				//在計算出新的貼圖坐標之后,還需判斷此新的坐標會不會使得恐龍貼圖超出窗口邊界,若超出,則將該方向上的坐標設定為剛好等于臨界值
          				if(dra[i].y < 0)
          					dra[i].y = 0;
          				dra[i].dir = 0;
          				break;
          			//其他方向按照和上面相同的方法計算
          			case 1:				     	//下
          				switch(dra[i].dir)
          				{
          					case 0:
          						dra[i].x -= 2;
          						dra[i].y += 31;
          						break;
          					case 1:
          						dra[i].y += 20;
          						break;
          					case 2:
          						dra[i].x += 15;
          						dra[i].y += 29;
          						break;
          					case 3:
          						dra[i].x += 15;
          						dra[i].y += 29;
          						break;
          				}
          
          				if(dra[i].y > 370)
          					dra[i].y = 370;
          				dra[i].dir = 1;
          				break;
          			case 2:				    	//左
          				switch(dra[i].dir)
          				{
          					case 0:
          						dra[i].x -= 34;
          						break;
          					case 1:
          						dra[i].x -= 34;
          						dra[i].y -= 9;
          						break;
          					case 2:
          						dra[i].x -= 20;
          						break;
          					case 3:
          						dra[i].x -= 20;
          						break;
          				}
          				if(dra[i].x < 0)
          					dra[i].x = 0;
          				dra[i].dir = 2;
          				break;
          			case 3:				    	//右
          				switch(dra[i].dir)
          				{
          					case 0:
          						dra[i].x += 6;
          						break;
          					case 1:
          						dra[i].x += 6;
          						dra[i].y -= 10;
          						break;
          					case 2:
          						dra[i].x += 20;
          						break;
          					case 3:
          						dra[i].x += 20;
          						break;
          				}
          				if(dra[i].x > 535)
          					dra[i].x = 535;
          				dra[i].dir = 3;
          				break;
          		}
          	}
          }
          
          //****消息處理函數***********************************
          LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
          {
          	switch (message)
          	{
          		int i;
          
          		case WM_DESTROY:					//窗口結束消息,撤銷各種DC  
          			DeleteDC(mdc);
          			DeleteDC(bufdc);
          			for(i=0;i<4;i++)
          				DeleteObject(draPic[i]);
          			DeleteObject(bg);
          			ReleaseDC(hWnd,hdc);
          			PostQuitMessage(0);
          			break;
          		default:							//其他消息
          			return DefWindowProc(hWnd, message, wParam, lParam);
             }
             return 0;
          }


          程序運行結果如下:





          從圖中可以看出,由于貼圖前進行了排序操作,因此使得恐龍彼此之間沒有錯誤的遮掩。


          我們也可以按自己的喜好,通過設定程序中最前面定義的draNum常數值來改變畫面上出現的恐龍數目。





          筆記十一到這里就結束了。


          本節源代碼請點擊這里下載:   【Visual C++】Code_Note_11


          感謝一直支持【Visual C++】游戲開發筆記系列專欄的朋友們,也請大家繼續關注我的博客,我一有空就會把自己的學習心得,覺得比較好的知識點寫出來和大家一起分享。

          精通游戲開發的路還很長很長,非常希望能和大家一起交流,共同學習和進步。

          大家看過后覺得有啟發的話可以頂一下這篇文章,讓更多的朋友有機會看到它。也希望大家可以多留言來和我探討編程相關的問題。

          最后,謝謝大家一直的支持~~~


          The end






          作者:zhmxy555 發表于2012-3-23 3:20:34 原文鏈接
          閱讀:15955 評論:86 查看評論

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 达日县| 佛坪县| 句容市| 雅江县| 信丰县| 武邑县| 门头沟区| 河池市| 托克逊县| 三台县| 白朗县| 青龙| 阳泉市| 聊城市| 灌阳县| 贡觉县| 天等县| 阳江市| 正安县| 铜山县| 山阴县| 冀州市| 宜良县| 江北区| 杭锦旗| 汉阴县| 舞阳县| 兴和县| 宁陵县| 阜康市| 海林市| 聂荣县| 巴马| 黄梅县| 汝州市| 岑巩县| 房产| 洪泽县| 东辽县| 邵武市| 平武县|