标题: 用SDL打造我们自己的战棋游戏!, 11月30日更新第3个版本:可以绕过障碍移动的赵辉
性别:男-离线 岱瀛
(deving)

长平侯
川峡东路经略使
监管使

Rank: 19Rank: 19Rank: 19Rank: 19
组别 经略使
级别 左将军
好贴 1
功绩 2293
帖子 1370
编号 55810
注册 2005-12-22
来自 人间
家族 慕容世家


发表于 2009-12-1 21:42 资料 个人空间 短消息 看全部作者


QUOTE:
原帖由 赵辉 于 2009-12-1 13:02 发表
我广搜算法的优先顺序是上、下、左、右,所以应该是上1右1.

原版的我不知道,这就等岱儿他们来回答吧

我简单的说一下曹操传的AI算法吧。

我们知道,曹操传的AI是分了很多种的,比如坚守原地啊,主动出击啊,被动初级啊,跟随某人啊,攻击某人啊,而这里面的一个重要核心思想是 寻路算法。

我认为光荣的寻路算法应该是从 long long ago就沿用的,因为当初我看了那长长的汇编代码,读懂那个寻路算法的时候,忽然就想到了三国志第二代的一个现象。 我们操作某个人的时候,一点,周围可移动的格子里有一些奇怪的数字,7,6,5,4.有时候又7,4,1这样的。后来看曹操传代码,觉得就是一脉相承的东西了。

简单在这里说一下,印象中的曹操传寻路算法。

他是一个递归算法。

首先,我们简单看一下相关的数据结构,既然会C++,我就用C++来解释,这样都方便一些。
#define MAX_LANDFORM 16
#define MAX_METIER 23  //假设有23种职业

//假设一个30*30的地图
int nMapWidth  =  30;
int nMapHeight =   30;

struct Map
{
     int nLandformID;  //地形
     int nState;         //状态,0表示没有人占,-1表示敌人占,1表示我方或者友方占
};

struct Metier  //职业
{
     int nLandformConsume[MAX_LANDFORM];    //某职业的地形消耗数组
     int nAction;                                              //移动力
     //....   职业里还有很多层数据结构
};

Metier g_metier[MAX_METIER];  

Class CBattelPerson : public CPerson  //战场上的人,继承于人
{
private:
   int nID;            //人物的ID;
    int nMetierID;  //人对应的职业
    int nXposition;  //当前地图所在的X坐标位置
    int nYposition;  //当前地图所在的Y坐标位置
    int nStatus;     //当前状态  0表示未行动,1表示行动
    int nActionState;      //当前移动力状态  (可能强行,可能下降,可能是正常)
    int nRole;   //人物角色,1表示我军或者友军, -1表示敌军
   //....   战场上的人其实还有很多种属性,从略
private:
   void mfGetActionPath(int nActionValue, int nXpos,int nYpos) ;
public:
   void GetAcitonPath();
};

//会计算出一个所谓的移动队列,我简单的用vector来表示,相信引用下stl库也不是太麻烦的事情。

struct Position
{
     int nXpos;
     int nYpos;
     int nAIValue;  //这个位置的AI参考值
};
vector<Position> actionQueue;

全局的地图
Map map[nWidth][nHeight];
int nFlag[nWidth][nHeight];  //用来标记地图的AI计算状态

void  //求路径的递归函数,参数一,行动力,参数二,x坐标,参数三,y坐标
CBattelPerson::mfGetActionPath(int nActionValue, int nXpos,int nYpos)
{
    if (nFlag[nXpos][nYpos] != 0)  //说明这个位置遍历过,不遍历
        return;
    else
       nFlag[nXpos][nYpos] = nID;
     if (nActionValue <=0) return;  //没有行动力了,退出
     if (nRole *  map[x][y]. nState < 0)
          return;    //这个位置上有敌人,不能通过,退出  ,这里面的设计没有考虑突击移动的特性,如果有突击移动的特性,这个判断就可以跳过
     
     //为了解释清楚容易看点,下面代码拆细一些

    //取出当前职业
    Metier   metier = g_metier[nMetierID];
   
    //取出地形
    if (nXpos - 1 >= 0 )
   {
        int nCurLandFormIDLeft  = map[x-1][y]. nLandformID;
         mfGetActionPath(nActionValue - metier . nLandformConsume[nCurLandFormIDLeft],x-1,y);
   }

  if (nXpos  + 1 < nWidth )
   {
        int nCurLandFormIDRight  = map[x+1][y]. nLandformID;
        mfGetActionPath(nActionValue - metier . nLandformConsume[nCurLandFormIDRight],x+1,y);
   }

   if (nYpos - 1 >= 0)
   {
          int nCurLandFormIDTop  = map[x][y-1]. nLandformID;
          mfGetActionPath(nActionValue - metier . nLandformConsume[nCurLandFormIDTop],x,y-1);
   }
   
   if (nYpos + 1 < nHeight)
   {
         iint nCurLandFormIDBottom  = map[x][y+1]. nLandformID;  
         mfGetActionPath(nActionValue - metier . nLandformConsume[nCurLandFormIDBottom],x,y+1);
   }

   Position position;
   position.x = nXpos;
   position.y = nYpos;

   actionQueue.push_back(position);  //把坐标压入向量   
   
}

void
CBattelPerson ::GetAcitonPath()
{
     int nActionValue = 0;
     //当前移动力
     nActionValue  = g_metier[nMetierID]. nAction * nActionState; //假设移动力状态是个百分数,其实曹操转里面不是,不过这个无所谓了。
  
     memset(nFlag,0,nWidht*nHeight);  //把整个数组清零
     mfGetActionPath(nActionValue ,nXpos,nYpos);
}

这样,外部函数只要调用到GetAcitonPath(),就能得到当前这个人物的可移动向量。

如果是给用户交互操作,直接就是给界面作下渲染。

如果是给AI,那么就根据这个向量,再根据当前选择的AI类别,遍历这个vector,计算出每个位置的nAIValue;

最后,对这个vector作一下排序,那么AI的结果就出来了。

[ 本帖最后由 岱瀛 于 2009-12-2 01:03 编辑 ]


顶部
性别:男-离线 岱瀛
(deving)

长平侯
川峡东路经略使
监管使

Rank: 19Rank: 19Rank: 19Rank: 19
组别 经略使
级别 左将军
好贴 1
功绩 2293
帖子 1370
编号 55810
注册 2005-12-22
来自 人间
家族 慕容世家


发表于 2009-12-1 21:54 资料 个人空间 短消息 看全部作者


QUOTE:
原帖由 JFLS28mj 于 2009-12-1 13:02 发表




这个只要耗得移动力是一样的就无所谓,你可以写一个算法规定上移优先级高,也同样可以规定右移优先级高

还有引导攻击其实也是,不过要是让我写代码的话,我会优先攻击能一击毙命的(如果无限引导的话 ...

其实按照曹操传的AI算法,伤害计算是不考虑爆击和格挡,双击的。 我们自己写的话,当然可以考虑,不过我倒也是建议照猫画虎,甚至早期第一个版本,士气,敏捷这种东西都不要也行。

增加无谓的算法复杂性,对于早期不好,先把基本模型做出来,思路理清了,再慢慢添加,先易后难比较科学。

伤害计算,印象我在汇编解析里曾经解析过。反正所谓的AI,最重要的一个设计就是那个权值来影响到伤害值大小,最终成为行动的指标。


顶部
性别:男-离线 岱瀛
(deving)

长平侯
川峡东路经略使
监管使

Rank: 19Rank: 19Rank: 19Rank: 19
组别 经略使
级别 左将军
好贴 1
功绩 2293
帖子 1370
编号 55810
注册 2005-12-22
来自 人间
家族 慕容世家


发表于 2009-12-2 00:31 资料 个人空间 短消息 看全部作者


QUOTE:
原帖由 赵辉 于 2009-12-1 23:14 发表
多谢指点。
你对CCZ的研究真是太透彻了,若说重写exe,你才是不二人选。小弟目前的工作尚属自娱自乐型。
不过我感觉CCZ原版这个递归算法可能重复遍历一些点,ms效率不是最好的。

曹操传的大致算法思路是那个,我刚才写的确实有重复的点,曹操传有没有我也忘记了,修改了下,你重新看下代码,引入一个nFlag的二维数组,标记下每个点的状态,就可以规避掉这种重复计算点的问题了。

另外,自娱自乐挺好的,先练手嘛。 整个走一遍之后,再从设计的角度去看,去设计好,也许就可以写出来一个不错的引擎了,关键是要持之以恒。

另外,我上面的代码只是解释算法用的,设计角度来说不一定最优,一些struct,class的设计,不一定完美。还有存在大量的全局变量,其实是不对的。

[ 本帖最后由 岱瀛 于 2009-12-2 01:08 编辑 ]
顶部
性别:男-离线 岱瀛
(deving)

长平侯
川峡东路经略使
监管使

Rank: 19Rank: 19Rank: 19Rank: 19
组别 经略使
级别 左将军
好贴 1
功绩 2293
帖子 1370
编号 55810
注册 2005-12-22
来自 人间
家族 慕容世家


发表于 2009-12-4 19:08 资料 个人空间 短消息 看全部作者
A*算法个人觉得更适合于即时战略游戏。  他解决的是行走路线问题,并不能解决战棋的AI问题。

A*有一个最大的问题,就是有一个目标点,他求出来的是一个起始点到目标点的路径。但是站棋AI根本不是要找一条行动路径,本质上是有所有可行路径推出一个最佳目标点。

等到目标点确认出来了,怎么从起始点移动到目标点,只是一种简单绘图的问题。上下左右的走,反正是肯定可达的。

比如被动出击这种AI,就是把可移动的vector作一次点的遍历,每个可达点得到的伤害计算权值,最终对vector排序,就可以得到应该移动到哪个点了,至于怎么移动,其实只是上下左右移动靠近而已。
顶部
性别:男-离线 岱瀛
(deving)

长平侯
川峡东路经略使
监管使

Rank: 19Rank: 19Rank: 19Rank: 19
组别 经略使
级别 左将军
好贴 1
功绩 2293
帖子 1370
编号 55810
注册 2005-12-22
来自 人间
家族 慕容世家


发表于 2009-12-4 19:28 资料 个人空间 短消息 看全部作者


QUOTE:
原帖由 赵辉 于 2009-12-4 19:23 发表
这几天研究遇到瓶颈了,就是用SDL做窗体控件(对话框、按钮什么的),自己重头写很麻烦,已有的一些库配置、使用起来都不是很顺手。
努力求解中……

呵呵,用VC就得遇见这种问题。

不总是遇见问题,怎么可以进步,呵呵。
顶部

正在浏览此帖的会员 - 共 3 人在线




当前时区 GMT+8, 现在时间是 2024-11-18 06:19
京ICP备2023018092号 轩辕春秋 2003-2023 www.xycq.org.cn

Powered by Discuz! 5.0.0 2001-2006 Comsenz Inc.
Processed in 0.014749 second(s), 9 queries , Gzip enabled

清除 Cookies - 联系我们 - 轩辕春秋 - Archiver - WAP