本系列文章由七十一雾央编写,转载请注明出处。
http://blog.csdn.net/u011371356/article/details/9712321
作者:七十一雾央 新浪微博:http://weibo.com/1689160943/profile?rightmod=1&wvr=5&mod=personinfo
这两天不少朋友留言提出了一些问题,但是由于雾央家里网络出了点问题,所以这两天都上不了网,没有及时回答大家,关注了雾央微博的朋友就知道这件事,抱歉了。
另外,欢迎转载文章,雾央会把它当成对自己的认可~(@^_^@)~,但是请不要删除第一段话或者注明一下原文地址,好吗?请尊重一下作者的劳动。
一、原理回顾
今天继续来说战争迷雾,上一节介绍了一下战争迷雾的原理,不知道大家清楚了没?如果没清楚,也不要紧,现在再来啰嗦几句哈。
我们的素材是下面这张图

我们还是图解吧,这样应该更形象,先给它编上号。

用鼠标点击一下,散开一片迷雾,大家可以看到上面标示的数字,左上角是4,右上角是8,左下角是1,右下角是2

在右边再点一下,我们可以看到两片迷雾叠加起来了,过渡的很自然。大家注意一下数字,两片迷雾中间的数字变成了12=4+8,3=2+1

继续点,同理

看了上面的图,大家应该清楚了吧,雾央假定大家都清楚了~(@^_^@)~,如果有问题的朋友可以留言或者微博@七十一雾央。
我们每次点击游戏窗口的时候,驱散一个圆形的迷雾,这个圆形就只需要1+2+4+8号图元拼接起来就可以了,当同一个Tile内有多个图元时,将它们的数字相加,用新数字的图元替换掉即可。
二、实现步骤
我们知道,把上面的鼠标换成人物,就可以营造出游戏中的战争迷雾效果:随着人物的走动,迷雾散开,合理的方式应该是以人物为中心散开迷雾,就像魔兽那样。但是雾央简化了一下问题,采用的是以鼠标为左上角散开迷雾。以鼠标为中心散开留给大家完成,也就是加个判断,找出鼠标附近的四个方块。
如果大家看过了上上一节,即战争迷雾的初步实现,那么就容易多了,因为区别只存在于两个地方:绘图函数和更新函数。
这次雾央用了一个大一点的地图1280*640,为了多点几下,呵呵。我们的图元方块是128*128的,那么网格就是10*5个。
现在大家都清楚了每个网格要贴它的数字的图,那么我们怎么找到数字为n的图元的起始坐标呢?
大家观察一下下面的图,找找规律

大家发现了没有?每一列的数字除以4得到的商是相同的,分别为0,1,2,3;每一行的数字对4取余得到的结果也是相同的,分别是0,1,2,3!
那么问题就简单了,编号为n的图元的起始坐标
x=(n/4)*128,
y=(n%4)*128
那么我们绘制战争迷雾的函数就可以修改成下面这样了
//绘制战争迷雾
void CScene::DrawFog(CDC &cDC)
{
for(int i=0;i<10;i++)
for(int j=0;j<5;j++)
m_black[m_mode].Draw(cDC,i*128,j*128,128,128,(m_fogArray[i][j]/4)*128,(m_fogArray[i][j]%4)*128,128,128);
}
接下来要处理的就是更新迷雾区域的函数了
我们首先计算出鼠标点击的格子编号
//首先计算出鼠标所在的格子
int xPosBox=x/128;
int yPosBox=y/128;
如果这个格子没有被点击过,那么就展开迷雾,并进行数值叠加,注意如果数字达到了15以上,就保持15,因为15已经是全开的状态了,在上一节雾央提过,如果是用于地形拼接的话,那么就可以在几种铺满状态的草地图案随机选择,造成丰富的地形效果。另外,雾央偷懒了,没有进行数组边界判断!但是呢,为了防止数组越界导致的错误,雾央就把数组扩大了一点,变成11*6的数组,这样就不会有越界错误了。
void Add(int fogArray[][6],int i,int j,int num)
{
fogArray[i][j]+=num;
if(fogArray[i][j]>15)
fogArray[i][j]=15;
}
//更新迷雾区域
void CScene::UpdateFogArea(int x,int y)
{
//首先计算出鼠标所在的格子
int xPosBox=x/128;
int yPosBox=y/128;
if(m_clickArray[xPosBox][yPosBox]==0)
{
//左上+4,右上+8,左下+1,右下+2
Add(m_fogArray,xPosBox,yPosBox,4);
Add(m_fogArray,xPosBox+1,yPosBox,8);
Add(m_fogArray,xPosBox,yPosBox+1,1);
Add(m_fogArray,xPosBox+1,yPosBox+1,2);
//点过的地方已经散开过一次了,就不再叠加
m_clickArray[xPosBox][yPosBox]=1;
}
}
另外,为了帮助大家理解,雾央设置了两种模式,一种是显示出迷雾状态,另一种会多显示出每块迷雾图元的编号数字,按‘Q’键可以在两种状态之间切换。
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if(nChar=='Q' || nChar=='q')
m_scene->ChangeMode();
}
大家可以直接在图元上输出数字,雾央是干脆直接用了两张图,一张带数字,一张不带,呵呵。0和1表示两种模式,那么在0和1之间切换,就和1异或就好了,0^1=1,1^1=0。
void CScene::ChangeMode()
{
m_mode^=1;
}
场景类现在的代码就如同下面这样
头文件
class CScene
{
private:
CImage m_bg; //背景图片
CImage m_black[2];
int m_mode; //显示模式
//每块迷雾大小为128*128,对于1280*640的窗口即有10*5个小迷雾块组成
int m_fogArray[11][6];
//每块是否被点击过
int m_clickArray[11][6];
public:
CScene(char *bg);
~CScene();
public:
void ChangeMode();
//绘制背景
void DrawBG(CDC &cDC);
//绘制迷雾
void DrawFog(CDC &cDC);
//更新迷雾区域
void UpdateFogArea(int x,int y);
};
实现文件
CScene::~CScene()
{
m_bg.Destroy();
m_black[0].Destroy();
m_black[1].Destroy();
}
CScene::CScene(char *bg)
{
m_bg.Load(bg);
m_black[0].Load("fog.png");
m_black[1].Load("fog2.png");
m_mode=0;
//将数组清0,0表示为黑色迷雾状态
memset(m_fogArray,0,sizeof(m_fogArray));
memset(m_clickArray,0,sizeof(m_clickArray));
}
//绘制背景
void CScene::DrawBG(CDC &cDC)
{
m_bg.Draw(cDC,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,0,0,WINDOW_WIDTH,WINDOW_HEIGHT);
}
//绘制战争迷雾
void CScene::DrawFog(CDC &cDC)
{
for(int i=0;i<10;i++)
for(int j=0;j<5;j++)
m_black[m_mode].Draw(cDC,i*128,j*128,128,128,(m_fogArray[i][j]/4)*128,(m_fogArray[i][j]%4)*128,128,128);
}
void Add(int fogArray[][6],int i,int j,int num)
{
fogArray[i][j]+=num;
if(fogArray[i][j]>15)
fogArray[i][j]=15;
}
//更新迷雾区域
void CScene::UpdateFogArea(int x,int y)
{
//首先计算出鼠标所在的格子
int xPosBox=x/128;
int yPosBox=y/128;
if(m_clickArray[xPosBox][yPosBox]==0)
{
//左上+4,右上+8,左下+1,右下+2
Add(m_fogArray,xPosBox,yPosBox,4);
Add(m_fogArray,xPosBox+1,yPosBox,8);
Add(m_fogArray,xPosBox,yPosBox+1,1);
Add(m_fogArray,xPosBox+1,yPosBox+1,2);
//点过的地方已经散开过一次了,就不再叠加
m_clickArray[xPosBox][yPosBox]=1;
}
}
void CScene::ChangeMode()
{
m_mode^=1;
}
PS:雾央之前使用CImage贴图的时候一直没有主动释放资源,最近才发现这个问题,对不住大家了。大家在析构函数里都加上Destroy函数释放一下资源,要不然会产生内存泄露。
运行看到的效果就是下面这样,看起来还可以吧,哈哈


三、带详细注释的源代码
头文件
// ChildView.h : CChildView 类的接口
//
#pragma once
#include "particle.h"
#include "scene.h"
// CChildView 窗口
class CChildView : public CWnd
{
// 构造
public:
CChildView();
// 特性
public:
//保存客户区大小
CRect m_client;
//雪花
CParticle *m_snow;
//场景
CScene *m_scene;
//缓冲DC
CDC m_cacheDC;
//缓冲位图
CBitmap m_cacheCBitmap;
// 操作
public:
// 重写
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 实现
public:
virtual ~CChildView();
// 生成的消息映射函数
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
};
CPP
//-----------------------------------【程序说明】----------------------------------------------
// 【MFC游戏开发】笔记十四 战争迷雾 配套源代码
// VS2010环境
// 更多内容请访问雾央CSDN博客 http://blog.csdn.net/u011371356/article/category/1497651
// 雾央的新浪微博: @七十一雾央
//------------------------------------------------------------------------------------------------
// ChildView.cpp : CChildView 类的实现
//
#include "stdafx.h"
#include "GameMFC.h"
#include "ChildView.h"
#include "mmsystem.h"
#pragma comment(lib,"winmm.lib")//导入声音头文件库
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CChildView
CChildView::CChildView()
{
}
CChildView::~CChildView()
{
mciSendString("stop bgMusic ",NULL,0,NULL);
delete m_snow;
delete m_scene;
}
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_TIMER()
ON_WM_CREATE()
ON_WM_LBUTTONDOWN()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
// CChildView 消息处理程序
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
//-----------------------------------游戏数据初始化部分-------------------------
//打开音乐文件
mciSendString("open background.mp3 alias bgMusic ", NULL, 0, NULL);
mciSendString("play bgMusic repeat", NULL, 0, NULL);
//雪花
m_snow=new CParticle(100);
m_snow->Init();
//场景
m_scene=new CScene("bg.png");
return TRUE;
}
void CChildView::OnPaint()
{
static float lastTime=timeGetTime();
static float currentTime=timeGetTime();
//获取窗口DC指针
CDC *cDC=this->GetDC();
//获取窗口大小
GetClientRect(&m_client);
//创建缓冲DC
m_cacheDC.CreateCompatibleDC(NULL);
m_cacheCBitmap.CreateCompatibleBitmap(cDC,m_client.Width(),m_client.Height());
m_cacheDC.SelectObject(&m_cacheCBitmap);
//————————————————————开始绘制——————————————————————
//贴背景,现在贴图就是贴在缓冲DC:m_cache中了
m_scene->DrawBG(m_cacheDC);
//贴雪花
m_snow->Draw(m_cacheDC);
//更新雪花
currentTime=timeGetTime();
m_snow->Update(currentTime-lastTime);
lastTime=currentTime;
//画出战争迷雾
m_scene->DrawFog(m_cacheDC);
//最后将缓冲DC内容输出到窗口DC中
cDC->BitBlt(0,0,m_client.Width(),m_client.Height(),&m_cacheDC,0,0,SRCCOPY);
//————————————————————绘制结束—————————————————————
//在绘制完图后,使窗口区有效
ValidateRect(&m_client);
//释放缓冲DC
m_cacheDC.DeleteDC();
//释放对象
m_cacheCBitmap.DeleteObject();
//释放窗口DC
ReleaseDC(cDC);
}
//定时器响应函数
void CChildView::OnTimer(UINT_PTR nIDEvent)
{
OnPaint();
}
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
//创建一个10毫秒产生一次消息的定时器
SetTimer(TIMER_PAINT,10,NULL);
return 0;
}
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_scene->UpdateFogArea(point.x,point.y);
}
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if(nChar=='Q' || nChar=='q')
m_scene->ChangeMode();
}
四、游戏推荐
另外,大家都对游戏开发感兴趣,但是要做出好的游戏,我们也得首先看看别人都做出了些什么好玩的东西,感受一下别人的创意。所以从这一节开始,以后雾央会每次给大家推荐一个好玩的小游戏,一般都是很有创意或很有意思的游戏,雾央都亲测过,当然大众都知道的就不会推荐了,欢迎关注和向雾央推荐你知道的创意游戏。
今天给大家推荐一个小游戏,名字叫“打我啊”,这个游戏做的比较简陋,可能是作者随手弄的,但是挺有意思的。它就是一个打蜜蜂那种的小游戏,但是每当你胜利进入下一关的时候,AI的操作和你上一关一模一样,所以不做死就不会死,哈哈。
游戏地址请戳这里:
想玩请点击这里
源代码下载地址:请点这里下载源代码
《C++游戏开发》笔记十四到这里就结束了,更多精彩请关注下一篇。如果您觉得文章对您有帮助的话,请留下您的评论,点个赞,能看到你们的留言是我最高兴的事情,因为这让我知道我正在帮助曾和我一样迷茫的少年,你们的支持就是我继续写下去的动力,愿我们一起学习,共同努力,复兴国产游戏。
对于文章的疏漏或错误,欢迎大家的指出。
分享到:
相关推荐
本笔记将详细讲解如何实现平滑过渡的战争迷雾效果,以及提供相应的源代码供参考。 战争迷雾的核心是通过遮挡和可见性算法来隐藏地图上未被玩家探索的区域。一种常见的实现方法是使用A*寻路算法配合Fog of War(战争...
《C++游戏开发》笔记十二 战争迷雾的初步实现 源代码 配套博文http://blog.csdn.net/hust_xy/article/details/9475979 下载的朋友们如果觉得有帮助,请到博文支持一下雾央,好吗 有大家的支持,雾央就会有动力继续...
本笔记主要探讨的是如何在MFC(Microsoft Foundation Classes)框架下实现平滑动画,这通常是通过帧率控制、插值计算以及定时器事件来完成的。 首先,我们要理解平滑动画的基础——帧率控制。帧率是衡量游戏画面...
《Visual C++游戏开发经典案例详解》这本书是针对使用Visual C++进行游戏开发的专业指南,旨在帮助读者通过实例学习和掌握C++编程语言在游戏开发中的应用。书中的内容覆盖了从基础到高级的游戏开发技术,包括图形...
C++是一种广泛应用于游戏开发的强大编程语言,因为它提供了对硬件的直接访问能力,能够实现高性能的图形处理和复杂的逻辑计算,是许多大型游戏引擎如Unreal Engine和Unity3D的基础。 在描述中提到的“英文影印清晰...
在游戏开发中,加载界面(Loading Screen)是一个重要的组成部分,它在游戏启动或场景切换时显示,以便在后台处理资源的加载,同时为玩家提供视觉反馈,减少等待的枯燥感。本篇将深入探讨如何使用C++进行游戏加载...
在游戏开发中,地图迷雾效果是一种常见的设计,它用于模拟玩家视野的限制,增加游戏探索性和策略性。Cocos2d-x是一个流行的开源游戏引擎,支持多种平台,且使用C++作为主要开发语言。本篇文章将深入探讨如何在Cocos...
《Visual C++游戏开发技术与实例》这本书是针对游戏开发初学者和爱好者的一本实用教程,它涵盖了使用Microsoft Visual C++进行游戏编程的基础知识和技术。通过本书的学习,读者可以了解到如何利用C++语言和Visual ...
在本资源中,标题"用C++实现的经典小游戏源代码"揭示了主要的主题,即使用C++编程语言编写的经典小游戏的源代码。这通常包括一些基础和进阶的编程概念,适合学习C++和游戏开发的初学者,以及希望巩固编程技能的...
本资源实现如下:用C++架构了一个适合windows游戏开发的简易框架,效仿的MFC,能使用MFC类库进行面相对象游戏编程,简单实用 我将其取名为BCF:BlueCoder Frame 大家可以多多测试一下,如发现bug,请到我的博文中...
在C++游戏编程的世界里,开发者们利用C++的强大特性和效率来构建复杂、高性能的游戏。C++是一种静态类型的、编译式的、通用的、大小写敏感的、不仅支持过程化编程,也支持面向对象编程的程序设计语言。这篇PDF教程将...
在本文中,我们将深入探讨《MFC游戏开发》笔记三中的关键知识点——透明贴图技术。MFC(Microsoft Foundation Classes)是微软提供的一种C++库,用于构建Windows应用程序,包括游戏开发。透明贴图是游戏图形编程中...
这些小游戏的源代码能帮助初学者理解如何在C++中组织结构,实现游戏逻辑,以及如何调试和优化代码。对于交作业和课程设计,这些现成的例子可以作为起点,让学生在此基础上进行修改和扩展,既避免了从零开始的困难,...
《大富翁》是一款深受人们喜爱的桌面游戏,它的电子版同样吸引着众多程序员尝试用各种编程语言来实现。在C++中制作大富翁游戏,不仅能够锻炼编程能力,还能提升对面向对象编程的理解。这里我们将深入探讨C++游戏编程...
在本资源中,"Visual C++游戏开发经典案例详解 源代码" 包含了多个使用Microsoft Visual C++编写的经典游戏项目源代码。这些游戏涵盖了从简单到复杂的各种类型,是学习C++游戏编程的宝贵资料。下面将详细阐述每个...
C++是一种强大且广泛应用的编程语言,尤其在系统软件、应用软件、游戏开发、设备驱动等领域占据着重要地位。本资源集合了达内科技的C++课程资料,包括课件与源码笔记,为初学者提供了一条高效的学习路径。 课件部分...
源码的配套博文是 《 【Visual C++】游戏开发五十七 浅墨DirectX教程二十四 打造游戏GUI界面(二)》 ,文章地址http://blog.csdn.net/poem_qianmo/article/details/16922703, 点击Release文件夹下的exe文件可以...
本资源集合包含了多种语言和技术,包括C++、C#以及.NET,提供了AutoCAD二次开发的全面指导。 1. **C++ ARX(Application Resource eXtension)开发**: AutoCAD的ARX开发基于C++,是一种低级别但功能强大的接口。...
C++ 实现 Matlab Smooth 函数 C++ 平滑一维数组数据的...C++ 实现 Matlab Smooth 函数可以使用上述代码来实现平滑一维数组数据,该方法基于 Matlab 的 smooth 函数原理,并且可以在 C++ 中实现平滑信号处理的功能。
在C++游戏编程领域,开发各种小游戏是一种极好的学习实践方式。这个压缩包包含了丰富的实例,可以帮助初学者和有经验的开发者深入理解C++在游戏编程中的应用。下面,我们将详细探讨这些知识点。 1. **C++基础**: ...