1. 程式人生 > >基於DirectX的簡單UI介面製作

基於DirectX的簡單UI介面製作

程式碼裡註釋寫的比較清楚,有點DirectX基礎的就能簡單看懂,

謹以此文記錄那些年寫過的程式碼,和分享給那些需要的朋友o(∩_∩)o

//D3DComMacro.h

/*--------------------------------------------------------------
【標頭檔案】:D3DComMacro.h    Create by Beyond Ray,2014年2月
(描述):公共輔助巨集的定義
----------------------------------------------------------------*/
#pragma once

#ifndef HR
#define HR(hr) {if(FAILED(hr)) return E_FAIL ;}                //定義HR巨集,便於錯誤檢測
#endif

#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p)  {if(p) { (p)->Release(); p = NULL ;}}  //定義一個安全釋放巨集,便於COM介面的釋放
#endif

#ifndef SAFE_DELETE
#define SAFE_DELETE(p)   {if(p) {delete(p); p = NULL ;}}		//定義一個安全刪除巨集,便於new分配記憶體的刪除      
#endif

#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) {if(p) {delete [] (p); p = NULL ;}}   //定義一個安全刪除巨集,便於new[]分配記憶體的刪除
#endif

#ifndef IF
#define IF(p)	{if(p != D3D_OK) return false;}			   //定義一個IF巨集,用於返回BOOL型的檢測
#endif


//GUIClass.h

/*--------------------------------------------------------------
【標頭檔案】:GUIClass.h       Created by Beyond Ray,2014年2月
(描述):一個封裝了控制元件物件的類和控制元件處理函式
----------------------------------------------------------------*/

#pragma  once

#include"D3DComMacro.h"
#include<d3d9.h>
#include<d3dx9.h>

//定義控制元件型別
#define UGP_GUI_STATIC_TEXT	1
#define UGP_GUI_BUTTON		2

//定義滑鼠按鍵及指標的狀態
#define GUI_LBUTTON_DOWN	1
#define GUI_LBUTTON_UP		2
#define GUI_MOUSE_OVER		3

//定義選單頁面切換的巨集
#define GUI_BKGROUND_MAIN	1
#define GUI_BKGROUND_START	2
#define GUI_BKGROUND_LOAD	3
#define GUI_BKGROUND_OPTION	4

//具體的控制元件型別ID
#define GUI_STATIC_TEXT		0
#define GUI_BUTTON_START	1
#define GUI_BUTTON_LOAD		2
#define GUI_BUTTON_OPTION	3
#define GUI_BUTTON_QUIT		4
#define GUI_BUTTON_BACK     5

//定義控制元件型別ID最大上限編號
#define GUI_TEXT_MAX		0
#define GUI_BUTTON_MAX		5

//靈活頂點格式的結構
typedef struct  
{
	float x, y, z, rhw;			//頂點座標
	
	float u, v;					//紋理貼圖
	//DWORD color;
	
} GUIVERTEX, * LPGUIVERTEX;

#define D3DFVF_GUI (D3DFVF_XYZRHW | D3DFVF_TEX1 )

//控制元件屬性結構體
struct GUICONTROL
{
	int type;					//控制元件的型別(文字、按鈕)
	int controlID;				//控制元件的具體型別ID
	WORD index;					//文字:對字型物件的索引,按鈕:對頂點快取的索引			
	WORD x;						//控制元件相對於視窗客戶區左上角的X位置
	WORD y;						//控制元件相對於視窗客戶區左上角的Y位置		
	WORD width;					//控制元件的寬度
	WORD height;				//控制元件的高度
	TCHAR * text;				//文字的內容
	unsigned long color;		//文字的顏色
	LPDIRECT3DTEXTURE9 textureUP, textureDown, textureOver;  //(只相對於按鈕)表示滑鼠按下,釋放,經過時的紋理貼圖
};

//【定義一個GUI物件類】
class GUIClass
{
private:
	LPDIRECT3DDEVICE9		m_pd3dDevice9;		//Direct3D裝置物件(COM介面物件)
	LPDIRECT3DTEXTURE9		m_pBackGround;		//控制元件物件的背景貼圖(COM介面物件)
	LPDIRECT3DVERTEXBUFFER9 m_pBkBuffer;		//背景圖快取物件(COM介面物件)

	LPD3DXFONT			  * m_pFont;			//指向字型物件的指標(COM介面物件陣列)
	LPDIRECT3DVERTEXBUFFER9 * m_pCtrlBuffer;	//控制元件按鈕快取物件的指標(COM介面物件陣列)
	GUICONTROL			  * m_pControl;			//控制元件的陣列物件

	int		m_iFontNums;		//字型種類的數目
	int     m_iTextNums;		//文字物件的數目
	int		m_iButtonNums;		//按鈕物件的數目
	int		m_iControlNums;		//控制元件物件的數目

	WORD	m_wBackgroundWidth;		//貼圖背景的寬度
	WORD	m_wBackgroundHeight;	//貼圖背景的高度

	BOOL	m_bIsBKBufferUsed;	//用一個標識來判斷是否使用了背景(以緩衝區的建立為標誌)

public:
	GUIClass(LPDIRECT3DDEVICE9 pd3dDevice9, WORD Width, WORD Height);	//建構函式		
	~GUIClass(){ CleanUp(); }											//解構函式

public:
	LPDIRECT3DDEVICE9 GetDevice(){ return m_pd3dDevice9; }	//返回裝置物件

	int		GetFontNums(){ return m_iFontNums; }			//返回字型種類的數目	
	int		GetTextNums(){ return m_iTextNums; }			//返回文字物件的數目
	int		GetButtonNums(){ return m_iButtonNums; }		//返回按鈕物件的數目
	int		GetControlNums(){ return m_iControlNums; }		//返回控制元件物件的數目

	WORD	GetBackgroundWidth(){ return m_wBackgroundWidth; }		//返回貼圖背景的寬度
	WORD	GetBackgroundHeight(){ return m_wBackgroundHeight; }	//返回貼圖背景的高度

	VOID	SetBackgroundWidth(WORD Width){ m_wBackgroundWidth = Width; }		//設定貼圖背景的寬度
	VOID	SetBackgroundHeight(WORD Height){ m_wBackgroundHeight = Height; }	//設定貼圖背景的高度

	LPDIRECT3DTEXTURE9 GetBkTexture(){ return m_pBackGround; }		//返回背景紋理貼圖
	LPDIRECT3DVERTEXBUFFER9 GetBkBuffer(){ return m_pBkBuffer; }	//返回背景頂點快取

	BOOL	IsBKBufferUsed(){ return m_bIsBKBufferUsed; }			//判斷是否使用了背景(以緩衝區的建立為標誌)

	//用索引分別得到字型,按鈕頂點快取,控制元件的物件
	LPD3DXFONT	GetFont(WORD Index)
	{
		if (Index >= m_iFontNums) return NULL;
		return m_pFont[Index];
	}

	LPDIRECT3DVERTEXBUFFER9 GetCtrlBuffer(WORD Index)
	{
		if (Index >= m_iButtonNums) return NULL;
		return m_pCtrlBuffer[Index];
	}

	GUICONTROL*	GetControl(WORD Index)
	{ 
		if (Index >= m_iControlNums) return NULL;
		return &m_pControl[Index];
	}

	BOOL AddBackground(TCHAR * pBackgroundName);												//新增背景
	BOOL CreateTextFont(TCHAR * FontName, DWORD SIZE, WORD *Index);								//增加字型
	BOOL AddStaticText(TCHAR * TextContent, UINT ControlID, DWORD Color, WORD x, WORD y, WORD Index);//增加文字
	BOOL AddButton(UINT ControlID, WORD x, WORD y,												//增加按鈕
		TCHAR* TextureDown, TCHAR* TextureUp, TCHAR* TextureOver);
	VOID CleanUp();																				//資源的清理	
};

BOOL ProcessGUI(GUIClass * GUI, BOOL LMBDown, WORD MouseX, WORD MouseY,
	void(CALLBACK *GUIProc)(int Id, int State));	//回撥函式作形參





//GUIClass.cpp

/*--------------------------------------------------------------
【Cpp檔案】:GUIClass.cpp       Created by Beyond Ray,2014年2月
(描述):一個封裝了控制元件物件的類和控制元件處理函式的實現
----------------------------------------------------------------*/

#include"GUIClass.h"

//-------------------------------------------【GUIClass()函式】--------------------------------------------------
// Desc:建構函式(初始化)
//---------------------------------------------------------------------------------------------------------------
GUIClass::GUIClass(LPDIRECT3DDEVICE9 pd3dDevice9, WORD Width, WORD Height)
{
	m_pd3dDevice9 = pd3dDevice9;
	m_pBackGround = NULL;
	m_pBkBuffer = NULL;

	m_pControl = NULL;
	m_pCtrlBuffer = NULL;
	m_pFont = NULL;

	m_iFontNums = 0;
	m_iTextNums = 0;
	m_iControlNums = 0;
	m_iButtonNums = 0;

	m_wBackgroundWidth = Width;
	m_wBackgroundHeight = Height;

	m_bIsBKBufferUsed = FALSE;
}

//-------------------------------------------【AddBackground()函式】---------------------------------------------
// Desc:新增GUIClass物件的唯一背景圖片
//---------------------------------------------------------------------------------------------------------------
BOOL GUIClass::AddBackground(TCHAR *  pBackgroundName)
{
	if (!pBackgroundName) return FALSE;
	IF(D3DXCreateTextureFromFile(m_pd3dDevice9, pBackgroundName, &m_pBackGround));

	IF(m_pd3dDevice9->CreateVertexBuffer(4 * sizeof(GUIVERTEX), D3DUSAGE_WRITEONLY, D3DFVF_GUI,
		 D3DPOOL_DEFAULT, &m_pBkBuffer, NULL));

	float w = (float)m_wBackgroundWidth;
	float h = (float)m_wBackgroundHeight;

	GUIVERTEX Vertices[] =
	{
		{ 0, h, 0.0f, 1.0f, 0.0f, 1.0f },
		{ 0, 0, 0.0f, 1.0f, 0.0f, 0.0f },
		{ w, h, 0.0f, 1.0f, 1.0f, 1.0f },
		{ w, 0, 0.0f, 1.0f, 1.0f, 0.0f }
	};

	VOID *pVertices = NULL;
	IF(m_pBkBuffer->Lock(0, 0, &pVertices, NULL));
	memcpy(pVertices, Vertices, sizeof(Vertices));
	m_pBkBuffer->Unlock();

	m_bIsBKBufferUsed = true;
	return TRUE;
}

//--------------------------------------------【CreateTextFont()函式】-------------------------------------------
// Desc:建立並增加字型物件
//---------------------------------------------------------------------------------------------------------------
BOOL GUIClass::CreateTextFont(TCHAR * FontName, DWORD SIZE, WORD *Index)
{
	if (!m_pFont)
	{
		m_pFont = new LPD3DXFONT[1];
		if (!m_pFont) return FALSE;
	}
	else
	{
		LPD3DXFONT * pTempFont = new LPD3DXFONT[m_iFontNums + 1];
		if (!pTempFont) return FALSE;

		memset(pTempFont, 0, sizeof(pTempFont));
		memcpy(pTempFont, m_pFont, sizeof(m_pFont));
		SAFE_DELETE_ARRAY(m_pFont);
		m_pFont = pTempFont;
	}
	IF(D3DXCreateFont(m_pd3dDevice9, SIZE, 0, 1000, 0, false,
		DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, FontName, &m_pFont[m_iFontNums]));

	*Index = m_iFontNums;
	m_iFontNums++;
	return TRUE;
}

//-------------------------------------------【AddText()函式】---------------------------------------------------
// Desc:增加文字控制元件
//---------------------------------------------------------------------------------------------------------------
BOOL GUIClass::AddStaticText(TCHAR * TextContent,  UINT ControlID, DWORD Color, WORD x, WORD y, WORD Index)
{
	//對形參作檢測判斷
	if ( ControlID > GUI_TEXT_MAX || Index >= m_iFontNums)
		return FALSE;

	//為m_pControl分配新的記憶體空間
	if (!m_pControl)
	{
		m_pControl = new GUICONTROL[1];
		if (!m_pControl) return FALSE;
	}
	else
	{
		GUICONTROL * pTempCtrl = new GUICONTROL[m_iControlNums + 1];
		if (!pTempCtrl) return FALSE;

		memset(pTempCtrl, 0, (m_iControlNums + 1) * sizeof(GUICONTROL));
		memcpy(pTempCtrl, m_pControl, m_iControlNums * sizeof(GUICONTROL));
		SAFE_DELETE_ARRAY(m_pControl);
		m_pControl = pTempCtrl;
	}

	//填充新資料的引數
	m_pControl[m_iControlNums].type = UGP_GUI_STATIC_TEXT;
	m_pControl[m_iControlNums].controlID = ControlID;
	m_pControl[m_iControlNums].x = x;
	m_pControl[m_iControlNums].y = y;
	m_pControl[m_iControlNums].index = Index;
	m_pControl[m_iControlNums].color = Color;

	//新文字的複製
	int iChars = lstrlen(TextContent);
	m_pControl[m_iControlNums].text = new TCHAR[iChars + 1];

	if (!m_pControl[m_iControlNums].text) return FALSE;
	lstrcpy(m_pControl[m_iControlNums].text, TextContent);
	m_pControl[m_iControlNums].text[iChars] = '\0';

	//通過描述欄位讀取並計算寬和高
	D3DXFONT_DESC desc;
	m_pFont[Index]->GetDesc(&desc);
	m_pControl[m_iControlNums].height = desc.Height;
	m_pControl[m_iControlNums].width = desc.Width * iChars;

	//控制元件數量和文字數量的增加
	m_iControlNums++;
	m_iTextNums++;
	return TRUE;
}

//-------------------------------------------【AddButton()函式】----------------------------------------------
// Desc:增加按鈕控制元件
//------------------------------------------------------------------------------------------------------------
BOOL GUIClass::AddButton(UINT ControlID, WORD x, WORD y, TCHAR* TextureDown, TCHAR* TextureUp, TCHAR* TextureOver)
{
	//對形參作檢測判斷
	if (ControlID <= GUI_TEXT_MAX || ControlID > GUI_BUTTON_MAX)
		return FALSE;

	//為m_pControl分配新的記憶體空間
	if (!m_pControl)
	{
		m_pControl = new GUICONTROL[1];
		if (!m_pControl) return FALSE;
	}
	else
	{
		GUICONTROL * pTempCtrl = new GUICONTROL[m_iControlNums + 1];
		if (!pTempCtrl) return FALSE;

		memset(pTempCtrl, 0, (m_iControlNums + 1) * sizeof(GUICONTROL));
		memcpy(pTempCtrl, m_pControl, m_iControlNums * sizeof(GUICONTROL));
		SAFE_DELETE_ARRAY(m_pControl);
		m_pControl = pTempCtrl;
	}

	//填充新資料的引數
	m_pControl[m_iControlNums].type = UGP_GUI_BUTTON;
	m_pControl[m_iControlNums].controlID = ControlID;

	IF(D3DXCreateTextureFromFile(m_pd3dDevice9, TextureDown, &m_pControl[m_iControlNums].textureDown));
	IF(D3DXCreateTextureFromFile(m_pd3dDevice9, TextureUp, &m_pControl[m_iControlNums].textureUP));
	IF(D3DXCreateTextureFromFile(m_pd3dDevice9, TextureOver, &m_pControl[m_iControlNums].textureOver));

	D3DSURFACE_DESC desc;
	m_pControl[m_iControlNums].textureDown->GetLevelDesc(0, &desc);
	m_pControl[m_iControlNums].x = x;
	m_pControl[m_iControlNums].y = y;
	m_pControl[m_iControlNums].height = desc.Height;
	m_pControl[m_iControlNums].width = desc.Width;

	//為m_pCtrlBuffer分配新的記憶體
	if (!m_pCtrlBuffer)
	{
		m_pCtrlBuffer = new LPDIRECT3DVERTEXBUFFER9[1];
		if (!m_pCtrlBuffer) return FALSE;
	}
	else
	{
		LPDIRECT3DVERTEXBUFFER9 * pTempCtrlBuffer = new LPDIRECT3DVERTEXBUFFER9[m_iButtonNums + 1];
		if (!pTempCtrlBuffer) return FALSE;

		memcpy(pTempCtrlBuffer, m_pCtrlBuffer, m_iButtonNums * sizeof(LPDIRECT3DVERTEXBUFFER9));//指標的賦值
		SAFE_DELETE_ARRAY(m_pCtrlBuffer);
		m_pCtrlBuffer = pTempCtrlBuffer;
	}

	//建立頂點緩衝區
	IF(m_pd3dDevice9->CreateVertexBuffer(4 * sizeof(GUIVERTEX), 0, D3DFVF_GUI, D3DPOOL_MANAGED,
		&m_pCtrlBuffer[m_iButtonNums], NULL));

	GUIVERTEX Vertics[] =
	{
		{ x, y + desc.Height, 0, 1.0f, 0.0f, 1.0f },
		{ x, y, 0, 1.0f, 0.0f, 0.0f },
		{ x + desc.Width, y + desc.Height, 0, 1.0f, 1.0f, 1.0f },
		{ x + desc.Width, y, 0, 1.0f, 1.0f, 0.0f }
	};

	VOID *pVertices = NULL;
	IF(m_pCtrlBuffer[m_iButtonNums]->Lock(0, 0, (void **)&pVertices, NULL));
	memcpy(pVertices, Vertics, sizeof(Vertics));
	m_pCtrlBuffer[m_iButtonNums]->Unlock();

	m_pControl[m_iControlNums].index = m_iButtonNums;

	//增加控制元件和按鈕的數目
	m_iButtonNums++;
	m_iControlNums++;
	return TRUE;
}

//-------------------------------------------【CleanUp()函式】---------------------------------------------------
// Desc:資源的清理
//---------------------------------------------------------------------------------------------------------------
VOID GUIClass::CleanUp()
{
	if (m_bIsBKBufferUsed)
	{
		SAFE_RELEASE(m_pBackGround);
		SAFE_RELEASE(m_pBkBuffer);
	}

	for (int i = 0; i < m_iFontNums; i++)
		SAFE_RELEASE(m_pFont[i]);
	SAFE_DELETE_ARRAY(m_pFont);

	for (int i = 0; i < m_iButtonNums; i++)
	{
		SAFE_RELEASE(m_pCtrlBuffer[i]);
	}
	SAFE_DELETE_ARRAY(m_pCtrlBuffer);

	for (int i = 0; i < m_iControlNums; i++)
	{
		SAFE_RELEASE(m_pControl[i].textureDown);
		SAFE_RELEASE(m_pControl[i].textureUP);
		SAFE_RELEASE(m_pControl[i].textureOver);
		SAFE_DELETE_ARRAY(m_pControl[i].text);
	}
	SAFE_DELETE_ARRAY(m_pControl);
}

//----------------------------------------【ProcessGUI()函式】---------------------------------------------
// Desc:進行GUI的渲染
//---------------------------------------------------------------------------------------------------------
BOOL ProcessGUI(GUIClass * GUI, BOOL LMBDown, WORD MouseX, WORD MouseY, void(CALLBACK *GUIProc)(int Id, int State))
{
	if (!GUI) return FALSE;
	LPDIRECT3DDEVICE9  pd3dDevice9 = GUI->GetDevice();
	if (!pd3dDevice9) return FALSE;

	if (!(GUI->IsBKBufferUsed() && GUI->GetBkBuffer() && GUI->GetBkTexture()))
		return FALSE;

	//渲染GUI背景
	//pd3dDevice9->SetRenderState(D3DRS_LIGHTING, false);
	pd3dDevice9->SetTexture(0, GUI->GetBkTexture());
	pd3dDevice9->SetStreamSource(0, GUI->GetBkBuffer(), 0, sizeof(GUIVERTEX));
	pd3dDevice9->SetFVF(D3DFVF_GUI);
	pd3dDevice9->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);


	//用來顯示文字的物件
	LPD3DXFONT pFont = NULL;
	RECT rect = { 0, 0, GUI->GetBackgroundWidth(), GUI->GetBackgroundHeight() };

	// 建立一個頂點快取物件用於按鈕的渲染
	LPDIRECT3DVERTEXBUFFER9 pVertexBuffer = NULL;
	int iState = GUI_LBUTTON_UP;

	//渲染控制元件
	GUICONTROL *pGUIControl = NULL;
	for (WORD i = 0; i < GUI->GetControlNums(); i++)
	{
		iState = GUI_LBUTTON_UP;
		pGUIControl = GUI->GetControl(i);
		if (!pGUIControl) continue;

		switch (pGUIControl->type)
		{
		case UGP_GUI_STATIC_TEXT:
			pFont = GUI->GetFont(pGUIControl->index);
			if (!pFont) continue;

			//設定起始位置並輸出文字
			rect.left = pGUIControl->x;
			rect.top = pGUIControl->y;
			pFont->DrawText(NULL, pGUIControl->text, -1, &rect, DT_LEFT | DT_SINGLELINE, pGUIControl->color);
			break;

		case UGP_GUI_BUTTON:
			pVertexBuffer = GUI->GetCtrlBuffer(pGUIControl->index);
			if (!pVertexBuffer) continue;

			//開啟Alpha混合
			pd3dDevice9->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
			pd3dDevice9->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
			pd3dDevice9->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

			if (MouseX >= pGUIControl->x && MouseX <= (pGUIControl->x + pGUIControl->width) &&
				MouseY >= pGUIControl->y && MouseY <= (pGUIControl->y + pGUIControl->height))
			{
				if (LMBDown)
					iState = GUI_LBUTTON_DOWN;
				else
					iState = GUI_MOUSE_OVER;
			}
			//開始渲染按鈕
			if (iState == GUI_LBUTTON_DOWN) pd3dDevice9->SetTexture(0, pGUIControl->textureDown);
			if (iState == GUI_LBUTTON_UP) pd3dDevice9->SetTexture(0, pGUIControl->textureUP);
			if (iState == GUI_MOUSE_OVER) pd3dDevice9->SetTexture(0, pGUIControl->textureOver);

			pd3dDevice9->SetStreamSource(0, pVertexBuffer, 0, sizeof(GUIVERTEX));
			pd3dDevice9->SetFVF(D3DFVF_GUI);
			pd3dDevice9->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

			//關閉Alpha混合
			pd3dDevice9->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
		}
		if (GUIProc) GUIProc(pGUIControl->controlID, iState);
	}
	return TRUE;
}


//main.cpp

/*----------------------------------------------------------------
【Cpp檔案】:main.cpp	    Create by Beyond Ray,2014年2月
(描述):簡單的GUI介面實現
------------------------------------------------------------------*/

//【標頭檔案】
#include"D3DComMacro.h"
#include"GUIClass.h"
#include <d3d9.h>
#include <d3dx9.h>
#include <tchar.h>

//【庫檔案】
#pragma comment (lib, "winmm.lib")                                     //為呼叫 PlaySound 函式
#pragma comment (lib, "d3d9.lib")								       //Direct3D9相關庫檔案	
#pragma comment (lib, "d3dx9.lib")									   //ID3DX相關庫檔案
#pragma comment (lib, "dxguid.lib")									   //GUID相關識別
#pragma comment (lib, "dinput8.lib")								   //DirectInput相關庫檔案

//【巨集定義】
#define WINDOW_TITLE	L"致我們永不熄滅的遊戲夢想----【Beyond我愛楊】"      //視窗標題

//【全域性變數宣告】
TCHAR		lpClassName[] = TEXT("GDIDemo");						   //宣告類名
HINSTANCE   g_hInst = NULL;											   //例項控制代碼
LPDIRECT3DDEVICE9	g_pd3dDevice9 = NULL;							   //Direct3D裝置物件(COM介面物件)

LPD3DXFONT	g_pFont =  NULL;										   //字型物件(COM介面物件)

wchar_t		g_str[50];												   //用於輸出的字串陣列
int			g_cxClient = 1280;										   //視窗客戶區的寬度
int			g_cyClient = 775;										   //視窗客戶區的高度

//-----------------------------------【GUI相關的全域性變數宣告】---------------------------------------
int			g_cxMouse = 0;											   //滑鼠指標X位置
int			g_cyMouse = 0;											   //滑鼠指標Y位置
BOOL		g_bLMBDown = false;										   //判斷滑鼠左鍵是否按下的標誌

GUIClass	* g_pGUIMain = NULL;
GUIClass	* g_pGUIStart = NULL;
GUIClass	* g_pGUILoad = NULL;
GUIClass	* g_pGUIOption = NULL;									   //4個GUI類的物件

int			g_currentGUI = GUI_BKGROUND_MAIN;						   //預設的GUI標識
WORD		g_GUIMainFont = -1; 									   //GUI字型物件的索引
//--------------------------------------------------------------------------------------------------

//【全域性函式宣告】
LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);				   //視窗過程函式
HRESULT Direct3D_Init(HWND hwnd);									   //在該函式中進行Direct3D初始化
HRESULT Object_Init(HWND hwnd);										   //在該函式中進行繪製物體資源的初始化
VOID  Direct3D_Render(HWND hwnd);						               //在該函式中進行Direct3D的渲染
VOID  Direct3D_CleanUp();											   //在該函式中進行COM資源及其他資源的清理
VOID  HelpText_Render(HWND);										   //在該函式中渲染幫助文字
VOID  CALLBACK GUICallBack(int Id, int State);						   //GUI回撥函式
VOID  Light_Set(LPDIRECT3DDEVICE9, int);							   //在該函式中進行光源的設定
VOID  ClipClient(HWND hwnd);										   //該函式將限制滑鼠在客戶區中
float GetFPS();														   //得到每秒的幀率(Frame Per Second)	

//----------------------------------------【WinMain主函式】-------------------------------------------
//  描述:Windows程式的入口函式,程式從這裡開始
//----------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wndclass;
	HWND	   hwnd;
	MSG		   msg = { 0 };

	g_hInst = hInstance;
	//【設計視窗類】

	wndclass.cbSize = sizeof (WNDCLASSEX);								//結構大小
	wndclass.style = CS_HREDRAW | CS_VREDRAW;							//視窗風格樣式
	wndclass.lpfnWndProc = WndProc;										//設定視窗過程地址
	wndclass.cbClsExtra = 0;											//額外的視窗類記憶體空間
	wndclass.cbWndExtra = 0;											//額外的視窗記憶體空間
	wndclass.hInstance = hInstance;										//視窗的例項控制代碼
	wndclass.hIcon = (HICON)::LoadImage(NULL, L"pictures/icon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
	//載入圖示
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);						//載入滑鼠指標
	wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);		//載入視窗背景
	wndclass.lpszMenuName = NULL;										//視窗選單名稱
	wndclass.lpszClassName = lpClassName;								//視窗類名稱
	wndclass.hIconSm = 0;												//工作列中的小圖示

	//【註冊視窗類】

	if (!RegisterClassEx(&wndclass))
	{
		MessageBox(NULL, TEXT("視窗類註冊失敗"), lpClassName, MB_OK | MB_ICONEXCLAMATION);
		return -1;
	}

	//【建立視窗】

	hwnd = CreateWindow(lpClassName, WINDOW_TITLE,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, g_cxClient, g_cyClient,
		NULL, NULL, hInstance, NULL);                                  //父視窗控制代碼,選單控制代碼,例項控制代碼,額外欄位


	if (S_OK != Direct3D_Init(hwnd))
	{
		MessageBox(hwnd, _T("Direct3D初始化資源失敗!!"), _T("我愛楊的訊息視窗"), MB_OK);
		return E_FAIL;
	}

	//【移動,顯示和更新視窗】

	MoveWindow(hwnd, 0, 0, g_cxClient, g_cyClient, TRUE);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	//播放背景音樂
	PlaySound(_T("GUI/天下貳 - 太古銅門.wav"), NULL, SND_ASYNC | SND_FILENAME);

	//進入遊戲訊息迴圈

	static FLOAT fCurrentTime = (float)timeGetTime();
	static FLOAT fLastTime = (float)timeGetTime();
	static FLOAT fElapsedTime = 0.0f;

	while (msg.message != WM_QUIT)
	{

		fLastTime = fCurrentTime;
		fCurrentTime = (float)timeGetTime();
		fElapsedTime = (fCurrentTime - fLastTime) / 1000.0f;

		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			Direct3D_Render(hwnd);
		}
	}

	//登出視窗類
	UnregisterClass(lpClassName, wndclass.hInstance);

	return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

	switch (message)
	{
	case WM_SIZE:
		g_cxClient = LOWORD(lParam);
		g_cyClient = HIWORD(lParam);
		if (g_cxClient == 0 || g_cyClient == 0)
			return 0;
		ClipClient(hwnd);
		return 0;

	case WM_MOUSEMOVE:
		g_cxMouse = LOWORD(lParam) + GetSystemMetrics(SM_CXBORDER);
		g_cyMouse = HIWORD(lParam) + GetSystemMetrics(SM_CYCAPTION);
		return 0;

	case WM_LBUTTONDOWN:
		g_bLMBDown = TRUE;
		return 0;

	case WM_LBUTTONUP:
		g_bLMBDown = FALSE;
		return 0;

	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE)
			DestroyWindow(hwnd);
		return 0;

	case WM_PAINT:
		Direct3D_Render(hwnd);
		ValidateRect(hwnd, NULL);
		return 0;

	case WM_DESTROY:
		Direct3D_CleanUp();
		PostQuitMessage(0);
		return 0;

	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}

}
//--------------------------------------------【Direct3D_Init】---------------------------------------------
//  描述:在該函式中進行Direct3D初始化
//----------------------------------------------------------------------------------------------------------
HRESULT Direct3D_Init(HWND hwnd)
{
	//--------------------------------------------------------------------------------------
	// 【創介面】:建立Direct3D介面物件, 以便用該Direct3D物件建立Direct3D裝置物件
	//--------------------------------------------------------------------------------------
	LPDIRECT3D9		pD3D9;										//建立Direct3D介面物件
	if (FAILED(pD3D9 = Direct3DCreate9(D3D_SDK_VERSION)))	    //初始化Direct3D介面物件,並與DirectX_SDK版本協商,
		return E_FAIL;											//以便包含的標頭檔案與DirectX Runtime匹配

	//--------------------------------------------------------------------------------------
	// 【取資訊】:獲取硬體裝置資訊
	//--------------------------------------------------------------------------------------
	D3DCAPS9 caps; int vp = 0;
	if (FAILED(pD3D9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps)))
	{
		return E_FAIL;
	}
	if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;				//支援硬體頂點運算方式
	else
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;				//支援軟體頂點運算方式

	//--------------------------------------------------------------------------------------
	// 【填內容】:填充D3DPRESENT_PARAMETERS結構體
	//--------------------------------------------------------------------------------------

	D3DPRESENT_PARAMETERS	d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.BackBufferWidth = g_cxClient;							//後臺緩衝區寬度
	d3dpp.BackBufferHeight = g_cyClient;						//後臺緩衝區高度
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;					//後臺緩衝區儲存畫素格式
	d3dpp.BackBufferCount = 2;									//後臺緩衝區數量
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;				//多重取樣的型別
	d3dpp.MultiSampleQuality = 0;								//多重取樣的格式
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;					//指定如何將後臺緩衝區內容複製到前臺的快取中
	d3dpp.hDeviceWindow = hwnd;									//當前與裝置關聯的視窗,被啟用的視窗
	d3dpp.Windowed = true;										//對啟用視窗表示是否使用視窗模式
	d3dpp.EnableAutoDepthStencil = true;						//自動管理深度快取,模版快取
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;				//深度快取,模版快取格式,(這裡為24位深度,8位模版)
	d3dpp.Flags = 0;											//附加屬性
	d3dpp.FullScreen_RefreshRateInHz = 0;						//全屏模式時的螢幕重新整理率
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //後臺緩衝區和前臺緩衝區的最大交換頻率


	//--------------------------------------------------------------------------------------
	// 【創裝置】:建立Direct3D裝置介面
	//--------------------------------------------------------------------------------------

	if (FAILED(pD3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, vp, &d3dpp, &g_pd3dDevice9)))
	{
		SAFE_RELEASE(pD3D9);
		return E_FAIL;
	}
	SAFE_RELEASE(pD3D9);		//Direct3D介面物件使命完成,我們將其釋放掉


	if (S_OK != Object_Init(hwnd))
		return E_FAIL;

	return S_OK;
}
//--------------------------------------------【Object_Init】------------------------------------------------
//  描述:在該函式中進行繪製物體資源的初始化
//-----------------------------------------------------------------------------------------------------------
HRESULT Object_Init(HWND hwnd)
{
	
	//建立字型
	HR(D3DXCreateFont(g_pd3dDevice9, 36, 0, 0, 0, FALSE, DEFAULT_CHARSET,     //建立微軟雅黑字型
		OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("Clambria"), &g_pFont));
	
	srand(GetTickCount());	//用作業系統啟動到現在的時間來初始化隨機種子

//----------------------------------------【GUI相關資源的新增】----------------------------------------------
	//GUI物件的初始化
	g_pGUIMain = new GUIClass(g_pd3dDevice9, g_cxClient, g_cyClient);
	g_pGUIStart = new GUIClass(g_pd3dDevice9, g_cxClient, g_cyClient);
	g_pGUILoad = new GUIClass(g_pd3dDevice9, g_cxClient, g_cyClient);
	g_pGUIOption = new GUIClass(g_pd3dDevice9, g_cxClient, g_cyClient);

	//新增GUI字型物件
	g_pGUIMain->CreateTextFont(_T("微軟雅黑"), 36, &g_GUIMainFont);

	//新增背景圖片
	if (!g_pGUIMain->AddBackground(TEXT("GUI/main.jpg")))	return E_FAIL;
	if (!g_pGUIStart->AddBackground(TEXT("GUI/startgame.jpg")))	return E_FAIL;
	if (!g_pGUILoad->AddBackground(TEXT("GUI/loadgame.jpg")))	return E_FAIL;
	if (!g_pGUIOption->AddBackground(TEXT("GUI/option.jpg")))	return E_FAIL;


	//---------------------------------【主選單main頁面相關的頁面佈局程式碼】---------------------------------
	// 新增靜態文字到頁面中
	if (!g_pGUIMain->AddStaticText(TEXT("Version 我愛楊V2.0傾情製造版"), GUI_STATIC_TEXT,
		D3DCOLOR_RGBA(255, 255, 255, 210), 850, 720, g_GUIMainFont)) return false;

	// 新增4個按鈕,分別是開始遊戲,載入進度,選項和退出遊戲,每個按鈕對應3幅圖
	if (!g_pGUIMain->AddButton(GUI_BUTTON_START, 585, 340, TEXT("GUI\\startDown.png"),
		TEXT("GUI\\StartUp.png"), TEXT("GUI\\startOver.png"))) return false;

	if (!g_pGUIMain->AddButton(GUI_BUTTON_LOAD, 585, 385, TEXT("GUI\\loadDown.png"),
		TEXT("GUI\\loadUp.png"),TEXT("GUI\\loadOver.png"))) return false;

	if (!g_pGUIMain->AddButton(GUI_BUTTON_OPTION, 585, 430, L"GUI\\optionsDown.png",
		L"GUI\\optionsUp.png", L"GUI\\optionsOver.png")) return false;

	if (!g_pGUIMain->AddButton(GUI_BUTTON_QUIT, 585, 475, L"GUI\\quitDown.png",
		L"GUI\\quitUp.png", L"GUI\\quitOver.png")) return false;


	//------------------------【開始新遊戲start頁面相關的頁面佈局程式碼】------------------------
	// 新增按鈕到頁面中

	if (!g_pGUIStart->AddButton(GUI_BUTTON_BACK, 750, 400, L"GUI/backDown.png", L"GUI/backUp.png",
		L"GUI/backOver.png")) return false;

	if (!g_pGUIStart->AddButton(GUI_BUTTON_BACK, 300, 200, L"GUI/飛航模式down.png", L"GUI/飛航模式up.png",
		L"GUI/飛航模式over.png")) return false;

	//------------------------【載入遊戲load頁面相關的頁面佈局程式碼】------------------------
	// 新增按鈕到頁面中
	if (!g_pGUILoad->AddButton(GUI_BUTTON_BACK, 750, 400, L"GUI/backDown.png", L"GUI/backUp.png",
		L"GUI/backOver.png")) return false;


	//------------------------【遊戲設定option頁面相關的頁面佈局程式碼】------------------------
	// 新增按鈕到頁面中
	if (!g_pGUIOption->AddButton(GUI_BUTTON_BACK, 750, 400, L"GUI/backDown.png", L"GUI/backUp.png",
		L"GUI/backOver.png")) return false;
	
	return S_OK;
	
}


//--------------------------------------------【Direct3D_Render】--------------------------------------------
//  描述:在該函式中進行Direct3D的渲染
//-----------------------------------------------------------------------------------------------------------
VOID Direct3D_Render(HWND hwnd)
{
	//----------------------------------------------------------------------------------
	//【清屏操作】
	//----------------------------------------------------------------------------------
	g_pd3dDevice9->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_XRGB(0, 100, 200), 1.0f, 0);

	//----------------------------------------------------------------------------------
	//【開始場景】
	//----------------------------------------------------------------------------------
	g_pd3dDevice9->BeginScene();

	//----------------------------------------------------------------------------------
	//【正式繪製】
	//----------------------------------------------------------------------------------
	switch (g_currentGUI)
	{
	case GUI_BKGROUND_MAIN:
		ProcessGUI(g_pGUIMain, g_bLMBDown, g_cxMouse, g_cyMouse, GUICallBack);
		break;

	case GUI_BKGROUND_START:
		ProcessGUI(g_pGUIStart, g_bLMBDown, g_cxMouse, g_cyMouse, GUICallBack);
		break;

	case GUI_BKGROUND_LOAD:
		ProcessGUI(g_pGUILoad, g_bLMBDown, g_cxMouse, g_cyMouse, GUICallBack);
		break;

	case GUI_BKGROUND_OPTION:
		ProcessGUI(g_pGUIOption, g_bLMBDown, g_cxMouse, g_cyMouse, GUICallBack);
		break;
	}

	//渲染幫助文字:
	HelpText_Render(hwnd);

	//----------------------------------------------------------------------------------
	//【結束場景】
	//----------------------------------------------------------------------------------
	g_pd3dDevice9->EndScene();

	//----------------------------------------------------------------------------------
	//【翻轉與顯示】
	//----------------------------------------------------------------------------------
	g_pd3dDevice9->Present(NULL, NULL, NULL, NULL);
	//源矩形區域,目標矩形區域,當前繪製的視窗,最小更新區域

}

//--------------------------------------------【Game_CleanUp】------------------------------------------------
//  描述:在該函式中進行COM資源及其他資源的清理
//------------------------------------------------------------------------------------------------------------
VOID Direct3D_CleanUp()
{
	SAFE_RELEASE(g_pd3dDevice9);			//釋放Direct3D裝置介面
	SAFE_RELEASE(g_pFont);					//釋放字型介面物件
}

//----------------------------------------【HelpText_Render()函式】-------------------------------------------
// Desc:幫助文字資訊的輸出
//------------------------------------------------------------------------------------------------------------
VOID HelpText_Render(HWND hwnd)
{
	RECT rect;
	GetClientRect(hwnd, &rect);

	//在視窗右上角輸出幀率
	int icharCount = swprintf_s(g_str, 20, _T("FPS:%0.3f"), GetFPS());
	g_pFont->DrawText(NULL, g_str, icharCount, &rect, DT_RIGHT | DT_TOP, D3DCOLOR_XRGB(255, 10, 10));

}

//-----------------------------------------------【GUICallBack()函式】----------------------------------------
// Desc:GUI回撥函式(更改GUI物件標識和滑鼠狀態)
//------------------------------------------------------------------------------------------------------------
VOID CALLBACK GUICallBack(int Id, int State)
{
	switch (Id)
	{
	case GUI_BUTTON_BACK:
		if (State == GUI_LBUTTON_DOWN)
			g_currentGUI = GUI_BKGROUND_MAIN;
		break;

	case GUI_BUTTON_START:
		if (State == GUI_LBUTTON_DOWN)
			g_currentGUI = GUI_BKGROUND_START;
		break;

	case GUI_BUTTON_LOAD:
		if (State == GUI_LBUTTON_DOWN)
			g_currentGUI = GUI_BKGROUND_LOAD;
		break;

	case GUI_BUTTON_OPTION:
		if (State == GUI_LBUTTON_DOWN)
			g_currentGUI = GUI_BKGROUND_OPTION;
		break;

	case GUI_BUTTON_QUIT:
		if (State == GUI_LBUTTON_DOWN)
			PostQuitMessage(0);
		break;
	}
}
//-----------------------------------------------【ClipClient()函式】-----------------------------------------
// Desc:該函式將限制滑鼠在客戶區中
//------------------------------------------------------------------------------------------------------------
VOID ClipClient(HWND hwnd)
{
	POINT lt, rb;
	RECT rect;
	GetClientRect(hwnd, &rect);
	lt.x = rect.left;
	lt.y = rect.top;
	rb.x = rect.right;
	rb.y = rect.bottom;

	ClientToScreen(hwnd, <);
	ClientToScreen(hwnd, &rb);

	rect.left = lt.x;
	rect.top = lt.y;
	rect.right = rb.x;
	rect.bottom = rb.y;

	ClipCursor(&rect);
	//ShowCursor(false);
}

//--------------------------------------------【GetFPS()函式】-----------------------------------------------
//  描述:計算每秒畫面重新整理的幀數
//-----------------------------------------------------------------------------------------------------------
float GetFPS()
{
	static float fps = 0;					//每秒的幀率
	static int	 iframeCount = 0;			//圖形繪製的幀數
	static DWORD dwlastTime = 0;			//上次獲取的時間
	static DWORD dwcurrentTime;				//當前獲取的時間

	iframeCount++;

	dwcurrentTime = timeGetTime();

	if (dwcurrentTime - dwlastTime >= 1000.0f)
	{
		fps = (float)iframeCount * 1000.0f / (dwcurrentTime - dwlastTime);
		dwlastTime = dwcurrentTime;
		iframeCount = 0;
	}

	return fps;
}


此專案總共4個檔案,一個定義了巨集的標頭檔案,一個主體GUIClass類的.h檔案以及一個Cpp檔案,最後是一個包含了框架和執行過程的main.cpp

下面是GUI介面截圖:

主介面:

start game:

load game:

options:

最後給個資源連結的傳送門:點選開啟連結