说起即时战略游戏,不得不提的一个问题是如何把一个物体从一个位置移动到另一个位置,当然,我说的不是瞬移,而是一个移动的过程,那么在这个移动的过程中我们如何来规划路线呢,这就不得不提到寻路了。

我所了解到的寻路算法有很多,当然我还是向大家推荐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游戏编程系列(三):自己动手编写即时战略游戏之寻路的更多相关文章

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

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

  2. WCF编程系列(三)地址与绑定

    WCF编程系列(三)地址与绑定   地址     地址指定了接收消息的位置,WCF中地址以统一资源标识符(URI)的形式指定.URI由通讯协议和位置路径两部分组成,如示例一中的: http://loc ...

  3. 022年9月12日 学习ASP.NET Core Blazor编程系列三——实体

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  4. OWIN系列之自己动手编写中间件

    一.前言 1.基于OWIN的项目摆脱System.Web束缚脱颖而出,轻量级+跨平台,使得ASP.NET应用程序只需依赖这个抽象接口,不用关心所运行的Web服务器. 2.OWIN.dll介绍 使用反编 ...

  5. 异步编程系列第04章 编写Async方法

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  6. [Python] 文科生零基础学编程系列三——数据运算符的基本类别

    上一篇:[Python] 文科生零基础学编程系列二--数据类型.变量.常量的基础概念 下一篇: ※ 程序的执行过程,就是对数据进行运算的过程. 不同的数据类型,可以进行不同的运算, 按照数据运算类型的 ...

  7. 【Visual C++】游戏编程学习笔记之九:回合制游戏demo(剑侠客VS巡游天神)

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...

  8. D3D游戏编程系列(六):自己动手编写第一人称射击游戏之第一人称视角的构建

    说起第一人称射击游戏,不得不提第一人称视角啊,没有这个,那么这个第一就无从谈起啊,我作为一个观察者究竟如何在这个地图上顺利的移动和观察呢,那么,我们一起来研究下. 我们首先来看下CDXCamera类: ...

  9. D3D游戏编程系列(一):DXLib的介绍

    这篇文章里我准备向大家介绍下我封装的一个基础D3D库:DXLib.有了这样一个类库,可以减少很多无用功以及繁琐的工作,使我们的效率大大提高. DXLib.h #define DIRECTINPUT_V ...

随机推荐

  1. 解决websphere在aix linux下日志乱码

    管理控制台--->服务器--->应用程序服务器--->server1--->java和进程管理--->进程定义--->java虚拟机--->将通用jvm参数设 ...

  2. WINDOWS HYPER-V加新网卡,设置网络出错

    新网卡加入,设置好IP之后,HYPER-V需要更改相应外部网络连接,然后重新生成新的虚拟连接网卡. 不然,虚拟机无法正常使用网络. 但我昨天在绑定新的网站时,出现如下错误: Adding a new ...

  3. asp.net 中 .ASPX 与.CS文件的关系

    .aspx文件继承自.cs文件 虽然一个 Web 窗体页由两个单独的文件组成,但这两个文件在应用程序运行时形成了一个整体.项目中所有 Web 窗体的代码隐藏类文件都被编译成由项目生成的动态链接库 (. ...

  4. C调用OPENSSL做REST服务客户端的例子

    //SSL-Client.c #include <stdio.h> #include <errno.h> #include <unistd.h> #include ...

  5. 二维图形的矩阵变换(二)——WPF中的矩阵变换基础

    原文:二维图形的矩阵变换(二)--WPF中的矩阵变换基础 在前文二维图形的矩阵变换(一)——基本概念中已经介绍过二维图像矩阵变换的一些基础知识,本文中主要介绍一下如何在WPF中进行矩阵变换. Matr ...

  6. Dojo Widget系统(转)

    Dojo 里所有的小部件(Widget)都会直接或间接的继承 dijit._Widget / dijit._WidgetBase dijit._Widget 是 dojo 1.6 和 1.6之前的版本 ...

  7. 【HDOJ】4162 Shape Number

    循环串的最小表示法. /* */ #include <iostream> #include <string> #include <map> #include < ...

  8. 开发完整的Web项目必备

    开发工具 数据库系统 DB2数据库 Oracle数据库 SQL Server数据库 MySQL数据库 Access数据库 Web服务器 IIS BEA WebLogic Server Apache T ...

  9. mapreduce: InputFormat详解 -- RecordReader篇

    InputFormat是MapReduce中一个很常用的概念,它在程序的运行中到底起到了什么作用呢? InputFormat其实是一个接口,包含了两个方法: public interface Inpu ...

  10. Objective-C 记录

    NSdata 与 NSString,Byte数组,UIImage 的相互转换 原文网址:http://www.cnblogs.com/jacktu/archive/2011/11/08/2241528 ...