本系列文章由七十一雾央编写,转载请注明出处。

http://blog.csdn.net/u011371356/article/details/9475979

作者:七十一雾央 新浪微博:http://weibo.com/1689160943/profile?rightmod=1&wvr=5&mod=personinfo

一、前言

在这里雾央先解释一下战争迷雾的概念,以下内容引自维基百科:
战争迷雾(Fogof War),在传统意义上是指战争中由于对敌人情报不清楚而无法确认除友军所在以外的大部分地区,敌人的分布及活动情况。而目前在游戏范围内,尤其是即时战略类游戏中,这个词语出现的频率更高一些也更被多数人所熟悉。
从最初的即时战略《沙丘2》开始,战争迷雾的概念开始被引入和正式提出。在沙丘中每一次新开始游戏时,玩家只能观察到自己基地及单位周围极小的范围,而绝大多数地图区域均被黑色遮盖。当他命令单位向黑暗区移动后,经过的区域会被自动打开,地图变得可见,包括该区域的地形/敌人活动情况等等。这一经典模式也被绝大多数后来的即时策略游戏继承。
最初出现战争迷雾的沙丘2中,这些迷雾是一次性的,当玩家移动并探索一次后,绝大多数情况下将不再需要探索即可永久享有该处的情报。
在随后出现的下一个著名即时战略游戏《命令与征服》中,开始引进重复探测的概念,在非剧情模式的单人游戏下,可以设置迷雾是否再生,一旦选项生效,所有探测过的地区,其周边的黑暗区域每过一段时间即会膨胀再次积压掉一部分已探索的区域。同时游戏中也首次出现了反情报的设施,用于产生永久性的迷雾,进入该区域的对手,只要单位离开,几乎立刻就会重新被战争迷雾所遮住。第3个关于战争迷雾的新创意则是通过一些手段来确保永久性全地图全开。
《命令与征服》之后,游戏中的战争迷雾逐渐被普遍分割定义为地图层和单位层两种,地图层所包括的地形,由于很难改变或者根本不可能变化,在单位移开后仍然能保证其情报有效性,而单位层主要指该区域的活动单位之情况,由于不可能确保对方仍然停留,在我方情报源消失(如侦查单位移动开)之后,即会再次被遮盖。对两种层次的迷雾约定俗成,使用不同程度的黑色来区分,地图层的黑色更深,而地图层打开之后,遗留下的单位层迷雾相对更淡。以上这种战争迷雾形式相对更经典和受到普遍采用。
在即时战略的发展中,另外出现了更多关于战争迷雾的变化及设计,比如将不同单位的打开迷雾的能力区分以体现不同价值,将战争迷雾的获得能力与地形结合,站在高处能获得更多视野,低处不能观察高处,一些游戏开始取消地图层迷雾的设计,地形图从一开始就对所有玩家开放,等等。
另外很多回合策略中也吸收了战争迷雾的概念,尤其出现于4X概念体系的作品中。比如文明中需要单位移动才能打开地图,单位站在高处能额外获得多1格的视野(3代),从4X之一元素--探索的角度来说,战争迷雾是4X游戏所必不可少的概念。
如下面这张图


在这个截图中,我们可以看到两种战争迷雾:颜色较浅的部分为已探开区域,颜色较深的部分为单位层迷雾,全黑色部分为地图层迷雾。(游戏来源:《命令与征服:将军绝命时刻》)

像上面那样精致的战争迷雾的实现是比较困难的一件事,雾央肯定是没有能力实现的,而且在百度和谷歌中关于游戏中战争迷雾的资料非常少,达到了稀有的程度,所以这方面的知识学习起来很不容易,但这并不妨碍我们自己实现一些简陋的战争迷雾的效果。
在这一节笔记里,雾央将实现一种最简单的战争迷雾的效果。在接下来的几节笔记里,雾央将利用地图拼接算法实现过渡比较平滑一点的战争迷雾效果,地图拼接算法参考了网上一篇文章,雾央也将原作者的地址贴出来,供大家参考,雾央也会自己用C++带着大家实现一遍,在此先向原作者致以崇高的敬意。
地图拼接算法:地图拼接与战争迷雾,使用AS3实现

二、效果

雾央这一节里实现的效果如下,大家将鼠标想象成人物,鼠标的移动大家自行脑补成人物在移动,哈哈,人物周围的空间就是被照亮的。

明亮处看到的仍然是大雪纷飞的场景,呵呵

最上面看到一小部分

话说雾央开始打算将鼠标指针换成蜡烛,这样看起来就有感觉多了,但是可惜没有找到蜡烛图案的cur文件,不过大家可以试着去实现人物端着蜡烛在房间里行走的demo,呵呵。

三、原理

下面雾央就来详细解释一下实现原理。
大家看着效果图,就知道了雾央是把地图分成了一个个网格,地图背景是800*600,雾央采用的每个小网格是20*20,这样整个地图就是40*30个网格了。这就是之前我们提到过的TileMap,只不过我们的地图分成了两层,背景层和前景迷雾层,背景层是一张整图,而迷雾是TileMap。
我们使用的黑色迷雾就是下面这个小图了


大家还记得之前提到过的TileMap吗?前面说过,我们可以使用一个二维数组来保存地图,在这里我们可以使用1表示可见,0表示迷雾区域。
那么原理就很简单了,我们将鼠标所在位置的周围区域的Tile设置为可见,其他设置为不可见即可了。
大家可以看到,这样实现起来非常的简单,但是效果非常的差。迷雾的散开是以一个个方块进行的,迷雾的边缘处看起来就是方方正正的,离游戏中的光滑边缘差别很远,毕竟实现原理过于简陋了。但是在采用聪明或复杂的算法之前,我们仍然可以对这种方法进行改进,比如,我们减小方块的大小,由20*20变为5*5,那么效果看起来就会好很多,如果方块可以尽可能小,那么效果就越来越接近于平滑,如果方块可以只有一个像素大,每次展开一个圆,那么和圆的大小就几乎差不多了吧,当然这样带来的贴图次数也多了很多。

四、实现

雾央封装了一个场景类:

class CScene
{
private:CImage m_bg;      //背景图片CImage m_black; //每块迷雾大小为20*20,对于800*600的窗口即有40*30个小迷雾块组成int m_fogArray[40][30];
public:CScene(char *bg);~CScene();
public://绘制背景void DrawBG(CDC &cDC);//绘制迷雾void DrawFog(CDC &cDC);//更新迷雾区域void UpdateFogArea(int x,int y);
};

实现为:

#include"stdafx.h"
#include"scene.h"CScene::CScene(char *bg)
{m_bg.Load(bg);m_black.Load("black.png");//将数组清0,0表示为黑色迷雾状态memset(m_fogArray,0,sizeof(m_fogArray));
}//绘制背景
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<40;i++)for(int j=0;j<30;j++){if(m_fogArray[i][j]==0)m_black.Draw(cDC,i*20,j*20,20,20);}
}bool CheckFog(int xBox,int yBox,int xMouse,int yMouse)
{//出界了返回falseif(xBox<0 || xBox>=40 || yBox<0 || yBox>=30)return false;//未出界,则距离鼠标点击中心小于一定的范围内可见if( (xBox-xMouse)*(xBox-xMouse) + (yBox-yMouse)*(yBox-yMouse) <=16)return true;else return false;
}
//更新迷雾区域
void CScene::UpdateFogArea(int x,int y)
{//首先计算出鼠标所在的格子int xPosBox=x/20;int yPosBox=y/20;//将迷雾区域复原memset(m_fogArray,0,sizeof(m_fogArray));//设置可见区域for(int xBox=xPosBox-8;xBox<xPosBox+8;xBox++){for(int yBox=yPosBox-8;yBox<yPosBox+8;yBox++){if(CheckFog(xBox,yBox,xPosBox,yPosBox))m_fogArray[xBox][yBox]=1;}}
}

接着是CChildView.h

// 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;//缓冲DCCDC 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 OnMouseMove(UINT nFlags, CPoint point);
};

最后是CPP

之前对场景类,雾央忘记了释放资源,感谢sszgg2006指出,要在析构函数中添加一句delete m_scene


-----【程序说明】----------------------------------------------
// 【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// CChildViewCChildView::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_MOUSEMOVE()
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);//创建缓冲DCm_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);//释放缓冲DCm_cacheDC.DeleteDC();//释放对象m_cacheCBitmap.DeleteObject();//释放窗口DCReleaseDC(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::OnMouseMove(UINT nFlags, CPoint point)
{m_scene->UpdateFogArea(point.x,point.y);
}

本节笔记源代码点击这里下载

《C++游戏开发》笔记十二到这里就结束了,更多精彩请关注下一篇。如果您觉得文章对您有帮助的话,请留下您的评论,点个赞,能看到你们的留言是我最高兴的事情,因为这让我知道我正在帮助曾和我一样迷茫的少年,你们的支持就是我继续写下去的动力,愿我们一起学习,共同努力,复兴国产游戏。

对于文章的疏漏或错误,欢迎大家的指出。

《C++游戏开发》笔记十二 战争迷雾:初步实现相关推荐

  1. 【Visual C++】游戏开发笔记十二 游戏输入消息处理(一) 键盘消息处理

    相信大家都熟悉<仙剑奇侠传98柔情版>的人机交互方式,用的仅仅是键盘.在那个物质并不充裕的时代,一台配置并不高的电脑,一款名叫<仙剑奇侠传>的游戏,却能承载一代人对梦想的追逐. ...

  2. 【Visual C 】游戏开发笔记十二 游戏输入消息处理 一 键盘消息处理

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  3. 【Visual C++】游戏开发笔记十五 游戏人工智能(一) 运动型游戏AI

    分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 本系列文章由zhm ...

  4. 【转】【Visual C++】游戏开发笔记十五 游戏人工智能(一) 运动型游戏AI

    原文连接:http://www.cnblogs.com/dyllove98/archive/2012/04/07/2461865.html#commentform 我们常常听闻AI(Artificia ...

  5. 【Visual C++】游戏开发笔记十五 游戏人工智能(一) 运动型游戏AI .

    本系列文章由zhmxy555编写,转载请注明出处.  http://blog.csdn.net/zhmxy555/article/details/7434317 作者:毛星云    邮箱: happy ...

  6. [原]【Visual C++】游戏开发笔记十五 游戏人工智能(一) 运动型游戏AI

    本系列文章由zhmxy555编写,转载请注明出处.  http://blog.csdn.net/zhmxy555/article/details/7434317 作者:毛星云    邮箱: happy ...

  7. 游戏开发笔记十五 游戏人工智能(一) 运动型游戏AI

    我们常常听闻AI(Artificial Intelligence人工智能)这个名词,比如Dota里面的AI地图.写这篇文章的时候,最新版的Dota AI是6.72f,估计过几天6.73的AI也要出来了 ...

  8. 【Visual C++】游戏开发笔记十六 讲解一个完整的回合制游戏demo

    本系列文章由zhmxy555编写,转载请注明出处. 文章链接  http://blog.csdn.net/zhmxy555/article/details/7447864 作者:毛星云    邮箱:  ...

  9. 【Visual C 】游戏开发笔记十六 讲解一个完整的回合制游戏demo

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

最新文章

  1. linux驱动:i2c驱动(二)
  2. 华为OJ系列之---表示数字
  3. 模板类的全特化、偏特化
  4. 第三十三讲:tapestry表单组件详解之Label
  5. 背景图片自适应,不重复
  6. 幼儿园管理系统源码【免费分享】
  7. Python可视化深度图
  8. apollo学习之:如何测试canbus模块
  9. DataGridView 获取当前行数据
  10. c语言程序商品的打折,C语言程序设计习题doc.doc
  11. 企业微信企业邮箱设置,微信企业邮箱如何设置?
  12. flutter微信分享,qq分享
  13. 2020前端最新面试题总结(js、html、小程序、React、ES6、Vue、算法、全栈热门视频资源)(3年前端菜鸟级开发师含泪总结)
  14. Hadoop 为什么不建议使用 RAID?
  15. 前端兼容性问题解决方案
  16. 高质量 ppt 免费下载网站
  17. 简报 | 俄罗斯为离岸地区制定特殊加密货币规则
  18. elasticsearch修改字段类型
  19. WebSocket实现多屏互动的分析及方案
  20. 某大型连锁农贸市场预付费系统设计与应用

热门文章

  1. 华为机试HJ98:自动售货系统
  2. 计算机系 跨考 天文,非天文本科生,可以跨专业考天文方面的研究生吗?
  3. 2019电子科大计算机基础知识,电子科技大学820真题1999-2019终极版.pdf
  4. linux运行sqlite,Linux上安装sqlite3
  5. 经典html,经典 HTML
  6. rds本地库到rds恢复_阿里云rds 备份和还原
  7. java 输入输出流知识_Java知识点总结(JavaIO-字节流)
  8. 黑马博客——详细步骤(十)项目功能的实现之开发环境与生产环境
  9. 【转】Zookeeper入门
  10. 自动化测试unittest测试框架实例