Unity寻路功能 A星算法和NavMesh导航

通常项目开发中,会遇到自动寻路功能

在Unity中利用自带的NavMesh导航可以实现自动寻路功能,操作简单

还有一种经常收开发者喜欢的A星寻路,下面简单介绍一下A星算法在Unity的实现过程

在unity中把地面看成是N个坐标做组成,那么这个坐标集合就是,世界坐标原点,也就是(0,0)点。

利用当前坐标对目标坐标判断的最小值 ,障碍物的坐标在坐标集合中移除,那么人物的行走路线就出现了;

一下是实现的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;

public class AStar : MonoBehaviour {

	private const int mapWidth=30;

	private const int mapHeigh=20;

	private Point [,]map=new Point[mapWidth,mapHeigh];

	public CanvasGroup star;
	public CanvasGroup nav;

	private bool ismove=false;

	// Use this for initialization
	void Start () {
		
		InitMap ();

		Point start = map [2, 3];

		CreatPlayer (2,3);

		Point end = map [10, 2];

		FindPath (start,end);

		Vector3 []newpath= ShowPath (start,end);
	
		MoveEndPoint (player, newpath);
	}

	private GameObject hitObj;

	void Update()
	{
		
		if (ismove == false) {

			if (Input.GetMouseButtonDown (0)) {
				Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
				RaycastHit hitInfo;
				if (Physics.Raycast (ray, out hitInfo)) {
					if (hitInfo.collider.gameObject == null) {
						Debug.Log ("NULL");
						return;
					} else {
						
						ismove = true;

						hitObj = hitInfo.collider.gameObject;
						hitObj.GetComponent<BoxCollider> ().enabled = false;

						hitobj.Add (hitObj);

						int a = (int)player.transform.position.x;
						int b = (int)player.transform.position.z;
						int c = (int)hitInfo.collider.gameObject.transform.position.x;
						int d = (int)hitInfo.collider.gameObject.transform.position.z;

						FindPath (map [a, b], map [c, d]);

						Vector3[] newpath = ShowPath (map [a, b], map [c, d]);

						MoveEndPoint (player, newpath);

						star.blocksRaycasts = false;
						star.interactable = false;
						nav.blocksRaycasts = false;
						nav.interactable = false;

					}
				}
			}
		} else
		{
			return;
		}

	}

	private Hashtable args;

	private void MoveEndPoint(GameObject obj, Vector3[] paths)
	{
		
		args = new Hashtable ();
		args.Add ("speed",10);
		args.Add ("path",paths);
		args.Add ("easeType",iTween.EaseType.linear);

		args.Add ("oncomplete","AnimationEnd");
		args.Add ("oncompleteparams","end");
		args.Add ("oncompletetarget",gameObject);

		iTween.MoveTo (obj, args);

		ismove = true;
	}

	private List<GameObject>hitobj=new List<GameObject> ();

	private void AnimationEnd(){

		ismove = false;
		Debug.Log ("动画结束");
		map [(int)player.transform.position.x, (int)player.transform.position.z].Parent = null;


		Destroy (endCube);

		if (hitobj.Count>=2) {
			
			hitobj [0].GetComponent<BoxCollider> ().enabled = true;

			hitobj.RemoveAt (0);
		}

		star.blocksRaycasts = true;
		star.interactable = true;
		nav.blocksRaycasts = true;
		nav.interactable = true;


	}

	private GameObject endCube;
	private Vector3[] ShowPath(Point strat, Point end)
	{
		Point temp = end;
		List<Vector3> points = new List<Vector3> ();
		while (true) 
		{
			//Debug.Log (temp.X+" sd "+temp.Y);

			points.Add (new Vector3(temp.X,0,temp.Y) );
			Color c = Color.gray;
			if (temp == strat) {
				
			} else if (temp == end) {
			   c = Color.red;
				CreatCube (temp.X, temp.Y, c);
			} 
				
			if (temp.Parent==null) 
				break;
			temp = temp.Parent;
		}
			
		Vector3[] paths =  points.ToArray ();

		Array.Reverse (paths);

		return paths;

	}

	private void CreatMap(int x,int y,bool judge)
	{
		GameObject go = GameObject.CreatePrimitive (PrimitiveType.Cube);
		go.AddComponent<AStarMap> ();
		go.transform.position = new Vector3 (x,-1,y);
		if (map [x, y].IsWall) {
			go.GetComponent<BoxCollider> ().enabled = false;
		}

		if (judge==true) {
		//退出	go.GetComponent<MeshRenderer> ().material.color = Color.black;

			hitobj.Add (go);

			go.GetComponent<BoxCollider> ().enabled = false;
		}
	}

	private void CreatCube(int x,int y,Color color)
	{
		GameObject go = GameObject.CreatePrimitive (PrimitiveType.Cube);

		go.transform.position = new Vector3 (x,0,y);

		go.GetComponent<MeshRenderer> ().material.color = color;

		go.GetComponent<BoxCollider> ().enabled = false;

		endCube = go;
	}

	GameObject player;

	private void CreatPlayer(int x,int y)
	{
		GameObject go = GameObject.CreatePrimitive (PrimitiveType.Sphere);

		go.transform.position = new Vector3 (x,0,y);

		go.GetComponent<MeshRenderer> ().material.color = Color.green;

		go.GetComponent<SphereCollider> ().enabled = false;

		player = go;
	}


	private void InitMap()
	{
		for (int i = 0; i < mapWidth; i++) {
			for (int j = 0; j < mapHeigh; j++) {
				map [i, j] = new Point (i,j);
			}
		}

		map [2, 15].IsWall = true;
		map [2, 18].IsWall = true;
		map [2, 19].IsWall = true;


		map [5, 13].IsWall = true;
		map [5, 12].IsWall = true;
		map [5, 13].IsWall = true;

		map [4, 2].IsWall = true;
		map [4, 3].IsWall = true;
		map [4, 4].IsWall = true;
		map [5, 2].IsWall = true;
		map [8, 3].IsWall = true;
		map [10, 4].IsWall = true;

		map [6, 2].IsWall = true;
		map [6, 3].IsWall = true;
		map [6, 4].IsWall = true;
		map [7, 8].IsWall = true;
		map [12, 5].IsWall = true;
		map [12, 6].IsWall = true;
		map [12, 7].IsWall = true;
		map [12, 8].IsWall = true;


		map [18, 3].IsWall = true;
		map [18, 15].IsWall = true;
		map [18, 16].IsWall = true;
		map [18, 17].IsWall = true;


		map [20, 12].IsWall = true;
		map [20, 18].IsWall = true;
		map [20, 19].IsWall = true;
		map [20, 10].IsWall = true;

		map [22, 12].IsWall = true;
		map [22, 5].IsWall = true;
		map [22, 6].IsWall = true;
		map [22, 10].IsWall = true;

		map [25, 2].IsWall = true;
		map [25, 8].IsWall = true;
		map [25, 9].IsWall = true;
		map [25, 10].IsWall = true;

		map [14, 15].IsWall = true;
		map [14, 16].IsWall = true;
		map [14, 17].IsWall = true;
		map [14, 18].IsWall = true;
		map [28, 5].IsWall = true;
		map [28, 6].IsWall = true;
		map [28, 7].IsWall = true;
		map [28, 8].IsWall = true;

		for (int i = 0; i < mapWidth; i++) {
			for (int j = 0; j < mapHeigh; j++) {
				if (map[i,j].IsWall) {
					CreatCube (i, j, Color.blue);


				}

				if (i == 10 && j == 2) {
					CreatMap (i,j,true);
				} else
				{
					CreatMap (i,j,false);
				}

			}
		}
	}

	private void FindPath(Point start,Point end)
	{
		List<Point> openList = new List<Point> ();
		List<Point> closeList = new List<Point> ();

		openList.Add (start);

		while(openList.Count>0)
		{
			Point point = FindMinFOfPoint (openList);
			openList.Remove (point);
			closeList.Add (point);
			List<Point> surroundPoints = GetSurroundPoints (point);
			PointsFilter (surroundPoints,closeList);
			foreach (Point surroundPoint in surroundPoints) {
				if (openList.IndexOf (surroundPoint) > -1) 
				{
					float nowG = CarcG (surroundPoint, point);
					if (nowG < surroundPoint.G) {
						surroundPoint.UpdateParent (point, nowG);
					} 

				} else
				{
					surroundPoint.Parent = point;
					CarcF (surroundPoint,end);
					openList.Add (surroundPoint);
				}
			}
			//判断一下
			if (openList.IndexOf(end)>-1) {
				break;
			}
		}
	}
		
	private void PointsFilter(List<Point>src,List<Point>closePoint){
		foreach (Point p in closePoint) {
			if (src.IndexOf(p)>-1) {
				src.Remove (p);
			}
		}
	}
		
	private List<Point>GetSurroundPoints(Point point)
	{
		Point up = null, down = null, left = null, right = null;
		Point lu = null, ru = null, ld = null, rd = null;
		if (point.Y < mapHeigh - 1) {
			up = map [point.X, point.Y + 1];
		} 
		if (point.Y>0) {
			down = map [point.X, point.Y-1];
		}
		if (point.X>0) {
			left = map [point.X - 1, point.Y];
		}
		if (point.X<mapWidth-1) {
			right = map [point.X + 1, point.Y];
		}

		if (up!=null&&left!=null) {
			lu = map [point.X - 1, point.Y + 1];
		}
		if (up!=null&&right!=null) {
			ru = map [point.X + 1, point.Y + 1];
		}
		if (down!=null&&left!=null) {
			ld = map [point.X - 1, point.Y - 1];
		}
		if (down!=null&&right!=null) {
			rd = map [point.X + 1, point.Y - 1];
		}
		List<Point> list = new List<Point> ();
		if (down!=null&&down.IsWall==false) {
			list.Add (down);
		}
		if (up!=null&&up.IsWall==false) {
			list.Add (up);
		}
		if (left!=null&&left.IsWall==false) {
			list.Add (left);
		}
		if (right!=null&&right.IsWall==false) {
			list.Add (right);
		}
		if (lu!=null&&lu.IsWall==false&&left.IsWall==false&&up.IsWall==false) {
			list.Add(lu);
		}
		if (ld!=null&&ld.IsWall==false&&left.IsWall==false&&down.IsWall==false) {
			list.Add(ld);
		}
		if (ru!=null&&ru.IsWall==false&&right.IsWall==false&&up.IsWall==false) {
			list.Add(ru);
		}
		if (rd!=null&&rd.IsWall==false&&right.IsWall==false&&down.IsWall==false) {
			list.Add(rd);
		}
		return list;
	}

	private Point FindMinFOfPoint(List<Point>openList)
	{
		float f = float.MaxValue;

		Point temp = null;

		foreach (Point p in openList)
		{
			if (p.F<f) 
			{
				temp = p;
				f = p.F;
			}
		}
		return temp;
	}

	private float CarcG(Point now,Point parent)
	{
		return	Vector2.Distance (new Vector2 (now.X, now.Y), new Vector2 (parent.X, parent.Y))+parent.G;
	}

	private void CarcF(Point now,Point end)
	{
		//F=G+H
		float h=Mathf.Abs(now.X-end.X)+Mathf.Abs(now.Y-end.Y);

		float g=0;
		if (now.Parent == null) {
			g = 0;
		} else 
		{
			g=Vector2.Distance (new Vector2 (now.X, now.Y), new Vector2 (now.Parent.X, now.Parent.Y))+now.Parent.G;
		}
		float f = g + h;
		now.F = f;
		now.G = g;
		now.H = h;
	}
}

NavMash系统自带的组件,使用很简单,就不作过多介绍。项目中也已经实现

运行效果

源码下载链接:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个简单的Unity C#脚本来实现A*算法: ```csharp using UnityEngine; using System.Collections.Generic; public class AStar : MonoBehaviour { public Transform seeker, target; // 寻路的起点和终点 Grid grid; // 存储地图信息的网格 void Awake() { grid = GetComponent<Grid>(); } void Update() { FindPath(seeker.position, target.position); } void FindPath(Vector3 startPos, Vector3 targetPos) { Node startNode = grid.NodeFromWorldPoint(startPos); // 起点所在的网格节点 Node targetNode = grid.NodeFromWorldPoint(targetPos); // 终点所在的网格节点 List<Node> openSet = new List<Node>(); // 未处理的节点集合 HashSet<Node> closedSet = new HashSet<Node>(); // 已处理的节点集合 openSet.Add(startNode); // 将起点加入未处理的节点集合 while (openSet.Count > 0) { Node currentNode = openSet[0]; // 当前处理的节点 for (int i = 1; i < openSet.Count; i++) { if (openSet[i].fCost < currentNode.fCost || (openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)) { currentNode = openSet[i]; } } openSet.Remove(currentNode); closedSet.Add(currentNode); if (currentNode == targetNode) { // 已找到终点 RetracePath(startNode, targetNode); return; } foreach (Node neighbor in grid.GetNeighbors(currentNode)) { if (!neighbor.walkable || closedSet.Contains(neighbor)) { continue; } int newMovementCostToNeighbor = currentNode.gCost + GetDistance(currentNode, neighbor); if (newMovementCostToNeighbor < neighbor.gCost || !openSet.Contains(neighbor)) { neighbor.gCost = newMovementCostToNeighbor; neighbor.hCost = GetDistance(neighbor, targetNode); neighbor.parent = currentNode; if (!openSet.Contains(neighbor)) { openSet.Add(neighbor); } } } } } void RetracePath(Node startNode, Node endNode) { List<Node> path = new List<Node>(); Node currentNode = endNode; while (currentNode != startNode) { path.Add(currentNode); currentNode = currentNode.parent; } path.Reverse(); grid.path = path; } int GetDistance(Node nodeA, Node nodeB) { int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX); int dstY = Mathf.Abs(nodeA.gridY - nodeB.gridY); if (dstX > dstY) { return 14 * dstY + 10 * (dstX - dstY); } else { return 14 * dstX + 10 * (dstY - dstX); } } } ``` 这个脚本依赖于一个名为`Grid`的组件,它存储了地图信息的网格。 `Grid`组件的实现不在本文的讨论范围内,你可以参考其他教程或者使用你自己的实现。 在`Update()`函数中,我们不断地调用`FindPath()`函数来执行A*算法。在`FindPath()`函数中,我们首先找到起点和终点所在的网格节点,然后使用A*算法找到从起点到终点的最短路径。最后,我们使用`RetracePath()`函数来反向遍历路径链表,并将路径保存在`Grid`组件的`path`变量中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_37114869

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值