1. 程式人生 > >【VC++遊戲開發#五】2D篇 —— 遊戲之二:看看你能堅持多少秒

【VC++遊戲開發#五】2D篇 —— 遊戲之二:看看你能堅持多少秒

我的郵箱:[email protected]    歡迎大家和我交流程式設計心得

前段時間在網上看見了一個小遊戲——看看你能堅持多少秒——考你的敏捷性,我玩了幾次,然後居然超過了18秒大笑

事後,我用MFC模擬了該遊戲中方塊"撞牆反彈"的效果——撞牆反彈效果

今天呢,我就繼續借用上次模擬的效果,來實現這款小遊戲(我簡化了遊戲的難度,玩起來更易上手)

面詳細講述本遊戲製作細節

一、遊戲介紹及效果演示

(1). 執行程式出現一個對話方塊,點選"開始"按鈕開始遊戲

(2). 用滑鼠控制紅色方塊的移動,要避免和藍色方塊接觸,這是有些結束的判斷依據

(3). 當遊戲結束後,會彈出一個對話方塊(如下)。選擇"繼續"按鈕繼續遊戲,"退出按鈕退出遊戲"

(4). 之前,有很多朋友說執行不起單獨的exe程式——ok,這次人性化點兒,給一個提示,你就知道了

(5). 演示效果(藍色方塊的移動速度會根據你堅持的時間來改變, 10秒改變一次,20秒改變一次)

二、類檢視

三、程式設計思想的改變

(1). 之前的風格:

之前寫的遊戲都是在View類中一通搞定,所以導致×××View.h和×××View.cpp檔案都比較大,而且遊戲的各個部分分工不明確,可讀性也不太好(儘管我添加了很多註釋)

(2). 現在的風格:

本次遊戲的程式設計思想採用C++類的封裝、多型、繼承等特性,主要封裝了兩個類——CDiamond, CTimer

其中CDiamond是方塊類——繼承於CImage類,代表遊戲中的方塊,負責方塊的各種操作;CTimer

是計時器類——別誤解,它是計時間用的類(遊戲上方顯示的時間),這個類很簡單,就是起計時的作用

四、程式碼剖析

由於本次是採用面向物件的程式設計思想,故也就逐個類的講解

(1).CTimer類——前面已介紹過,此類主要負責時間計時作用

定義了三個成員變數和三個成員方法:

class CTimer
{
private:
		UINT m_sec;//秒級
		UINT m_ten;//十分位
		UINT m_pct;//百分位(percent)
public:
	CTimer(void);

	//過載運算子++來自加
	CTimer& operator++(int);

	//獲取秒數
	UINT GetSecond();

	//清零
	void Clear();

	//以字串形式返回計時器資訊
	CString ToString();

	~CTimer(void);
};

其中三個成員變數的作用示意圖如下:

 

三個成員函式的程式碼如下:

//過載運算子++來自加
CTimer& CTimer::operator++(int)//int表示是過載的字尾運算子
{
	m_pct++;

	if(m_pct == 10)//等於10, 就需要進位
	{
		m_ten++;

		if(m_ten == 10)//等於10, 就需要進位
		{
			m_sec++;
 
			m_ten = 0;
		}

		m_pct = 0;
	}

	return *this;
}

//獲取秒數
UINT CTimer::GetSecond()
{
	return m_sec;
}

//清零
void CTimer::Clear()
{
	m_sec = 0;
	m_ten = 0;
	m_pct = 0;
}

//以字串形式返回計時器資訊
CString CTimer::ToString()
{
	CString time;

	time.Format(_T("%u.%u%us"), m_sec, m_ten, m_pct);

	return time;
}

(2).CDiamond類——此類用於管理方塊的各個操作(程式碼中註釋已經夠清晰了,所以我就直接貼出來)

Diamond.h標頭檔案

#include<atlimage.h>//用到了CImage類

//方塊類——繼承於CImage
class CDiamond : public CImage
{
private:
	CSize	m_sBorder;	//邊界區域
	CSize	m_sMove;	//移動位移
	CPoint	m_ptDmd;	//方塊左上角點
	CRect	m_rtDmd;	//方塊所在區域

public:
	CDiamond(void);					//預設建構函式
	
	void	SetBorder(CSize border);//設定邊界
	bool	IsOutBorder(CPoint pt);//判斷方塊是否出界

	//判斷是否與指定的方塊相交
	bool	IsIntersect(CRect rect);

	void	SetMove(CSize move);//設定移動位移
	void	ExpandMove(int n);//移動位移擴大n倍

	void	SetDmdPt(CPoint pt);//設定方塊左上角點
	CPoint	GetDmdPt();			//獲取方塊左上角點

	void	SetDmdRect();	//設定方塊所在區域
	void	SetDmdRect(CRect rect);//過載
	CRect	GetDmdRect();	//獲取方塊所在區域

	void ChangeMove();	//改變移動位移
	void MoveDiamond();	//移動方塊

	~CDiamond(void);
};


Diamond.h標頭檔案Diamond.cpp

#include "stdafx.h"

#include "Diamond.h"

//預設建構函式
CDiamond::CDiamond(void)
{
	m_sBorder.SetSize(0, 0);
	m_sMove.SetSize(1, 1);
	m_ptDmd.SetPoint(0, 0);
	m_rtDmd.SetRect(0, 0, 0, 0);
}

//設定邊界
void CDiamond::SetBorder(CSize border)
{
	m_sBorder = border;
}

//判斷指定的點是否出邊界
bool CDiamond::IsOutBorder(CPoint pt)
{
	//出界返回true
	if(pt.x < 0 ||
	   pt.x > (m_sBorder.cx - m_rtDmd.Width()) ||
	   pt.y < 0 ||
	   pt.y > (m_sBorder.cy - m_rtDmd.Height()))
	{
		return true;
	}

	//未出界, 返回false
	return false;
}

//判斷是否與指定的方塊相交
bool CDiamond::IsIntersect(CRect rect)
{
	if(rect.PtInRect(CPoint(m_rtDmd.left, m_rtDmd.top)) ||
		rect.PtInRect(CPoint(m_rtDmd.right, m_rtDmd.top)) ||
		rect.PtInRect(CPoint(m_rtDmd.left, m_rtDmd.bottom)) ||
		rect.PtInRect(CPoint(m_rtDmd.right, m_rtDmd.bottom)))
	{
		return true;
	}

	return false;
}

//設定移動位移
void CDiamond::SetMove(CSize move)
{
	m_sMove = move;
}

//擴大移動位移
void CDiamond::ExpandMove(int n)
{
	int old = (int)fabs(m_sMove.cx * 1.0);

	m_sMove.cx /= old;
	m_sMove.cy /= old;

	m_sMove.cx *= n;
	m_sMove.cy *= n;
}

//設定左上角點
void CDiamond::SetDmdPt(CPoint pt)
{
	m_ptDmd = pt;
}

//獲取左上角點
CPoint CDiamond::GetDmdPt()
{
	return m_ptDmd;
}

//設定矩形區域
void CDiamond::SetDmdRect()
{
	m_rtDmd.SetRect(m_ptDmd.x, m_ptDmd.y,
		m_ptDmd.x + GetWidth(), m_ptDmd.y + GetHeight());
}

//過載設定矩形區域
void CDiamond::SetDmdRect(CRect rect)
{
	m_rtDmd = rect;
}

//獲取矩形區域
CRect CDiamond::GetDmdRect()
{
	return m_rtDmd;
}

//改變移動位移
void CDiamond::ChangeMove()
{
	//如果出了左右邊界,水平反向
	if(m_ptDmd.x < 0 ||
		m_ptDmd.x > (m_sBorder.cx - GetWidth()))
	{
		m_sMove.cx = -m_sMove.cx;
	}

	//如果出了上下邊界,垂直反向
	if(m_ptDmd.y < 0 ||
		m_ptDmd.y > (m_sBorder.cy - GetHeight()))
	{
		m_sMove.cy = -m_sMove.cy;
	}
}

//移動方塊
void CDiamond::MoveDiamond()
{
	m_ptDmd.x	+= m_sMove.cx;
	m_ptDmd.y	+= m_sMove.cy;

	//改變方塊所在區域
	SetDmdRect();
}

CDiamond::~CDiamond(void)
{
}

(3).CHoldOnView類——用於整個遊戲UI的佈局和執行管理

定義了兩個計時器

//定義計時器ID
#define ID_MOVE 100 //控制方塊移動
#define ID_INTERSECT 101//判斷是否相交

成員變數

//成員變數
private:
	CTimer	m_timer;//計時

	CImage	m_bk;//背景

	CDiamond	m_leftUp;//左上角方塊
	CDiamond	m_rightUp;//右上角方塊
	CDiamond	m_leftDown;//左下角方塊
	CDiamond	m_rightDown;//右下角方塊

	CDiamond	m_redDmd;//控制的紅色方塊
	CPoint		m_ptMoveStart;//記錄滑鼠移動的起點

	bool		m_isTwo, m_isThree;//標記位移是否擴大2、3倍

	CSize		m_sClient;//客戶區大小

成員函式

//成員函式
private:
	void LoadLeftUp();	//載入左上角方塊
	void LoadRightUp();	//載入右上角方塊
	void LoadLeftDown();//載入左下角方塊
	void LoadRightDown();//載入右下角方塊
	void LoadRedDmd();//載入紅色控制方塊

public:
	void RestartGame();//重新開始遊戲

初始化

CHoldOnView::CHoldOnView()
{
	//設定客戶區大小
	m_sClient.SetSize(294, 414);

	//一開始標記位移未進行任何擴大
	m_isTwo = m_isThree = false;

	//載入背景
	m_bk.Load(_T("res\\bk.png"));

	if(m_bk.IsNull())
	{
		MessageBox(_T("png載入失敗, 請將程式和res資料夾放在同目錄下!"));
		exit(0);
	}

	//載入並初始化左上角方塊
	LoadLeftUp();

	//載入並初始化右上角方塊
	LoadRightUp();

	//載入並初始化左下角方塊
	LoadLeftDown();

	//載入並初始化右下角方塊
	LoadRightDown();

	//載入紅色控制方塊
	LoadRedDmd();

	//載入開始對話方塊
	CStartDlg startDlg;

	startDlg.DoModal();

}

響應滑鼠訊息以用滑鼠控制紅色方塊

void CHoldOnView::OnLButtonDown(UINT nFlags, CPoint point)
{
	//設定移動起點為當前滑鼠左鍵按下的位置
	m_ptMoveStart = point;

	CView::OnLButtonDown(nFlags, point);
}

void CHoldOnView::OnMouseMove(UINT nFlags, CPoint point)
{
	//如果在按下了滑鼠左鍵時拖動滑鼠,那麼就移動紅色方塊
	if(nFlags == MK_LBUTTON)
	{
		//先獲取紅色方塊所在的點
		CPoint ptRed = m_redDmd.GetDmdPt();

		//移動紅色所在的點
		ptRed += (point - m_ptMoveStart);

		//下一次移動的起點就是當前的終點
		m_ptMoveStart = point;

		if(!m_redDmd.IsOutBorder(ptRed))
		{
			//設定紅色方塊的位置
			m_redDmd.SetDmdPt(ptRed);
			m_redDmd.SetDmdRect();
		}
	}

	CView::OnMouseMove(nFlags, point);
}

雙緩衝貼圖

void CHoldOnView::OnDraw(CDC* pDC)
{
	CHoldOnDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	//建立緩衝DC、bmp
	CDC		bufferDC;
	bufferDC.CreateCompatibleDC(NULL);

	CBitmap	bufferBmp;
	bufferBmp.CreateCompatibleBitmap(pDC, 
		m_sClient.cx, m_sClient.cy);
	bufferDC.SelectObject(bufferBmp);

	//貼背景
	bufferDC.SetStretchBltMode(COLORONCOLOR);
	m_bk.StretchBlt(bufferDC, 0, 0, 
		m_sClient.cx, m_sClient.cy, SRCCOPY);

	//貼各個方向上的方塊
	m_leftUp.BitBlt(bufferDC, m_leftUp.GetDmdPt(), SRCCOPY);
	m_rightUp.BitBlt(bufferDC, m_rightUp.GetDmdPt(), SRCCOPY);
	m_leftDown.BitBlt(bufferDC, m_leftDown.GetDmdPt(), SRCCOPY);
	m_rightDown.BitBlt(bufferDC, m_rightDown.GetDmdPt(), SRCCOPY);

	//貼紅色方塊
	m_redDmd.BitBlt(bufferDC, m_redDmd.GetDmdPt(), SRCCOPY);

	//貼時間
	CString time = m_timer.ToString();

	CSize sTime = bufferDC.GetTextExtent(time);

	bufferDC.SetBkMode(TRANSPARENT);
	bufferDC.TextOutW((m_sClient.cx - sTime.cx) / 2, 0, time);

	//將緩衝DC中的圖貼到客戶區
	pDC->BitBlt(0, 0, m_sClient.cx, m_sClient.cy,
		&bufferDC, 0, 0, SRCCOPY);

	//回收記憶體資源
	bufferBmp.DeleteObject();
	bufferDC.DeleteDC();
}

釋放記憶體資源

void CHoldOnView::OnDestroy()
{
	CView::OnDestroy();

	//關閉計時器
	KillTimer(ID_MOVE);
	KillTimer(ID_INTERSECT);

	//釋放
	m_bk.ReleaseGDIPlus();
	m_leftUp.ReleaseGDIPlus();
	m_leftDown.ReleaseGDIPlus();
	m_rightUp.ReleaseGDIPlus();
	m_rightDown.ReleaseGDIPlus();
}

五、零積分原始碼下載

相關推薦

VC++遊戲開發#2D —— 遊戲看看堅持多少

我的郵箱:[email protected]    歡迎大家和我交流程式設計心得前段時間在網上看見了一個小遊戲——看看你能堅持多少秒——考你的敏捷性,我玩了幾次,然後居然超過了18秒事後,我用MFC模擬了該遊戲中方塊"撞牆反彈"的效果——撞牆反彈效果今天呢,我就繼續

UE4 全域性光照明虛幻引擎學習渲染模組全域性光照明 by UWA侑虎科技

◆◆◆◆◆ 全域性光照總結 Unreal 4引擎提供了完整的工具鏈來支援靜態和動態物體的全域性光照效果。在計算全域性光照時採用了Photon Mapping演算法,通過不同的引數設定可以達到不同質量的全域性光照效果。Unreal 4引擎提供了同一場景使用不同全域性光照設定的功能Light Scena

死磕Java併發-----J.U.CAQS阻塞和喚醒執行緒

此篇部落格所有原始碼均來自JDK 1.8 線上程獲取同步狀態時如果獲取失敗,則加入CLH同步佇列,通過通過自旋的方式不斷獲取同步狀態,但是在自旋的過程中則需要判斷當前執行緒是否需要阻塞,其主要方法在acquireQueued(): if (sho

死磕Java併發—– J.U.CAQS同步狀態的獲取與釋放

此篇部落格所有原始碼均來自JDK 1.8在前面提到過,AQS是構建Java同步元件的基礎,我們期

死磕Java併發-----J.U.CAQSAQS簡介

Java的內建鎖一直都是備受爭議的,在JDK 1.6之前,synchronized這個重量級鎖其效能一直都是較為低下,雖然在1.6後,進行大量的鎖優化策略(【死磕Java併發】—–深入分析synchronized的實現原理),但是與Lock相比synchroni

死磕Java併發-----J.U.CAQSCLH同步佇列

此篇部落格所有原始碼均來自JDK 1.8 CLH同步佇列是一個FIFO雙向佇列,AQS依賴它來完成同步狀態的管理,當前執行緒如果獲取同步狀態失敗時,AQS則會將當前執行緒已經等待狀態等資訊構造成一個節點(Node)並將其加入到CLH同步佇列,同時會

Visual C++遊戲開發十一 淺墨DirectX教程十九 網格模型進階

                最近幾個星期,不停地收到大家的評論和郵件,大家都說希望淺墨早點講骨骼動畫。本來按淺墨擬定的寫作計劃是把骨骼動畫放到很後面,因為骨骼動畫知識需要前面的網格模型知識為基礎,知識量本身有些大,很可能要佔很多次更新的篇幅。但是看到大家一致的評論,都說非常期待骨骼動畫。那就好吧,我們就來

Visual C++遊戲開發 淺墨DirectX教程 水乳交融的美學alpha混合技術

本系列文章由zhmxy555(毛星雲)編寫,轉載請註明出處。  在這篇文章裡面,我們一起非常詳細地探討了Direct3D中Alpha混合相關的內容。首先是認識了Alpha通道與混合技術,然後結識了融合因子,瞭解了融合運算方式和融合因子的取法,以及Alpha的三處來源,接著是大

Visual C++遊戲開發十六 淺墨DirectX教程十三 打造遊戲GUI介面(一)

眾所周知,GUI是遊戲中不可缺少的元素,這篇文章中,我們首先了解了遊戲GUI介面的知識與相關概念,然後一起設計了一個封裝好GUI圖形介面的C++類。這個類有著非常強的擴充套件性,使用也是極其方便,很適合二次開發。 先看一張實現的效果圖吧: 其中的背景音樂,遊戲圖

Eclipse提高開發速度-插件安裝VJET插件,JS等提示開發插件

cau sof sta scrip update 1.4 spa text article 1、安裝Apache Batik CSS 一般安裝VJET插件會出現 Cannot complete the install because one or more requir

[置頂]實用 .NET Core開發系列- 導航

ati res pos dock 持續集成 cat swa bapi vue 前言 此系列從出發點來看,是 上個系列的續篇, 上個系列因為後面工作的原因,後面幾篇沒有寫完,後來.NET Core出來之後,註意力就轉移到了.NET Core上,所以再也就沒有繼續下去,此是原因

U3D系統架構教程——開發Log日誌系統2.0

這篇文章由唐三胖ヾ(•ω•`)o網路整理總結,將告訴你如何開發一個高效率的日誌系統。 通過這篇文章,你可以知道 1)C#特性Condtional 2)開發2.0版的日誌系統 開篇介紹 通過上一章節的介紹,我們已經實現了重寫的日誌

VC MFC開發Dll 中對話方塊的控制元件無法接受到按鍵訊息的解決辦法

使用DLL注入到 別的程式中時,發現DLL的視窗過程無法響應WM_CHAR事件,摸索了很久,才找到原因,給碰到同樣問題的人蔘考。 WNDPROC OldComboEditProc = NULL; LRESULT CALLBACK ComboEditProc(   HW

[轉]比特幣錢包開發 新建錢包生成子賬號地址與路徑

本文轉自:https://www.chaindesk.cn/witbook/9/74 課程目標掌握連線到比特幣正式網路與測試網進行開發學會建立錢包與備份錢包生成賬號地址前言比特幣的賬號是通過bip協議生成種子,然後擴充套件成多個子地址,這些子地址都由同一個種子可以推匯出來,而知道其中一個子賬號的私鑰不能推導

pygame遊戲編程第一-----創建一個窗口

屏幕 surface 這就是 程序 set_mode exit span get while 下面我們一起來創建一個背景為藍色的窗口作為遊戲編程的開始: import sys import pygame def creat_screen(): #初始化py

pygame遊戲編程第二-----移動圖像

center pan set com 設置圖 file rec game 防止 Learning From Here import pygame import sys pygame.init() screen_width = 640 screen_high =

Android開發-5界面裝修,五大布局選誰

比例 技術分享 article 嵌套 content java lin layout mark 前言:假設要開一家店,門店裝修是非常重要的事情。有錢都請專門的建築設計公司來設計裝修,沒錢的僅僅能自己瞎折騰。好不好看全憑自己的感覺。像Android開發。在移動端大家看到的

VC編程技巧窗口?3.6以漸變效果加載對話框

ng- 屏幕 運行 程序 true 周期性 利用 win rect 平時我們常常能夠看到非常多應用程序啟動過程非常酷。什麽百葉窗。漸變,各種效果,今天我們看一下怎樣在程序中添加這樣的效果。 一、演示樣例展示: 二、演示樣例思路: 1.首先

2017-07-01Linux應用開發工程師面試問題記錄關於結構體的大小及內存對齊問題

偶數 而且 strong span net 但是 開發 f11 flag Tencent後臺服務器開發有一道題是計算一個結構體的sizeof的大小: struct strData { int m_Int; char m_Char; short m_Short; char

iOS與EV3混合機器人編程系列工欲善其事,必先利其器(準備

style 混合 版權 相同 開發 code 操作系統 圖形 ipa 在上一篇文章中,我們論述了iOS與EV3結合後機器人開發的無限可能。那麽,大家要不要一起來Hacking一把呢?為了能夠完整地完畢我接下來我講的項目。我們須要做下面準備:1、一臺Mac執行MAC OS