需求
unity3d的3d开发环境中,原生自带了Navigation的组件,可以很便捷快速的实现寻路功能。但是在原生的2d中并没有相同的功能。
现在国内很多手机游戏都有自动寻路的功能,或者游戏中存在一些例如机器人、npc等,都需要自动寻路的功能。
我需要实现的功能类似于当年FC游戏中淘金者的运动方式。游戏中有淘金者、敌人,可移动,不可移动区域,只能沿着直线的向前向后或者向上向下。
思路
unity3d中也有一些2d寻路的插件。例如A Pathfinding Project Pro和NavMesh 2D。
两个插件都是收费插件,可以满足不同需求,但是我用过之后发现并不是我想要的效果,而且修改别人的代码的还是挺难受的。所以决定自己写一遍A*算法。
A*算法已经有很多大牛的博客中,都有非常不错的介绍。我看的是这篇,给大家推荐下:传送
这篇文章的代码在于拐点的处理上是有一定问题的,我在下面的代码中有进行修改。
解决方案
先上结果,实现后的路径是这样的。请忽略那只乱入恐龙,他其实只是来打酱油的~~
核心代码如下:
1 public Point FindPath (Point start, Point end, bool IsIgnoreCorner)
2 {
3 OpenList.Add (start);
4 while (OpenList.Count != 0) {
5 //找出F值最小的点
6 var tempStart = OpenList.MinPoint ();
7 OpenList.RemoveAt (0);
8 CloseList.Add (tempStart);
9 //找出它相邻的点
10 var surroundPoints = SurrroundPoints (tempStart, IsIgnoreCorner);
11 foreach (Point point in surroundPoints) {
12 if (OpenList.Exists (point))
13 //计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F
14 FoundPoint (tempStart, point);
15 else
16 //如果它们不在开始列表里, 就加入, 并设置父节点,并计算GHF
17 NotFoundPoint (tempStart, end, point);
18 }
19 if (OpenList.Get (end) != null)
20 return OpenList.Get (end);
21 }
22 return OpenList.Get (end);
23 }
原文中有些小问题的CanReach方向,我修改的如下:
1 public bool CanReach (Point start, int x, int y, bool IsIgnoreCorner)
2 {
3 if (!CanReach (x, y) || CloseList.Exists (x, y))
4 return false;
5 else {
6 if (Math.Abs (x - start.X) + Math.Abs (y - start.Y) == 1)
7 return true;
8 //如果是斜方向移动, 判断是否 "拌脚"
9 else {
10 if (IsIgnoreCorner) {
11 if (CanReach (Math.Abs (x - 1), y) && CanReach (x, Math.Abs (y - 1)))
12 return true;
13 else
14 return false;
15 } else
16 return false;
17 }
18 }
19 }
总结
算法还是要多自己写一些,总是拿来主义不利于自己的成长,在大学里学过了,基本都还给老师了,项目里的东西,要用也要明明白白的用,防止为以后的开发留下隐患,到时候从头再找就需要话费给多的时间了。
项目源代码中包含了spine的例子,所以有点大,有兴趣的朋友可以下载去玩一玩。
点击下载源代码