版权声明:本文为博主原创文章,未经博主允许不得转载。 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是否可移动与移动消耗等参数,等到添加游戏参数时再来细说。