Win32遊戲製作之---Bizzard

       之前寫了一片關於遊戲引擎設計的文章,今天就用遊戲引擎來實現一個小遊戲(其實不算是嚴格意義上的遊戲),主要就是爲了感受遊戲引擎給遊戲設計帶來的便利,而並不是遊戲本身,使用遊戲引擎之後你會發現,遊戲設計會簡便很多。它給你提供一個框架,而你所需要做的就是往裏面填內容。

多得不說了,下面來看遊戲引擎在遊戲之中具體的使用。

       先來想一想怎麼在原來的遊戲引擎上面填寫內容,你需要在原來的基礎上再新建一個遊戲頭文件以及遊戲實現文件,關於遊戲引擎上一篇內容已經講過了,那麼這一次只需要往裏面填內容即可。

先來看看頭文件Bizzard.h:

//Blizzard應用程序
//C++頭文件-Blizzard.h
#pragma once

//--------------------------------------
//包含文件
//--------------------------------------
#include <Windows.h>
#include <time.h>
#include <tchar.h>
#include "resource.h"
#include "GameEngine.h"


//--------------------------------------
//全局變量
//--------------------------------------
GameEngine * g_pGame = NULL;                    //每一個基於遊戲引擎的遊戲都需要一個全局遊戲引擎指針
這個頭文件中的指針爲Blizzard對遊戲引擎的訪問,因此非常重要。


資源文件沒什麼可說的,它定義了在程序之中使用的所有的資源標識符,在這個例子之中比肩簡單,只是定義了兩個圖標以及標題和名字,當然你還可以添加其他資源,這裏就不再多說。

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 GameEngine.rc 使用
//
#define IDS_APP_TITLE                   101
#define IDC_BIZZARD                     102
#define IDI_BLIZZARD                    1000
#define IDI_BLIZZARD_SM                 1001

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

最後來看一下重點的內容,就是遊戲主體的實現:

//--------------------------------------
//Blizzard應用程序
//C++源程序 - - Blizzard.cpp
//--------------------------------------

//--------------------------------------
//包含文件
//--------------------------------------
#include "Blizzard.h"

//--------------------------------------
//遊戲引擎函數
//--------------------------------------
BOOL GameInitialize(HINSTANCE hInstance)
{
	//創建遊戲引擎
	g_pGame = new GameEngine(hInstance, TEXT("Blizzard"), TEXT("Blizzard"), IDI_BLIZZARD, IDI_BLIZZARD_SM);
	if (NULL == g_pGame)
	{
		return FALSE;
	}
	//設置幀頻
	g_pGame->SetFrameRate(10000);         //這裏沒必要使用默認的(20幀每秒)
	return TRUE;
}

void GameStart(HWND hWindow)
{
	//生成隨機生成器種子
	srand((unsigned int)GetTickCount());        //如果遊戲需要調用標準的rand()函數來生成隨機數字,那麼總是應該調用Srand()函數來指定隨機數字生成器種子
}

void GameEnd()
{
	//清理遊戲
	delete g_pGame;
}

void GameActivate(HWND hWindow)
{
	HDC hdc;
	RECT rect;
	//在遊戲屏幕上繪製文本
	GetClientRect(hWindow, &rect);
	hdc = GetDC(hWindow);
	DrawText(hdc, TEXT("Here comes the blizzard!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
	ReleaseDC(hWindow, hdc);
}
void GameDeactivate(HWND hWindow)
{
	HDC hdc;
	RECT rect;
	//在屏幕上繪製停用文本
	GetClientRect(hWindow, &rect);
	hdc = GetDC(hWindow);
	DrawText(hdc, TEXT("The blizzard has passed."), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
	ReleaseDC(hWindow, hdc);
}
void GamePaint(HDC hdc)
{
	//在這個例子中沒有必要調用這個重繪函數,因爲所有的繪製工作都在GameCycle()函數之中就完成了,不過這種情況較爲少見
}
void GameCycle()
{
	HDC hdc;
	HWND hWindow = g_pGame->GetWindow();

	//在遊戲屏幕上隨機隨機位置繪製雪花圖標
	hdc = GetDC(hWindow);
	DrawIcon(hdc, rand() % (g_pGame->GetWidth()), rand() % (g_pGame->GetHeight()), (HICON)(WORD)GetClassLong(hWindow, GCL_HICON));
	ReleaseDC(hWindow, hdc);
}

這裏附上游戲引擎代碼:

#pragma once

//所要包含的文件
#include <windows.h>

#define MAX_LOADSTRING 32

//windows函數聲明
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);


//
//遊戲引擎函數的聲明
//這些函數的特定實現是遊戲特有的,必須由使用該遊戲引擎的各個遊戲提供
BOOL GameInitialize(HINSTANCE hInstance);
void GameStart(HWND hWindow);
void GameEnd();
void GameActivate(HWND hWindow);
void GameDeactivate(HWND hWindow);
void GamePaint(HDC hdc);
void GameCycle();

//
//GameEngine
//
class GameEngine
{
protected:
	//成員變量
	static GameEngine * m_pGameEngine;
	HINSTANCE m_hInstance;
	HWND m_hWindow;
	TCHAR m_szWindowClass[MAX_LOADSTRING];
	TCHAR m_szTitle[MAX_LOADSTRING];
	WORD m_wIcon, m_wSmallIcon;
	int m_iWidth, m_iHeight;
	int m_iFrameDelay;
	BOOL m_bSleep;

public:
	//構造函數/析構函數
	GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth = 640, int iHeight = 480);
	virtual ~GameEngine();

	//常規方法
	static GameEngine * GetEngine(){ return m_pGameEngine; };
	BOOL Initialize(int nCmdShow);
	LRESULT HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);

	//訪問方法
	HINSTANCE GetInstance(){ return m_hInstance; };
	HWND GetWindow(){ return m_hWindow; };
	void SetWindow(HWND hWindow){ m_hWindow = hWindow; };
	LPTSTR GetTitle(){ return m_szTitle; };
	WORD GetIcon(){ return m_wIcon; };
	WORD GetSmallIcon(){ return m_wSmallIcon; };
	int GetWidth(){ return m_iWidth; };
	int GetHeight(){ return m_iHeight; };
	int GetFrameDelay(){ return m_iFrameDelay; };
	void SetFrameRate(int iFrameRate){ m_iFrameDelay = 1000 / iFrameRate; };
	BOOL GetSleep(){ return m_bSleep; };
	void SetSleep(BOOL bSleep){ m_bSleep = bSleep; };
};

遊戲引擎源文件:

#include "GameEngine.h"

GameEngine * GameEngine::m_pGameEngine = NULL;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;
	static int iTickTrigger = 0;
	int iTickCount;
	if (GameInitialize(hInstance))
	{
		//初始化遊戲引擎
		if (!GameEngine::GetEngine()->Initialize(nCmdShow))
		{
			return FALSE;
		}
		//進入主消息循環
		while (TRUE)
		{
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				//處理消息
				if (WM_QUIT == msg.message)
				{
					break;
				}
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else
			{
			if (!GameEngine::GetEngine()->GetSleep())
			{
				//檢查滴答計數,看看是否過了一個週期
				iTickCount = GetTickCount();
				if (iTickCount - iTickTrigger)
				{
					iTickTrigger = iTickCount + GameEngine::GetEngine()->GetFrameDelay();
					GameCycle();
				}
			}
			}
		}
		return (int)msg.wParam;
	}
	GameEnd();
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
	//將所有的Windows消息都傳遞給遊戲引擎
	return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}

//
//GameEngine的構造函數/析構函數
//
GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight)
{
	//設置遊戲引擎的成員變量
	m_pGameEngine = this;
	m_hInstance = hInstance;
	m_hWindow = NULL;
	if (lstrlen(szWindowClass) > 0)
	{
		lstrcpy(m_szWindowClass, szWindowClass);
	}
	if (lstrlen(szTitle) > 0)
	{
		lstrcpy(m_szTitle, szTitle);
	}
	m_wIcon = wIcon;
	m_wSmallIcon = wSmallIcon;
	m_iHeight = iHeight;
	m_iWidth = iWidth;
	m_iFrameDelay = 20;        //默認爲20幀每秒
	m_bSleep = TRUE;
}
//析構函數
GameEngine::~GameEngine()
{}

int GameEngine::Initialize(int nCmdShow)
{
	//註冊窗口類
	WNDCLASSEX wndclass;
	wndclass.cbSize = sizeof(WNDCLASSEX);
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = (WNDPROC)WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = m_hInstance;
	wndclass.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetIcon()));
	wndclass.hIconSm = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetSmallIcon()));
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = m_szWindowClass;

	if (!RegisterClassEx(&wndclass))
	{
		MessageBox(m_hWindow, L"註冊窗口失敗!", L"警告", MB_OK);
		return FALSE;
	}
	//根據遊戲的大小計算窗口大小和位置
	int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2; 
	int iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION);
	if (wndclass.lpszMenuName != NULL)
	{
		iWindowHeight += GetSystemMetrics(SM_CYMENU);
	}
	int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2;
	int iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;

	//創建窗口
	m_hWindow = CreateWindowEx(NULL, m_szWindowClass, m_szTitle, WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth, iWindowHeight, NULL, NULL, m_hInstance, NULL);
	if (!m_hWindow)
	{
		MessageBox(m_hWindow, L"創建窗口失敗!", L"警告", MB_OK);
		return FALSE;
	}
	//更新和顯示窗口
	ShowWindow(m_hWindow,nCmdShow);
	UpdateWindow(m_hWindow);
	return TRUE;
}

LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
	//將Windows消息傳遞給遊戲引擎的成員函數
	switch (msg)
	{
	case WM_CREATE:
		//設置遊戲窗口並開始遊戲
		SetWindow(hWindow);
		GameStart(hWindow);
		break;
	case WM_SETFOCUS:
		//激活遊戲並更新休眠狀態
		GameActivate(hWindow);
		SetSleep(FALSE);
		break;
	case WM_KILLFOCUS:
		GameDeactivate(hWindow);
		SetSleep(TRUE);
		break;
	case WM_PAINT:
		PAINTSTRUCT ps;
		HDC hdc;
		hdc = BeginPaint(hWindow, &ps);
		//繪製遊戲
		GamePaint(hdc);

		EndPaint(hWindow, &ps);
		break;
	case WM_DESTROY:
		//結束遊戲並退出應用程序
		GameEnd();
		PostQuitMessage(0);
		break;
	default:
		break;
	}
	return DefWindowProc(hWindow, msg, wParam, lParam);
}

        這樣一個遊戲就算製作完成了,此次博客主要倒不是遊戲製作,而是遊戲引擎帶來的方便!

來看一下效果:

       當然你可以換成更好看的圖片,這樣可以更好的看到雪花布滿整個屏幕的感覺!

我試着換了一張圖,效果如下:

如果想要將遊戲做的更有樂趣,那麼就可以不讓它隨機生成,而是有條件的生成,這樣會更有樂趣!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章