游戏开发论坛

 找回密码
 立即注册
搜索
查看: 1612|回复: 3

最近有点闲,有点闲,灌水一篇

[复制链接]

89

主题

822

帖子

847

积分

高级会员

Rank: 4

积分
847
发表于 2005-12-19 13:26:00 | 显示全部楼层 |阅读模式
   前年开发一个RPG的游戏的时候,我放弃了A Star的算法,本来写好了,后来没有用到,对于3D游戏发现有点画蛇添足的味道(寻路是2D的),今年项目中有一个四国,工兵寻路觉得有点意思,但代码不好意思放上来,现在有个连连看的东西,跟上面的几种有区别但也有点相似。跟大家探讨。

  常见的路径搜索算法,其实是一种穷举法,我们通常用的地图是连续存储的,比如用一个2维的数组来表示。
{ 1, 1, 1, 1,  
  1, 1, 0, 1,  
  1, 1, 1, 1,  
  1, 0, 1, 1 }
  但也有非单一数组表示的,比如像四国军旗,每一个节点的下一步走法都不尽相同,有的可以8个方向走,有的只能2个方向,有的可以4个方向走,有的可以5个方向走。这个都没关系,只要把下一步能走的节点都存储起来即可。
struct NodeDir
{
    int NextPos[MAX_DIR];
}

  通常我们会有一个数组来存放每个节点的权重,初始化时将能走动的节点权重设置为一个大数,而将不能走动的节点设置为0。

  同时我们定义一个双向链表树来存储已经走过路径

typedef struct tagCheckNode
{
    int                           pos;

    tagCheckNode *        child[4];
    tagCheckNode *        father;

    tagCheckNode( tagCheckNode * _father, int _pos );
    ~tagCheckNode( );

    tagCheckNode * add_child( int _pos );
    void del_child( tagCheckNode * _child );
}
CHK_NODE, *LP_CHK_NODE;

  有了节点方向,权重数组,和路径树就可以开始了,常见的2D Tile地图不需要这个,默认就是4向或者8向。

  所谓穷举法就是一个节点(格子)一个节点的试,找到一条适合我们需要的路径,那么连连看的连线路径比较特殊,最多只能拐2个弯,这是一个绝对Judge,当然我们常见的绝对Judge还有曼哈顿距离等等,当然我们会有其他的Judge,最常见的就是每次走过的路径不能比上一次的长,这就是权重的意义,由于初始化的时候,能走的地方权重都是大数,所以都可以走,不能走的地方都是0,那么都不能走,没有什么路径比0长。

现在开始从起点开始走(以四向连连看为例)

有4个方向(但不包括上次来的节点),如果在地图边上就只有3个方向走,地图角上是2个方向,每走一步把走过的节点存放到双向链表树中,同时该步路经的权重+1,并修改走过的节点的权重。

如果发现下面情况,则从双向链表树中删除当前节点,并退回到上一步
1、当前节点的权重比当前小,证明有一条更好的路已经来过了
2、当前节点不是目标点并四个方向都是死路
3、当前节点的路径比已经走过的一条正确的路径的长
4、当前节点拐了3个弯
5、其他觉得是死路的理由
如果当前节点是目标点,则 [ 不 ] 从双向链表树中删除当前节点,并退回到上一步

直到退回到双向链表树根节点,证明所有的节点都已经试过了,这时候我们可以认为最后一条路径就是最适合我们的路径,其实有可能最后2条,3条都符合,但我们只需取最后一条就OK了。

class GGamePath
{
private:

    int *  m_PathWeight; // 权重

    int    m_nCountX;
    int    m_nCountY;
    int    m_nCount;

    CHK_NODE *    m_pHeadPath;
    CHK_NODE *    m_pTailPath;

private:

    // nFatherDir 上一次方向, 1右, 2左, 3上, 4下
    // nChangeCount 拐弯次数
    bool FindPath( CHK_NODE * pFarherNode, int nCheckPos, int nObjPos, int nFatherWeight, int nFatherDir, int nChangeCount )
    {
        char sDInfo[DEBUG_LEN];

        if ( nFatherWeight != 0 )
        {
            if ( pFarherNode == null )
            {
                return false; // 错误
            }

            if ( nFatherWeight + 1 > m_PathWeight[nCheckPos] )  
            {
                return false; // 不是最佳路径
            }

            if ( nCheckPos == nObjPos )  
            {
                m_PathWeight[nCheckPos] = nFatherWeight + 1; // 标记路径权重

                m_pTailPath = pFarherNode->add_child( nCheckPos );
                return true; // 到达目标
            }

            if ( m_PathWeight[nCheckPos] == 0 )  
            {
                return false; // 不通
            }
        }

        m_PathWeight[nCheckPos] = nFatherWeight + 1; // 标记路径权重

        bool bRet = CheckNextPath( pFarherNode, nCheckPos, nObjPos, nFatherWeight, nFatherDir, nChangeCount );

        // bRet == false 死路
        return bRet;
    }

// 检查下一个方向
    inline bool CheckNextPath( CHK_NODE * pFarherNode, int nCheckPos, int nObjPos, int nFatherWeight, int nFatherDir, int nChangeCount )
    {
        bool bRet = false;
        int nNewPos;
        int nNewChangeCount;

        int x = nCheckPos % m_nCountX;
        int y = nCheckPos / m_nCountX;

        CHK_NODE * pNewNode = pFarherNode->add_child( nCheckPos );

        nNewChangeCount = nChangeCount;
        if ( x > 0 && nFatherDir != 1 )
        {
            if ( nFatherDir != 2 && nFatherDir != 0 ) nNewChangeCount = nChangeCount + 1;
            if ( nNewChangeCount < 3 )
            {
                nNewPos = x - 1 + y * m_nCountX;
                bRet |= FindPath( pNewNode, nNewPos, nObjPos, m_PathWeight[nCheckPos], 2, nNewChangeCount ); // 左
            }
        }

        nNewChangeCount = nChangeCount;
        if ( x < m_nCountX-1 && nFatherDir != 2 )  
        {
            if ( nFatherDir != 1 && nFatherDir != 0 ) nNewChangeCount = nChangeCount + 1;
            if ( nNewChangeCount < 3 )
            {
                nNewPos = x + 1 + y * m_nCountX;
                bRet |= FindPath( pNewNode, nNewPos, nObjPos, m_PathWeight[nCheckPos], 1, nNewChangeCount ); // 右
            }
        }

        nNewChangeCount = nChangeCount;
        if ( y > 0 && nFatherDir != 4 )
        {
            if ( nFatherDir != 3 && nFatherDir != 0 ) nNewChangeCount = nChangeCount + 1;
            if ( nNewChangeCount < 3 )
            {
                nNewPos = x + (y-1) * m_nCountX;
                bRet |= FindPath( pNewNode, nNewPos, nObjPos, m_PathWeight[nCheckPos], 3, nNewChangeCount ); // 上
            }
        }

        nNewChangeCount = nChangeCount;
        if ( y < m_nCountY-1 && nFatherDir != 3 )
        {
            if ( nFatherDir != 4 && nFatherDir != 0 ) nNewChangeCount = nChangeCount + 1;
            if ( nNewChangeCount < 3 )
            {
                nNewPos = x + (y+1) * m_nCountX;
                bRet |= FindPath( pNewNode, nNewPos, nObjPos, m_PathWeight[nCheckPos], 4, nNewChangeCount ); // 下
            }
        }

        if ( bRet == false ) // 死路
        {
            //m_PathWeight[nCheckPos] = 0;
            if ( pNewNode )  
            {
                delete pNewNode;
                pNewNode = null;
            }
        }

        return bRet;
    }

    void Destory( )
    {
        if ( m_PathWeight )  
        {
            delete m_PathWeight;
            m_PathWeight = null;
        }

        if ( m_pHeadPath )  
        {
            delete m_pHeadPath;
            m_pHeadPath = null;

            m_pTailPath = null;
        }
    }

public:

    GGamePath( )  
    {
        m_PathWeight = null;

        m_pHeadPath = null;
        m_pTailPath = null;
    }

    ~GGamePath( )
    {
        Destory( );
    }

    // nCountX和nCountY包括最外一层没有棋子的区域
    bool SetCheckPath( int pArryPath[], int nCountX, int nCountY, int nStartPos, int nObjPos )
    {
        int i;

        m_nCountX = nCountX;
        m_nCountY = nCountY;
        m_nCount = m_nCountX * m_nCountY;

        m_PathWeight = new int[m_nCount];

        m_pHeadPath = new CHK_NODE( null, nStartPos );
        m_pTailPath = null;

        for ( i=0; i        {
            if ( pArryPath > 0 )  
                m_PathWeight = 0;
            else
                m_PathWeight = m_nCount + 1;
        }
        m_PathWeight[nStartPos] = 0;
        m_PathWeight[nObjPos] = m_nCount + 1;

        return FindPath( m_pHeadPath, nStartPos, nObjPos, 0, 0, 0 );
    }

    CHK_NODE * GetPath( ) { return m_pTailPath; }
};

89

主题

822

帖子

847

积分

高级会员

Rank: 4

积分
847
 楼主| 发表于 2005-12-22 15:26:00 | 显示全部楼层

Re:最近有点闲,有点闲,灌水一篇

自己的文章自己顶

50

主题

382

帖子

392

积分

中级会员

Rank: 3Rank: 3

积分
392
发表于 2005-12-22 15:46:00 | 显示全部楼层

Re:最近有点闲,有点闲,灌水一篇

我帮你顶

20

主题

118

帖子

118

积分

注册会员

Rank: 2

积分
118
发表于 2005-12-22 15:56:00 | 显示全部楼层

Re:最近有点闲,有点闲,灌水一篇

ts
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-23 01:20

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表