SRPG游戏开发(二十四)第七章 寻路与地图对象 - 二 搜索范围算法(Search Range Algorithm)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/darkrabbit/article/details/82315637

返回总目录

第七章 寻路与地图对象(Pathfinding and Map Object)

这一章主要进行寻路与地图对象的部分工作。



二 搜索范围算法(Search Range Algorithm)

移动范围与攻击范围的搜索算法十分的类似,所以我们先假定所有格子所消耗的移动点数都为1,
在这之后再来改造它们。为了便于在Unity中使用,我们让它继承自ScriptableObject

要注意的是,搜索范围并没有目标节点,而是限定在一个范围中,所以可以不用H值。

建立搜索范围算法:

namespace DR.Book.SRPG_Dev.Maps.FindPath
{
    [CreateAssetMenu(fileName = "FindRange.asset", menuName = "SRPG/How to find range")]
    public class FindRange : ScriptableObject, IHowToFind
    {
        public virtual CellData ChoseCell(PathFinding search)
        {
            // TODO

            return null;
        }

        public virtual bool IsFinishedOnChose(PathFinding search)
        {
            // TODO

            return true;
        }

        public virtual float CalcGPerCell(PathFinding search, CellData adjacent)
        {
            return 1f;
        }

        public virtual float CalcH(PathFinding search, CellData adjacent)
        {
            return 0f;
        }

        public virtual bool CanAddAdjacentToReachable(PathFinding search, CellData adjacent)
        {
            // TODO

            return true;
        }

        public virtual void BuildResult(PathFinding search)
        {
            // TODO
        }
    }
}

1 选择节点(Chose Cell)

在显示范围上,我们要搜索全部的可用网格,并不是点到点的搜索,这样我们可能并不需要进行排序。

首先,在开放集中选择并提取节点:

        public virtual CellData ChoseCell(PathFinding search)
        {
            if (search.reachable.Count == 0)
            {
                return null;
            }

            int index = search.reachable.Count - 1;
            CellData chose = search.reachable[index];
            search.reachable.RemoveAt(index);
            return chose;
        }

其次,选择节点后,将节点加入到关闭集中,还应该进行判断是否完成目标;
当然,这里由于我们没有目标点,所以只有当开放集中节点数量为0时结束。

        public virtual bool IsFinishedOnChose(PathFinding search)
        {
            if (search.currentCell == null)
            {
                return true;
            }

            if (!search.IsCellInExpored(search.currentCell))
            {
                search.explored.Add(search.currentCell);
            }
            return false;
        }

2 加入到开放集中(Add to Open Set)

在邻居节点加入到开放集之前:

  • 判断邻居节点是否已经在开放集中;

  • 计算邻居节点损失;

  • 判断邻居节点是否已经在关闭集中;

  • 判断邻居节点是否不在限定范围(损失)内。

以上需要判断的部分,全部为否时才加入到开放集中。

        public virtual bool CanAddAdjacentToReachable(PathFinding search, CellData adjacent)
        {
            // 如果已经在关闭集
            if (search.IsCellInExpored(adjacent))
            {
                return false;
            }

            // 已经加入过开放集
            if (search.IsCellInReachable(adjacent))
            {
                return false;
            }

            // 计算消耗 = 当前cell的消耗 + 邻居cell的消耗
            float g = search.currentCell.g + CalcGPerCell(search, adjacent);

            // 不在范围内
            if (g < 0f || g > search.range.y)
            {
                return false;
            }

            adjacent.g = g;

            return true;
        }

你会注意到在范围内判断为g < 0f,而没有使用范围的最小值。这是因为你的攻击范围可能不是从1开始的(例如攻击范围为2-3),如果选择search.range.x会造成提前结束搜索,不会搜索到想要的结果,甚至不再搜索(还没有开始就结束了)。你不用担心结果会出错,因为生成结果时会进行判断筛选。


3 生成结果(Build Result)

当搜索结束后,需要返回结果。

在限定范围内的所有节点:

        public virtual void BuildResult(PathFinding search)
        {
            for (int i = 0; i < search.explored.Count; i++)
            {
                CellData cell = search.explored[i];
                if (cell.g >= search.range.x && cell.g <= search.range.y)
                {
                    search.result.Add(cell);
                }
            }
        }

这样,基础的搜索范围就完成了。看起来你可以直接使用它作为搜索攻击范围的方法了;而移动范围要复杂一些,需要判断Tile是否可移动与移动消耗等参数,等到添加游戏参数时再来细说。


猜你喜欢

转载自blog.csdn.net/darkrabbit/article/details/82315637