先上图
兰色格子是起点
黄色格子是终点
绿框格子是将要探索的地方
蓝框格子是已经走过的路
最后出现的绿色格子就是算出的路线
思路
我是看的他的文章, 写的很棒, 深入浅出:
https://blog.csdn.net/zhulichen/article/details/78786493
下面放代码
花了一个中午时间瞎写的, 代码很乱, 知道思路就行
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public struct AStarDef
{
public static Color colorDead = new Color(60 / 255f, 60 / 255f, 60 / 255f, 100 / 255f);
public static Color colorStart = new Color(60 / 255f, 1, 1, 100 / 255f);
public static Color colorEnd = new Color(1, 1, 60 / 255f, 100 / 255f);
public static Color colorPath = new Color(60 / 255f, 1, 60 / 255f, 100 / 255f);
public static Color colorCur = new Color(60 / 255f, 1, 1);
public static Color colorCanWalk = new Color(60 / 255f, 1, 60 / 255f);
public static Color colorNull = new Color(200 / 255f, 200 / 255f, 200 / 255f);
public static Color colorChecked = new Color(60 / 255f, 60 / 255f, 1);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStarMgr : MonoBehaviour
{
public Transform parent;
public AStarCube prefab;
float height;
Vector3 offset;
AStarCube startCube;
AStarCube endCube;
bool isComplete = false;
bool isAuto = false;
const int maxX = 10;
const int maxY = 10;
const int deadNum = 40;
//全部方块
Dictionary<int, AStarCube> allCubes = new Dictionary<int, AStarCube>();
//每个方块 和它的8个邻居
Dictionary<AStarCube, List<AStarCube>> allNeighbours = new Dictionary<AStarCube, List<AStarCube>>();
//每个可走的方块 和它的可走的8个邻居
Dictionary<AStarCube, List<AStarCube>> availableNeighbours = new Dictionary<AStarCube, List<AStarCube>>();
//待走的方块
List<AStarCube> canWalks = new List<AStarCube>();
//不可走的方块
List<AStarCube> deads = new List<AStarCube>();
void Awake()
{
height = prefab.GetComponent<RectTransform>().rect.height;
offset = Vector3.one * height;
CreateAllCubes();
Reset();
StartCoroutine(AutoNext());
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Next();
}
if (Input.GetKeyDown(KeyCode.R))
{
Reset();
}
if (Input.GetKeyDown(KeyCode.A))
{
isAuto = !isAuto;
}
}
void Reset()
{
foreach (AStarCube item in allCubes.Values)
{
item.Reset();
}
CreateDiedCubes();
CalcuAvailableNeighbours();
SetStartCubeEndCube();
}
void SetStartCubeEndCube()
{
isComplete = false;
startCube = null;
endCube = null;
while (startCube == null || endCube == null)
{
int x = Random.Range(0, maxX);
int y = Random.Range(0, maxY);
int id = GetID(x, y);
if (allCubes.ContainsKey(id))
{
if (!deads.Contains(allCubes[id]))
{
if (startCube == null)
{
startCube = allCubes[id];
deads.Add(allCubes[id]);
}
else if (endCube == null)
{
endCube = allCubes[id];
deads.Add(allCubes[id]);
}
}
}
}
startCube.SetStart();
endCube.SetEnd();
canWalks.Clear();
canWalks.Add(startCube);
}
WaitForSeconds wait = new WaitForSeconds(0.2f);
WaitForSeconds wait1 = new WaitForSeconds(1);
IEnumerator AutoNext()
{
while (true)
{
if (isAuto)
{
if (!isComplete)
{
Next();
}
else
{
yield return wait1;
Reset();
}
}
yield return wait;
}
}
void Next()
{
if (isComplete)
{
print("已完成, 别再按空格了, 按R键重置");
return;
}
if (canWalks.Count == 0)
{
isComplete = true;
print("无路可走");
return;
}
AStarCube me = null;
for (int i = 0; i < canWalks.Count; i++)
{
//这里用>=, 而不是用>, 是为了优先选择最新的
if (me == null || me.F >= canWalks[i].F)
{
me = canWalks[i];
}
}
canWalks.Remove(me);
List<AStarCube> ns = availableNeighbours[me];
if (ns.Count == 0)
{
Next();
}
for (int i = 0; i < ns.Count; i++)
{
AStarCube neighbour = ns[i];
if (neighbour.isDead)
{
continue;
}
if (neighbour.isChecked)
{
continue;
}
neighbour.SetCanWalk();
me.SetChecked();
if (!canWalks.Contains(neighbour))
{
canWalks.Add(neighbour);
}
neighbour.G = me.G + GetDistance(me, neighbour);
neighbour.H = GetDistance(neighbour, endCube);
neighbour.F = neighbour.G + neighbour.H;
if (neighbour.myDir == null)
{
neighbour.myDir = me;
}
if (neighbour == endCube)
{
print("找到了!");
MarkPath(endCube);
isComplete = true;
}
}
}
float GetDistance(AStarCube a, AStarCube b)
{
int xDis = Mathf.Abs(a.x - b.x);
int yDis = Mathf.Abs(a.Y - b.Y);
int max = xDis > yDis ? xDis : yDis;
int orthogonal = Mathf.Abs(xDis - yDis);
int diagonal = max - orthogonal;
return diagonal * 1.4f + orthogonal;
}
void MarkPath(AStarCube item)
{
if (item.myDir != null)
{
item.myDir.SetPathMark();
MarkPath(item.myDir);
}
else
{
return;
}
}
//生成白色方块
void CreateAllCubes()
{
for (int x = 0; x < maxX; x++)
{
for (int y = 0; y < maxX; y++)
{
AStarCube go = Instantiate<AStarCube>(prefab, new Vector3(x, y) * height + offset, Quaternion.identity, parent);
go.x = x;
go.Y = y;
int id = GetID(x, y);
allCubes.Add(id, go);
}
}
foreach (KeyValuePair<int, AStarCube> item in allCubes)
{
int[] nbsArray = GetMyNeighbours(item.Key);
List<AStarCube> nbs = new List<AStarCube>();
for (int i = 0; i < nbsArray.Length; i++)
{
if (allCubes.ContainsKey(nbsArray[i]))
{
nbs.Add(allCubes[nbsArray[i]]);
}
}
allNeighbours.Add(item.Value, nbs);
}
}
int[] GetMyNeighbours(int me)
{
int y = me % 100;
int x = (me - y) / 100;
int next0 = GetID(x - 1, y);
int next1 = GetID(x + 1, y);
int next2 = GetID(x, y - 1);
int next3 = GetID(x, y + 1);
int next4 = GetID(x - 1, y - 1);
int next5 = GetID(x - 1, y + 1);
int next6 = GetID(x + 1, y - 1);
int next7 = GetID(x + 1, y + 1);
return new int[] { next0, next1, next2, next3, next4, next5, next6, next7 };
}
//生成不可走的方块
void CreateDiedCubes()
{
deads.Clear();
for (int i = 0; i < deadNum; i++)
{
int x = Random.Range(0, maxX);
int y = Random.Range(0, maxY);
int id = GetID(x, y);
if (allCubes.ContainsKey(id))
{
allCubes[id].SetDead();
if (!deads.Contains(allCubes[id]))
{
deads.Add(allCubes[id]);
}
}
}
}
void CalcuAvailableNeighbours()
{
availableNeighbours.Clear();
foreach (KeyValuePair<AStarCube, List<AStarCube>> item in allNeighbours)
{
if (!item.Key.isDead)
{
List<AStarCube> n = new List<AStarCube>();
for (int i = 0; i < item.Value.Count; i++)
{
if (!item.Value[i].isDead)
{
n.Add(item.Value[i]);
}
}
availableNeighbours.Add(item.Key, n);
CalcuNeighbours(item.Key, n);
}
}
}
void CalcuNeighbours(AStarCube me, List<AStarCube> ns)
{
int x = me.x;
int y = me.Y;
int up = GetID(x, y + 1);
int down = GetID(x, y - 1);
int left = GetID(x - 1, y);
int right = GetID(x + 1, y);
int upleft = GetID(x - 1, y + 1);
int upright = GetID(x + 1, y + 1);
int downleft = GetID(x - 1, y - 1);
int downright = GetID(x + 1, y - 1);
if (allCubes.ContainsKey(up) && allCubes[up].isDead)
{
if (allCubes.ContainsKey(upleft))
{
ns.Remove(allCubes[upleft]);
}
if (allCubes.ContainsKey(upright))
{
ns.Remove(allCubes[upright]);
}
}
if (allCubes.ContainsKey(down) && allCubes[down].isDead)
{
if (allCubes.ContainsKey(downleft))
{
ns.Remove(allCubes[downleft]);
}
if (allCubes.ContainsKey(downright))
{
ns.Remove(allCubes[downright]);
}
}
if (allCubes.ContainsKey(left) && allCubes[left].isDead)
{
if (allCubes.ContainsKey(upleft))
{
ns.Remove(allCubes[upleft]);
}
if (allCubes.ContainsKey(downleft))
{
ns.Remove(allCubes[downleft]);
}
}
if (allCubes.ContainsKey(right) && allCubes[right].isDead)
{
if (allCubes.ContainsKey(upright))
{
ns.Remove(allCubes[upright]);
}
if (allCubes.ContainsKey(downright))
{
ns.Remove(allCubes[downright]);
}
}
}
int GetID(int x, int y)
{
return x * 100 + y;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class AStarCube : MonoBehaviour
{
public AStarCube myDir;
public bool isDead;
public bool isChecked;
public Image bg;
public Text textXY;
public Text textG;
public Text textH;
public Text textF;
public Image dead;
public int x;
private int y;
public int Y
{
get { return y; }
set
{
y = value;
textXY.text = "(" + x.ToString() + "," + y.ToString() + ")";
}
}
private float g;
public float G
{
get { return g; }
set
{
g = value;
textG.text = g.ToString("F1");
}
}
private float h;
public float H
{
get { return h; }
set
{
h = value;
textH.text = h.ToString("F1");
}
}
private float f;
public float F
{
get { return f; }
set
{
f = value;
textF.text = f.ToString("F1");
}
}
public void SetStart()
{
dead.gameObject.SetActive(true);
dead.color = AStarDef.colorStart;
}
public void SetEnd()
{
dead.gameObject.SetActive(true);
dead.color = AStarDef.colorEnd;
}
public void SetCanWalk()
{
bg.color = AStarDef.colorCanWalk;
}
public void SetDead()
{
dead.gameObject.SetActive(true);
dead.color = AStarDef.colorDead;
isDead = true;
}
public void SetChecked()
{
bg.color = AStarDef.colorChecked;
isChecked = true;
}
public void SetCur()
{
bg.color = AStarDef.colorCur;
}
public void SetPathMark()
{
dead.gameObject.SetActive(true);
dead.color = AStarDef.colorPath;
}
public void Reset()
{
isChecked = false;
isDead = false;
dead.gameObject.SetActive(false);
bg.color = AStarDef.colorNull;
G = 0;
H = 0;
F = 0;
myDir = null;
}
}