本系列文章由七十一雾央编写,转载请注明出处。
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)
{
//出界了返回false
if(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;
//缓冲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 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
// 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_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);
//创建缓冲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::OnMouseMove(UINT nFlags, CPoint point)
{
m_scene->UpdateFogArea(point.x,point.y);
}
本节笔记源代码点击这里下载
《C++游戏开发》笔记十二到这里就结束了,更多精彩请关注下一篇。如果您觉得文章对您有帮助的话,请留下您的评论,点个赞,能看到你们的留言是我最高兴的事情,因为这让我知道我正在帮助曾和我一样迷茫的少年,你们的支持就是我继续写下去的动力,愿我们一起学习,共同努力,复兴国产游戏。
对于文章的疏漏或错误,欢迎大家的指出。
分享到:
相关推荐
《C++游戏开发》笔记十二 战争迷雾的初步实现 源代码 配套博文http://blog.csdn.net/hust_xy/article/details/9475979 下载的朋友们如果觉得有帮助,请到博文支持一下雾央,好吗 有大家的支持,雾央就会有动力继续...
通过深入研究《C++游戏开发》笔记中的内容,开发者不仅可以掌握战争迷雾的实现原理,还能学习到C++编程、图形渲染和游戏逻辑设计等多方面技能。结合提供的源代码,这将是一个极好的学习和实践平台,有助于提升游戏...
《Visual C++游戏开发经典案例详解》这本书是针对使用Visual C++进行游戏开发的专业指南,旨在帮助读者通过实例学习和掌握C++编程语言在游戏开发中的应用。书中的内容覆盖了从基础到高级的游戏开发技术,包括图形...
C++服务器开发精髓笔记 本笔记涵盖了C++服务器开发的精髓知识点,从RAII到Pimpl、C++11新特性、统一的类成员初始化语法、std::initializer_list、C++17注解标签等多方面内容。 1. RAII(Resource Acquisition Is ...
C++学习笔记C++学习笔记C++学习笔记C++学习笔记C++学习笔记
在本篇中,我们将深入探讨使用...通过"【Visual C++】游戏编程学习笔记之九",我们可以逐步学习并掌握这些技能,实现一个功能完备的回合制游戏。在实际开发过程中,不断迭代和优化,将使你的游戏更具吸引力和竞争力。
C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。
在本次C++编程实践中,我们以经典游戏“坦克大战”为例,结合Qt库进行开发。Qt是一个跨平台的应用程序开发框架,广泛应用于桌面和移动设备。这个项目的目标是帮助开发者深入理解和运用C++面向对象编程,同时体验游戏...
- **游戏开发**:C++ 因其高性能特性,被广泛应用于游戏开发。 - **科学计算**:在科研领域,C++ 能够处理复杂的数学模型和大规模的数据集。 - **网络通讯**:如 ACE(Adaptive Communication Environment)这样的...
C++(qt)游戏实战项目:...此坦克游戏采用c++开发,有详细的源码解析 已实现功能 地图编辑m,地图加载l,地图保存s 玩家坦克移动asdw 发射子弹j 发挥你的才智实现以下功能吧: 关卡设计 坦克打怪升级 坦克魔法攻击 等
《千锋C++笔记》是一份综合性的学习资料,涵盖了C++编程语言的基础到高级概念。这份笔记由知名教育机构“千锋”提供,旨在帮助初学者和有一定基础的程序员深入理解和掌握C++这一强大的系统级编程语言。下面将详细...
C++是一种广泛应用于游戏开发的强大编程语言,因为它提供了对硬件的直接访问能力,能够实现高性能的图形处理和复杂的逻辑计算,是许多大型游戏引擎如Unreal Engine和Unity3D的基础。 在描述中提到的“英文影印清晰...
C++是一种广泛应用于系统软件、应用软件、游戏开发、嵌入式系统等领域的高级编程语言。这份"完整的C++课件以及笔记整理"是学习C++的宝贵资源,包括了课件、源码和笔记,适合初学者和有一定基础的学习者进行深入研究...
在本资源中,标题"用C++实现的经典小游戏源代码"揭示了主要的主题,即使用C++编程语言编写的经典小游戏的源代码。这通常包括一些基础和进阶的编程概念,适合学习C++和游戏开发的初学者,以及希望巩固编程技能的...
《Visual C++游戏开发技术与实例》这本书是针对游戏开发初学者和爱好者的一本实用教程,它涵盖了使用Microsoft Visual C++进行游戏编程的基础知识和技术。通过本书的学习,读者可以了解到如何利用C++语言和Visual ...
【Visual C++】游戏开发笔记系列教程,从入门讲起,也包括有一定深度的direct编程
c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏...
【Visual C++】游戏开发笔记系列教程,从入门讲起,也包括有一定深度的direct编程
【C语言和C++Builder学习笔记】 C语言和C++是两种广泛使用的编程语言,它们在软件开发领域占据着重要地位。C语言以其简洁、高效和底层操作能力著称,而C++则在C的基础上引入了面向对象编程,极大地扩展了其应用范围...