我的家園

          我的家園

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

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



          “排序貼圖”是源自于物體遠近呈現(xiàn)的一種貼圖概念。回憶我們之前筆記的貼圖思想,先進行距離比較遠的物體的貼圖操作,然后再進行近距離物體的貼圖操作,一旦定出貼圖的順序之后就無法再改變了。


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

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



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



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



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


          	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)
          				{       //進行數(shù)組元素的交換
          					tmp = pop[j+1];
          					pop[j+1] = pop[j];
          					pop[j] = tmp;
          					f = true;
          				}
          			}
          			if(!f)                 //無交換操作則結(jié)束循環(huán)
          				break;
          		}
          	}


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



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


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


          #include "stdafx.h"
          #include <stdio.h>
          
          //定義一個結(jié)構(gòu)體
          struct dragon        //定義dragon結(jié)構(gòu),代表畫面上的恐龍,其結(jié)構(gòu)成員x和y為貼圖坐標,dir為目前恐龍的移動方向
          {
          	int x,y;
          	int dir;
          };
          
          //定義常數(shù)
          const int draNum = 12;  //定義常數(shù)draNum,代表程序在畫面上要出現(xiàn)的恐龍數(shù)目,在此設(shè)定為12個
          //全局變量定義
          HINSTANCE hInst;
          HBITMAP draPic[4],bg;   //draPic[4]儲存恐龍上下左右移動的連續(xù)圖案,bg為存儲背景圖
          HDC		hdc,mdc,bufdc;
          HWND	hWnd;
          DWORD	tPre,tNow;
          int		picNum;
          dragon  dra[draNum];   //按照draNum的值建立數(shù)組dra[],產(chǎn)生畫面上出現(xiàn)的恐龍。
          
          
          //全局函數(shù)聲明
          ATOM				MyRegisterClass(HINSTANCE hInstance);
          BOOL				InitInstance(HINSTANCE, int);
          LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
          void				MyPaint(HDC hdc);
          
          //****WinMain函數(shù),程序入口點函數(shù)**************************************
          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
          	//消息循環(huán)
              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;
          }
          
          //****設(shè)計一個窗口類,類似填空題,使用窗口結(jié)構(gòu)體*************************
          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);
          }
          
          //****初始化函數(shù)*************************************
          // 加載位圖并設(shè)定各初始值
          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);
          
          
          	//設(shè)定所有恐龍初始的貼圖坐標都為(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;
          	}
          }
          
          //****自定義繪圖函數(shù)*********************************
          // 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);    //貼上恐龍圖之前調(diào)用BubSort()函數(shù)進行排序
          
          
          	//下面這個for循環(huán),按照目前恐龍的移動方向dra[i].dir,選取對應(yīng)的位圖到bufdc中,并設(shè)定截切的大小。每一張要在窗口上出現(xiàn)的恐龍圖案依次先在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循環(huán),決定每一只恐龍下一次的移動方向及貼圖坐標
          	for(i=0;i<draNum;i++)
          	{
          		switch(rand()%4)          //隨機數(shù)除以4的余數(shù)來決定下次移動方向,余數(shù)0,1,2,3分別代表上,下,左,右
          		{
          			//case 0里面的代碼,按照目前的移動方向來修正因為各個方向圖案尺寸不一致而產(chǎn)生的貼圖坐標誤差,加入恐龍每次移動的單位量(上,下,左,右每次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;
          				}
          				//在計算出新的貼圖坐標之后,還需判斷此新的坐標會不會使得恐龍貼圖超出窗口邊界,若超出,則將該方向上的坐標設(shè)定為剛好等于臨界值
          				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;
          		}
          	}
          }
          
          //****消息處理函數(shù)***********************************
          LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
          {
          	switch (message)
          	{
          		int i;
          
          		case WM_DESTROY:					//窗口結(jié)束消息,撤銷各種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;
          }


          程序運行結(jié)果如下:





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


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





          筆記十一到這里就結(jié)束了。


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


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

          精通游戲開發(fā)的路還很長很長,非常希望能和大家一起交流,共同學(xué)習(xí)和進步。

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

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


          The end






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

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 织金县| 深州市| 达拉特旗| 盐池县| 渝中区| 铜山县| 永定县| 台湾省| 耒阳市| 水城县| 略阳县| 彩票| 涿州市| 桦川县| 太和县| 辽阳市| 锡林浩特市| 原平市| 花莲县| 庄河市| 陕西省| 阿拉尔市| 九寨沟县| 香格里拉县| 偃师市| 精河县| 全南县| 嘉峪关市| 丽江市| 福安市| 威远县| 西宁市| 丹阳市| 江门市| 华坪县| 丁青县| 泾阳县| 民丰县| 安西县| 塔城市| 阜康市|