D3D游戏编程系列(三):自己动手编写即时战略游戏之寻路
说起即时战略游戏,不得不提的一个问题是如何把一个物体从一个位置移动到另一个位置,当然,我说的不是瞬移,而是一个移动的过程,那么在这个移动的过程中我们如何来规划路线呢,这就不得不提到寻路了。
我所了解到的寻路算法有很多,当然我还是向大家推荐A*算法,这个应该是目前在八个方向上效率最高的寻路算法了吧,在这里,我不准备详细的去介绍这个算法的原理,给大家一个链接,http://www.cnblogs.com/technology/archive/2011/05/26/2058842.html,这是我在网上看到的我个人认为有关A*算法最好的讲解了。
好了,废话不多说,我给出在4个方向上的算法代码(上下左右,因为我的游戏里就是在四个方向上移动,八个方向类似)。
void MyWin::FindPath(sEleObj* Ele)
{
static sPathNode EleMem[1024*1024];
CPoint desPoint=Ele->DesPos;
if((m_MapInfo[desPoint.y][desPoint.x]<200 && m_MapInfo[desPoint.y][desPoint.x]!=Ele->ID) || (m_MapInfo[desPoint.y+Ele->CurPos.Height()-10][desPoint.x]<200 && m_MapInfo[desPoint.y+Ele->CurPos.Height()-10][desPoint.x]!=Ele->ID) || (m_MapInfo[desPoint.y][desPoint.x+Ele->CurPos.Width()-10]<200 && m_MapInfo[desPoint.y][desPoint.x+Ele->CurPos.Width()-10]!=Ele->ID) || (m_MapInfo[desPoint.y+Ele->CurPos.Height()-10][desPoint.x+Ele->CurPos.Width()-10]<200 && m_MapInfo[desPoint.y+Ele->CurPos.Height()-10][desPoint.x+Ele->CurPos.Width()-10]!=Ele->ID))
{
Ele->VecPath.clear();
m_FindPathEleList.remove(Ele);
m_DynamicFindPathEleList.remove(Ele);
m_DynamicFindPathEleList.push_back(Ele);
return;
}
sPathNode *node=new sPathNode;
node->Cur=Ele->CurPos.TopLeft();
node->F=abs(desPoint.x-node->Cur.x)+abs(desPoint.y-node->Cur.y);
node->Par=0;
m_OpenListSet.insert(node);
int count=0;
int pos=0;
set<DWORD> PointMap;
#define MAKEDWORD( wLow, wHigh ) ((LONG)(((WORD)(wLow)) | ((DWORD)((WORD)(wHigh))) << 16))
while(1)
{
if(++count==1000)
{
break;
}
if(m_OpenListSet.empty())
{
break;
}
sPathNode *MinNode=*m_OpenListSet.begin();
m_OpenListSet.erase(m_OpenListSet.begin());
m_CloseListSet.insert(MinNode);
//cout<<MinNode->Cur.x<<" "<<MinNode->Cur.y<<endl;
node=MinNode;
CPoint TopPoint;
TopPoint.x=node->Cur.x;
TopPoint.y=node->Cur.y-10;
if(TopPoint.y>=0)
{
CPoint TempPoint;
TempPoint.x=TopPoint.x+Ele->CurPos.Width()-10;
TempPoint.y=TopPoint.y;
if(m_MapInfo[TopPoint.y][TopPoint.x]==200 && m_MapInfo[TempPoint.y][TempPoint.x]==200)
{
sPathNode *NewNode=&EleMem[pos++];
NewNode->Cur=TopPoint;
NewNode->Par=node;
NewNode->F=abs(desPoint.x-NewNode->Cur.x)+abs(desPoint.y-NewNode->Cur.y);
if(PointMap.find(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y))==PointMap.end())
{
m_OpenListSet.insert(NewNode);
PointMap.insert(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y));
if(NewNode->F==0)
{
break;
}
}
}else if(TopPoint==desPoint)
{
break;
}
}
CPoint RightPoint;
RightPoint.x=node->Cur.x+10;
RightPoint.y=node->Cur.y;
if(RightPoint.x+Ele->CurPos.Width()<=1600)
{
CPoint TempPoint;
TempPoint.x=RightPoint.x+Ele->CurPos.Width()-10;
TempPoint.y=RightPoint.y+Ele->CurPos.Height()-10;
if(m_MapInfo[RightPoint.y][RightPoint.x+Ele->CurPos.Width()-10]==200 && m_MapInfo[TempPoint.y][TempPoint.x]==200)
{
sPathNode *NewNode=&EleMem[pos++];
NewNode->Cur=RightPoint;
NewNode->Par=node;
NewNode->F=abs(desPoint.x-NewNode->Cur.x)+abs(desPoint.y-NewNode->Cur.y);
if(PointMap.find(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y))==PointMap.end())
{
m_OpenListSet.insert(NewNode);
PointMap.insert(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y));
if(NewNode->F==0)
{
break;
}
}
}else if(RightPoint==desPoint)
{
break;
}
}
CPoint BottomPoint;
BottomPoint.x=node->Cur.x;
BottomPoint.y=node->Cur.y+10;
if(BottomPoint.y+Ele->CurPos.Height()<=1200)
{
CPoint TempPoint;
TempPoint.x=BottomPoint.x+Ele->CurPos.Width()-10;
TempPoint.y=BottomPoint.y+Ele->CurPos.Height()-10;
if(m_MapInfo[BottomPoint.y+Ele->CurPos.Height()-10][BottomPoint.x]==200 && m_MapInfo[TempPoint.y][TempPoint.x]==200)
{
sPathNode *NewNode=&EleMem[pos++];
NewNode->Cur=BottomPoint;
NewNode->Par=node;
NewNode->F=abs(desPoint.x-NewNode->Cur.x)+abs(desPoint.y-NewNode->Cur.y);
if(PointMap.find(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y))==PointMap.end())
{
m_OpenListSet.insert(NewNode);
PointMap.insert(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y));
if(NewNode->F==0)
{
break;
}
}
}else if(BottomPoint==desPoint)
{
break;
}
}
CPoint LeftPoint;
LeftPoint.x=node->Cur.x-10;
LeftPoint.y=node->Cur.y;
if(LeftPoint.x>=0)
{
CPoint TempPoint;
TempPoint.x=LeftPoint.x;
TempPoint.y=LeftPoint.y+Ele->CurPos.Height()-10;
if(m_MapInfo[LeftPoint.y][LeftPoint.x]==200 && m_MapInfo[TempPoint.y][TempPoint.x]==200)
{
sPathNode *NewNode=&EleMem[pos++];
NewNode->Cur=LeftPoint;
NewNode->Par=node;
NewNode->F=abs(desPoint.x-NewNode->Cur.x)+abs(desPoint.y-NewNode->Cur.y);
if(PointMap.find(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y))==PointMap.end())
{
m_OpenListSet.insert(NewNode);
PointMap.insert(MAKEDWORD(NewNode->Cur.x,NewNode->Cur.y));
if(NewNode->F==0)
{
break;
}
}
}else if(LeftPoint==desPoint)
{
break;
}
} }
if(count<1000)
{
if(!m_OpenListSet.empty())
{
node=*m_OpenListSet.begin();
while(node->Par!=0)
{
Ele->VecPath.push_front(node->Cur);
node=node->Par;
}
}else
{
if(m_VecSelectTank.size()>0)
{
Ele->VecPath.clear();
m_FindPathEleList.remove(Ele);
m_DynamicFindPathEleList.remove(Ele);
m_DynamicFindPathEleList.push_back(Ele);
} }
}
m_OpenListSet.clear();
m_CloseListSet.clear();
}
这里有用到二叉堆的数据结构,这样可以极大的提高效率,下面我给出相关的结构定义:
struct sPathNode;
class sort
{
public:
bool operator () (const sPathNode* b1,const sPathNode* b2) const{
return b1->F<b2->F;
}
};
struct sPathNode
{
CPoint Cur;
sPathNode *Par;
int F;
sPathNode(){}
sPathNode(int x,int y)
{
Cur.SetPoint(x,y);
}
};
struct sEleObj
{
IDirect3DTexture9* Sur;
CPoint DesPos;
CRect CurPos;
list<CPoint> VecPath;
bool bMove;
byte ID;
};
multiset<sPathNode*,sort> m_OpenListSet;
multiset<sPathNode*,sort> m_CloseListSet;
这样我们每次添加一个节点时,F值最小的节点便会在最开头的位置。在此我说下关于该算法的一些优化,我在函数的开头便定义了static sPathNode EleMem[1024*1024];这样的话我们每次就可以直接从这个数组里分配节点,而不用去new和delete一个新的节点,节约了操作内存的时间,并且,我在算法里用的容器都是Set,因为set内部是红黑二叉树实现的,所以在查找上速度十分快。还有一点是,千万不要同时为多个物体寻路,你应该把需要寻路的物体放在一个list里面,每一帧或者每过几帧(这根据你的需求)依次取出来做寻路,这样在速度上会大大提高,就算有几十到几百ms的延迟,用户也很难察觉。
还有我不得不提的是,动态碰撞怎么办,对,就是在你规划路径好了以后,在你的路径上出现了其他物体该怎么解决呢,那么我们就需要去做动态寻路,在我们遇到障碍物后,会根据当前物体的id,决定是否等待,如果等待,则障碍物如果是移动状态,则重新去寻路,规划一条新的路径,如果障碍物已经是停止状态,则当前物体重新寻路,规划出一条新的路径。
最后一点是,当我们操作多个物体移动到同一地点时,因为不可能重叠,所以这些物体不可能到达同一位置,这就是说,当物体的目的地不可到达时,我们该怎么处理呢?其实很简单,就是以目的地为中心向外搜索,找到一个物体可以到达的位置作为新的目的地,然后去寻路这个目的地就可以了。
说起来简单,但是这其中的代码量和繁琐程度还是很大的,大家可以参考一下我上传的代码,有什么问题一起交流下。
本文有不足之处,还望大家多多指正。
D3D游戏编程系列(三):自己动手编写即时战略游戏之寻路的更多相关文章
- D3D游戏编程系列(四):自己动手编写即时战略游戏之网络同步
说到网络同步,这真是一个网络游戏的重中之重,一个好的网络同步机制,可以让玩家的用户体验感飙升,至少,我玩过的魔兽争霸在网络同步方面做得非常好,即便是网络状况很不稳定,依然可以保证用户数据不会出现意想不 ...
- WCF编程系列(三)地址与绑定
WCF编程系列(三)地址与绑定 地址 地址指定了接收消息的位置,WCF中地址以统一资源标识符(URI)的形式指定.URI由通讯协议和位置路径两部分组成,如示例一中的: http://loc ...
- 022年9月12日 学习ASP.NET Core Blazor编程系列三——实体
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...
- OWIN系列之自己动手编写中间件
一.前言 1.基于OWIN的项目摆脱System.Web束缚脱颖而出,轻量级+跨平台,使得ASP.NET应用程序只需依赖这个抽象接口,不用关心所运行的Web服务器. 2.OWIN.dll介绍 使用反编 ...
- 异步编程系列第04章 编写Async方法
p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...
- [Python] 文科生零基础学编程系列三——数据运算符的基本类别
上一篇:[Python] 文科生零基础学编程系列二--数据类型.变量.常量的基础概念 下一篇: ※ 程序的执行过程,就是对数据进行运算的过程. 不同的数据类型,可以进行不同的运算, 按照数据运算类型的 ...
- 【Visual C++】游戏编程学习笔记之九:回合制游戏demo(剑侠客VS巡游天神)
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...
- D3D游戏编程系列(六):自己动手编写第一人称射击游戏之第一人称视角的构建
说起第一人称射击游戏,不得不提第一人称视角啊,没有这个,那么这个第一就无从谈起啊,我作为一个观察者究竟如何在这个地图上顺利的移动和观察呢,那么,我们一起来研究下. 我们首先来看下CDXCamera类: ...
- D3D游戏编程系列(一):DXLib的介绍
这篇文章里我准备向大家介绍下我封装的一个基础D3D库:DXLib.有了这样一个类库,可以减少很多无用功以及繁琐的工作,使我们的效率大大提高. DXLib.h #define DIRECTINPUT_V ...
随机推荐
- CSS 实现背景半透明
IE过渡滤镜 + CSS3 rgba 即可完美实现. 具体实现代码如下: .transparent { background:rgba(0, 0, 0, 0.3); filter:pr ...
- [XJOI NOI2015模拟题13] A 神奇的矩阵 【分块】
题目链接:XJOI NOI2015-13 A 题目分析 首先,题目定义的这种矩阵有一个神奇的性质,第 4 行与第 2 行相同,于是第 5 行也就与第 3 行相同,后面的也是一样. 因此矩阵可以看做只有 ...
- responsive web design
http://d.alistapart.com/responsive-web-design/ex/ex-site-flexible.html http://alistapart.com/article ...
- no identities are available for signing
原地址:http://www.cnblogs.com/imzzk/p/3501868.html 今天将做好的app提交到app store,结果就出现标题上的错误.“No identities are ...
- UVA 10801 Lift Hopping
算是一道需要动脑筋的最短路问题了,关键在于建图部分,对于n个电梯中每一个都要经过cnt个楼层,a[0],a[1],a[2],a[3],a[4],......a[cnt-1],那么对于任意两个楼层a[j ...
- [jobdu]二进制中1的个数
做法是n&(n-1).据说还有变态的查表法:http://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html.最后,居然必须用sc ...
- 模拟+二分 poj-1019-Number Sequence
题目链接: http://poj.org/problem?id=1019 题目大意: Sk表示123...k 把S1S2S3...Sk排成一行 比如:112123123412345123456.... ...
- JAVA bean与XML互转的利器---XStream
最近在项目中遇到了JAVA bean 和XML互转的需求, 本来准备循规蹈矩使用dom4j忽然想起来之前曾接触过的XStream, 一番研究豁然开朗,利器啊利器, 下来就XStream的一些用法与大家 ...
- 常见 jar包详解
常见 jar包详解 jar包 用途 axis.jar SOAP引擎包 commons-discovery-0.2.jar 用来发现.查找和实现可插入式接口,提供一些一般类实例化.单件的生命周期 ...
- Hadoop2.2编程:新旧API的区别
Hadoop最新版本的MapReduce Release 0.20.0的API包括了一个全新的Mapreduce JAVA API,有时候也称为上下文对象. 新的API类型上不兼容以前的API,所以, ...