说起即时战略游戏,我第一时间想起魔兽争霸,这个不知道陪伴我多少个日日夜夜,让我哭让我笑的游戏,让我想起了sky,moon,grubby等人牵动心弦的战斗历程,让我想起了当年日日守在电脑前专注的欣赏着wcg的每一场比赛,想起了当年学校门口的网吧里我跟我哥在浩方上奋力的拼杀着,想起了很多年前和寝室室友打赌谁输谁请一天杂粮饼的承诺。哎,不说了,说起来都是泪啊。那么进入本文的正题吧,用D3D加mfc编写一个即时战略游戏。

其实这个游戏只是一个很简单的demo,各位千万不要把他想复杂了,但是我也实现了即时战略游戏的基本需求:地图编辑器,人物寻路,动态行走,网络同步等功能。在写这个游戏之前,我也在网上拼命的搜寻着相关资料,却发现这方面的资料十分残缺,许多都是只言片语,所以我觉得我需要把自己掌握的东西与大家一起分享,这样也能让跟我一样迷茫的朋友从中多少有点收获。

任何一个像样的游戏都离不开一个地图编辑器,我们可以在这个编辑器上面去创建修改我们想要的地图,本文里的地图编辑器就是用mfc编写,而地图文件用xml储存,这样查看的话也很方便。下面给出一个地图文件的样式:

<?xml version="1.0"?>
<root><class type="house" width="50" height="50"><item left="780" top="460"/></class><class type="tree" width="50" height="50"><item left="940" top="410"/></class>
</root>

这个文件很简单,class节点就是地图上元素的类型,目前支持house和tree,item则是每一个该类型元素所在的位置。下面我们来看一下如何用mfc来编写这个地图编辑器。

首先,在我们的程序界面上需要一个可以实时渲染地图场景的窗口,这个窗口用d3d来渲染,那么这个窗口怎么实现,大家应该还记得在d3d初始化的时候会指定一个窗口的句柄,于是我便定义一个名叫m_DrawWnd的CStatic类型的变量,然后在OnInitDialog函数里面创建该窗口即可:

m_DrawWnd.Create(0,0,CRect(10,10,810,610),this,0);

m_DrawWnd.ShowWindow(1);

然后在d3d初始化的时候指定该窗口的句柄:

m_d3dpp.hDeviceWindow              = m_DrawWnd.m_hWnd;

m_d3dpp.BackBufferWidth            = 800;
m_d3dpp.BackBufferHeight           = 600;

是不是很简单,不过,等等,渲染那一块怎么办呢,mfc是消息驱动来重绘窗体的,很难做到实时渲染的啊,不要急,我仔细看了下mfc 的文档,发现WM_KICKIDLE这个消息,看下这个消息的官方解释:

So, how do you handle idle processing in a dialog-based app where the dialog has no parent window? Fortunately, it's trivial. The MFC developers provided a hook: WM_KICKIDLE. RunModalLoop sends this MFC-private message repeatedly when there are no messages in your dialog's queue just the way CWinThread::Run calls OnIdle. RunModalLoop even passes a counter and increments it for you. In effect, WM_KICKIDLE is the dialog equivalent of OnIdle. (Historical note: earlier versions of MFC did the modal/modeless swap and WM_KICKIDLE thing for property sheets. Apparently it worked so well they decided to make all modal dialogs modeless.)

其实就可以简单的看做是窗体的空闲消息,如果我们需要做实时渲染的话,那么这个消息的返回值应该为1,否则返回0就可以了。(我在看了mfc源码后发现,当我们的消息为wm_mousemove或者wm_ncmousemove的时候,会重置idle状态,如果当前没有接受到新的消息时,且idle为true的时候,会去发送 WM_KICKIDLE消息,以上只针对模式对话框)。当然了,实时渲染的代价便是cpu的上升,所以会有一个idlecount来记录WM_KICKIDLE被send 的次数,感兴趣的朋友可以自己去试试,看看其中究竟是怎么一回事。

好了,渲染部分已经差不多了,下面我们来看下怎么去显示地图。我们的地图大小是1600*1200,但是我们设定的bufferSize只有800*600,所以我们需要去移动地图来显示地图的不同位置,怎么移动,按住鼠标左键拖动即可,代码实现如下:

POINT p;GetCursorPos(&p);if(m_bDown){m_iScreenLeft-=(p.x-m_iDownX);m_iScreenTop-=(p.y-m_iDownY);m_iDownX=p.x;m_iDownY=p.y;if(m_iScreenLeft<0){m_iScreenLeft=0;}if(m_iScreenLeft>800){m_iScreenLeft=800;}if(m_iScreenTop<0){m_iScreenTop=0;}if(m_iScreenTop>600){m_iScreenTop=600;}CString strPoint;strPoint.Format(L"Left:%d,Top:%d",m_iScreenLeft,m_iScreenTop);SetWindowText(strPoint);}

是不是很简单啊,那么下面我们该怎么往这个地图场景上添加元素呢,这个也不难,我创建一个imagelist,往里面添加我想要的图片,然后绑定listctrl就可以了啊。

m_TexListControl.MoveWindow(850,350,120,200);m_TexListControl.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);m_TexListControl.SetIconSpacing(CSize(100, 90));   m_ImageList.Create(60,60,ILC_COLORDDB|ILC_COLOR32, 1,1);WCHAR buf[255]={0};GetCurrentDirectory(255,buf);CString strDir=buf;CBitmap bit;bit.Attach(LoadPicture(strDir+L"\\img\\tree.jpg"));m_ImageList.Add(&bit,RGB(0,0,0));bit.Detach();bit.Attach(LoadPicture(strDir+L"\\img\\house.jpg"));m_ImageList.Add(&bit,RGB(0,0,0));bit.Detach();m_TexListControl.SetImageList(&m_ImageList,LVSIL_NORMAL);m_TexListControl.InsertItem(0,L"tree",0);m_TexListControl.InsertItem(1,L"house",1);

是不是也很简单啊,然后我们需要去记录当前地图上每一个位置的状态,比如说在地图的宽300,高200 的位置上有没有物体啊什么的,这里我们需要一个变量来记录这些,于是我定义:

byte m_MapInfo[600*2][800*2];

这里一定要注意,我们需要修改堆栈的大小,默认堆栈的大小为1MB,我们这里设置为4MB,否则会报错,属性->链接器->系统->堆栈保留大小  设置为4096000即可。

if(m_bNewBuildVaild){for(int i=0;i<100;i++){if(m_BuildingInfo[i]==0){m_BuildingInfo[i]=new sBuildingInfo;m_BuildingInfo[i]->type=m_strSelectTex;CRect NewBuildRect(m_iScreenLeft+m_NewBuildRect.left,m_iScreenTop+m_NewBuildRect.top,m_iScreenLeft+m_NewBuildRect.right,m_iScreenTop+m_NewBuildRect.bottom);m_BuildingInfo[i]->rect=NewBuildRect;for(int row=0;row<NewBuildRect.Height();row++){for(int col=0;col<NewBuildRect.Width();col++){m_MapInfo[NewBuildRect.top+row][NewBuildRect.left+col]=i;}}break;}}}m_strSelectTex.Empty();

当我们选中一个物体,放置在地图上的时候,便会调用上面的代码,在这里我说一下,m_MapInfo存储的元素为200的时候,表明该位置为空,否则为新建物体的id。值得注意的是,物体之间是不能重叠的,这个我在onmousemove里面做了相应的判断。

CRect r;m_DrawWnd.GetWindowRect(&r);if(!m_strSelectTex.IsEmpty() && PtInRect(&r,p)){D3DSURFACE_DESC desc;m_TexList[m_strSelectTex]->GetLevelDesc(0,&desc);int width=desc.Width;int height=desc.Height;int left=p.x-r.left-width/2;int top=p.y-r.top-height/2;left=floor(left*1.0/10)*10;top=floor(top*1.0/10)*10;m_NewBuildRect.SetRect(left,top,left+width,top+height);CRect NewBuildRect(m_iScreenLeft+left,m_iScreenTop+top,m_iScreenLeft+left+width,m_iScreenTop+top+height);m_bNewBuildVaild=true;if(NewBuildRect.top<0){m_bNewBuildVaild=false;}else if(NewBuildRect.left<0){m_bNewBuildVaild=false;}else if(NewBuildRect.bottom>1199){m_bNewBuildVaild=false;}else if(NewBuildRect.right>1599){m_bNewBuildVaild=false;}else{for(int row=0;row<NewBuildRect.Height();row++){for(int col=0;col<NewBuildRect.Width();col++){if(m_MapInfo[NewBuildRect.top+row][NewBuildRect.left+col]!=200){m_bNewBuildVaild=false;break;}}}}}

好了,这一部分的主体代码差不多就这些,其他的代码大家可以看下源码,有什么不懂的可以一起交流下,下一节我将给大家带来即时战略游戏中非常重要的一章:寻路。

本文有不足之处,还望大家多多指正。

D3D游戏编程系列(二):自己动手编写即时战略游戏之地图编辑器的制作相关推荐

  1. D3D游戏编程系列(四):自己动手编写即时战略游戏之网络同步

    说到网络同步,这真是一个网络游戏的重中之重,一个好的网络同步机制,可以让玩家的用户体验感飙升,至少,我玩过的魔兽争霸在网络同步方面做得非常好,即便是网络状况很不稳定,依然可以保证用户数据不会出现意想不 ...

  2. D3D游戏编程系列自己动手编写即时战略游戏之网络同步

    说到网络同步,这真是一个网络游戏的重中之重,一个好的网络同步机制,可以让玩家的用户体验感飙升,至少,我玩过的魔兽争霸在网络同步方面做得非常好,即便是网络状况很不稳定,依然可以保证用户数据不会出现意想不 ...

  3. Unity实战 RTS3D即时战略游戏开发(十二) 战斗AI的控制

    大家好,我是Zander,我们接着来开发Rts3D即时战略游戏开发.这节课我们接着来讲AI的相关部分:战斗AI. 游戏中要进行战斗,就需要让AI会攻击,首先在一定范围内如果有足够的单位则进行攻击,在A ...

  4. 【转】网络即时战略游戏软件开发 结构体系分析

    文档下载地址:http://download.csdn.net/detail/wanggan768q/4388056   网络即时战略游戏软件开发 结构体系分析 前言 本人对网络游戏的技术问题一直比较 ...

  5. 网络即时战略游戏结构体系分析

    前言 本人对网络游戏的技术问题一直比较感兴趣,我认为网络游戏的开发在不远的将来是一个非常庞大的产业.这段时间有空,特地玩了几天网络游戏"破碎银行系",并分析了一下其中体系结构,有些 ...

  6. 网络即时战略游戏软件开发 结构体系分析

    文档下载地址:http://download.csdn.net/detail/wanggan768q/4388056   网络即时战略游戏软件开发 结构体系分析 前言 本人对网络游戏的技术问题一直比较 ...

  7. 暴雪经典即时战略游戏十周年回顾

    随便打开一本游戏杂志,被玩家讨论最多的游戏,开发者基本都是他:能做到一个网络游戏还没开始测试,就能占据玩家心目中第一位置的,也是他:一个游戏公司自从确立自己名称之后至今还没有开发过失败游戏的,同样是他 ...

  8. 即时战略游戏中如何协调对象移动

    作者:Dave C. Pottinger 翻译改写:lzc 在图论中人们研究了通过怎样的计算才能找到一条从A点到B点的通路,以图论本身来说这已经解决了从A到B的问题,剩下的只是从A沿着找到的路线移动到 ...

  9. 即时战略游戏中如何协调对象移动(转帖)

    即时战略游戏中如何协调对象移动 http://www.lihuasoft.net/news/list.php?frmid=97 作者:Dave C. Pottinger 在图论中人们研究了通过怎样的计 ...

最新文章

  1. php和python区别-php和python什么区别
  2. 51cto mysql下载_安装MySQL
  3. 集合 setfrozenset
  4. [蓝桥杯][2014年第五届真题]稍大的串(STL)
  5. [TaskList] 省选前板子补完计划
  6. http请求requestUtils
  7. C语言 顺时针打印矩阵(二维数组)
  8. Hive union vs union all
  9. windows7文本文档换成c语言,win10电脑新建文本文档默认编码是UTF-8怎么修改成ANSI编码...
  10. HTTP消息推送原理
  11. mysql 统计每天、每周、每月、每年数据
  12. 如何从Apple电子钱包中删除旧登机牌
  13. 怎么在html模板里加图片,页面中添加图片模块并编辑
  14. item2vec--word2vec在推荐领域的使用
  15. Moviepy模块之视频去除声音、添加音乐
  16. javamail调用阿里企业邮箱实现推送包括多个附件
  17. 51信用卡的日志分析变迁史和技术细节
  18. 互联网电视无处不在的广告惹人烦,消费者转投传统电视怀抱
  19. ERR! code 1; ERR! path \node_modules\node-sass;
  20. 显示器已入手,我快成显示器采购专家了

热门文章

  1. 股票分析及利用tushare查看股票部分信息
  2. 大学小说男主计算机,一部小说,男女主是大学校友,男主比较高冷,一次聚会上认识,男主对女主一见钟情,然后带去出租屋里睡了...
  3. 删除微软office正版验证补丁
  4. python 文件操作大总结 对文件的增删改查,备份, 批量修改,创建和删除 文件的备份
  5. OpenCloner Ripper:集光盘翻录软件+光盘视频转换+光盘解密于一体的全能光盘工具
  6. contenteditable H5聊天室发送表情
  7. yacs、yaml进行实验参数配置详解
  8. 百题突击12:1,SVM算法的优缺点 2,SVM的超参数C如何调节 3,SVM的核函数如何选择 4,简述SVM硬间隔推导过程 5,简述SVM软间隔推导过程
  9. 2020YKB西医综合全程班资料
  10. 【jvm】jvm 参数设置查看与设置