项目实训(十五)——关于汽车路线规划的相关优化

一、前言

由于之前实现的车辆路线规划的相关功能不够完善,且之前的代码实现较为复杂,在场景中进行使用有一定的难度,所以我编写了一些新的脚本对功能进行了补充。

二、CarPath车辆路线相关脚本及功能

如图所示,在为汽车装载上CarPath脚本之后,就可以为汽车添加Node作为汽车行驶路线的检查点、对汽车行驶路线是否循环、汽车速度等属性进行设置。



[ExecuteInEditMode]
public class CarPath : MonoBehaviour {

	[HideInInspector]
	public List<GameObject> Nodes = new List<GameObject>();
	//public List<float> Speeds = new List<float>();

	public List<NodeListClass> NodesProperties = new List<NodeListClass>();

	[HideInInspector]
	public float RotationSpeed = 50f;
	[HideInInspector]
	private int NumberOfTurnPoints = 100;
	[HideInInspector]
	public float NodeThreshold = 0.25f;

	public bool Repeat = true;
	public bool ReverseDirections = false;
	public bool SingleSpeedForAllNodes = false;
	[Range(0.01f,120f)]
	public float SingleSpeed;
	//public bool UpdateAfterEveryLoop = false;

	public GameObject NodeParent;

	List<Vector3> points = new List<Vector3>();

	public IEnumerator ienum;

	Quaternion SampledRotation;

	void Update(){



	}

	void OnDrawGizmos(){

		if(Repeat){
			if(Nodes.Count >= 2){
				Gizmos.DrawLine(Nodes.Last().transform.position,Nodes.First().transform.position);
			}
		}else{

			Nodes.First().GetComponent<NodeScript>().BefNode = null;
			Nodes.First().GetComponent<NodeScript>().CurvePoint = false;

			Nodes.Last().GetComponent<NodeScript>().AftNode = null;
			Nodes.Last().GetComponent<NodeScript>().CurvePoint = false;

		}

		//foreach(Vector3 point in points){

			//Gizmos.DrawSphere(point, 0.15f);

		//}

	}

	/*private float m_LastEditorUpdateTime;

	public virtual void Play(){
		
		//Debug.Log("Nothing here ATM");
		m_LastEditorUpdateTime = Time.realtimeSinceStartup;
		EditorApplication.update += OnEditorUpdate;

	}

	protected virtual void Stop(){

		//Debug.Log("Nothing here ATM");
		EditorApplication.update -= OnEditorUpdate;
		transform.position = points.First();
		//transform.LookAt()

	}

	protected virtual void OnEditorUpdate()
	{
		// In here you can check the current realtime, see if a certain
		// amount of time has elapsed, and perform some task.
	}*/

	public void AddNode(){

		if(NodeParent != null){

			GameObject new_node = new GameObject("Node");

			new_node.AddComponent<NodeScript>();

			if(Nodes.Count == 0){
				new_node.transform.position = transform.position;
			}else{
				new_node.GetComponent<NodeScript>().BefNode = Nodes.Last();
				new_node.transform.position = Nodes.Last().transform.position;
				Nodes.ToArray()[Nodes.Count-1].GetComponent<NodeScript>().AftNode = new_node;
				Nodes.First().GetComponent<NodeScript>().BefNode = new_node;
			}

			new_node.transform.parent = NodeParent.transform;
			new_node.GetComponent<NodeScript>().Car = gameObject;

			#if UNITY_EDITOR

			Selection.activeGameObject = new_node;

			#endif

			Nodes.Add(new_node);

		}else{

			Debug.Log("No Node Parent set, creating one automatically");
			NodeParent = new GameObject("NodeParent");
			NodeParent.transform.position = Vector3.zero;

			GameObject new_node = new GameObject("Node");

			new_node.AddComponent<NodeScript>();

			if(Nodes.Count == 0){
				new_node.transform.position = transform.position;
			}else{
				new_node.GetComponent<NodeScript>().BefNode = Nodes.Last();
				new_node.transform.position = Nodes.Last().transform.position;
				Nodes.ToArray()[Nodes.Count-1].GetComponent<NodeScript>().AftNode = new_node;
				Nodes.First().GetComponent<NodeScript>().BefNode = new_node;
			}

			new_node.transform.parent = NodeParent.transform;
			new_node.GetComponent<NodeScript>().Car = gameObject;

			#if UNITY_EDITOR

			Selection.activeGameObject = new_node;

			#endif

			Nodes.Add(new_node);

		}


	}

	public void RemoveNode(){

		if(Nodes.Count != 0){
			DestroyImmediate(Nodes.Last());
			Nodes.Remove(Nodes.Last());

			//Nodes.Last().GetComponent<NodeScript>().AftNode = null;
			//Nodes.First().GetComponent<NodeScript>().AftNode = null;

			if(Nodes.Last().GetComponent<NodeScript>().CurvePoint || Nodes.First().GetComponent<NodeScript>().CurvePoint){
				Nodes.Last().GetComponent<NodeScript>().CurvePoint = false;
				Nodes.First().GetComponent<NodeScript>().CurvePoint = false;
			}

		}else{

			Debug.Log("No nodes to remove");

		}

	}

	public void RecalculatePath(){

		//if(Application.isPlaying) {

		NodesProperties.Clear();

		for(int i = 0; i < Nodes.Count; i++) {
			//Debug.Log("yup");
			NodesProperties.Add(Nodes.ToArray()[i].GetComponent<NodeScript>().ThisNodeClass);
			//Debug.Log(Nodes.ToArray()[i].GetComponent<NodeScript>().ThisNodeClass.Speed);
			//Debug.Log(NodesTest.ToArray()[i].Speed);
		}

		float sum = 0f;
		foreach(GameObject node in Nodes) {
			sum += node.GetComponent<NodeScript>().Speed;
		}
		sum = sum / Nodes.Count;

		//sum_p = sum;

		if(sum < 12f) {

			NodeThreshold = 0.35f;
			RotationSpeed = 30f;

		} else if(sum >= 20f && sum < 50f) {

			NodeThreshold = 0.95f;
			RotationSpeed = 100f;

		} else if(sum >= 50f && sum < 100f) {

			NodeThreshold = 1.55f;
			RotationSpeed = 120f;

		} else if(sum >= 100f && sum < 200f) {

			NodeThreshold = 1.95f;
			RotationSpeed = 150f;

		} else if(sum >= 200 && sum < 300) {

			NodeThreshold = 2.75f;
			RotationSpeed = 250f;

		} else if(sum >= 300) {

			NodeThreshold = 2.5f;
			RotationSpeed = 350f;

		}

		StopAllCoroutines();

		points.Clear();

		int n = 0;

		while(n < Nodes.ToArray().Length) {

			if(Nodes.ToArray()[n].GetComponent<NodeScript>().CurvePoint == true) {

				float t = 0f;
				Vector3 position = Vector3.zero;
				for(int i = 0; i < NumberOfTurnPoints; i++) {
					t = i / (NumberOfTurnPoints - 1.0f);
					position = (1.0f - t) * (1.0f - t) * Nodes.ToArray()[n].GetComponent<NodeScript>().CurveStartPoint + 2.0f * (1.0f - t) * t * Nodes.ToArray()[n].transform.position + t * t * Nodes.ToArray()[n].GetComponent<NodeScript>().CurveEndPoint;

					points.Add(position);
				}

				n++;

			} else {

				points.Add(Nodes.ToArray()[n].transform.position);
				n++;

			}

		}

		if(ReverseDirections){
			points.Reverse ();
		}

		if(Nodes.Count >= 2) {

			StartCoroutine(Following());

		} else {

			Debug.Log("Too few nodes to follow");

		}



		//} else {
		
			//Debug.Log("Use 'Recalculate Path' in Play mode, the path gets updated after every loop in edit mode");

		//}

	}
		
	void OnValidate(){

		if(SingleSpeedForAllNodes) {
			foreach(NodeListClass node_lc in NodesProperties) {
				node_lc.Node.GetComponent<NodeScript>().Speed = SingleSpeed;
			}
		} else {
			foreach(NodeListClass node_lc in NodesProperties) {
				node_lc.Node.GetComponent<NodeScript>().Speed = node_lc.Speed;
			}
		}

		//if(Application.isPlaying){
			//RecalculatePath();
		//}

		//RecalculatePath();

	}
		
	protected virtual void EditorUpdate(){
		if(ienum == null) {
			ienum = Following();
		}
		if(!Application.isPlaying){
			if(ienum.MoveNext()) {
				for(int i = 0; i < 2500; i++) {
					ienum.MoveNext();
				}
			}
		}
			
	}

	public void SampleRotation(){
		SampledRotation = transform.rotation;
	}

	public void MTSP(){

		transform.position = Nodes.First ().transform.position;
		transform.rotation = SampledRotation;

	}

	#if UNITY_EDITOR

	public void Play(){
		//MonoBehaviour.res
		if(!Application.isPlaying) {
			//StopAllCoroutines();
			EditorApplication.update -= EditorUpdate;
			//RecalculatePath();
			EditorApplication.update += EditorUpdate;
			//goto restart;
		} else {
			Debug.Log("Play is only used in edit mode");
		}
	}

	public void Stop(){
		if(!Application.isPlaying) {
			EditorApplication.update -= EditorUpdate;
			//transform.position = Nodes.First().transform.position;
			//StopCoroutine(ienum);
			StopAllCoroutines();
		} else {
			Debug.Log("Stop is only used in edit mode");
		}
	}

	#endif

	void Start () {

		if(!Application.isPlaying) {
			ienum = Following();
		}

		//NodesTest = new List<NodeListClass>(new NodeListClass[Nodes.Count]);

		if(NodesProperties.Count == 0){
			for(int i = 0; i < Nodes.Count; i++){
				//Debug.Log("yup");
				NodesProperties.Add(Nodes.ToArray()[i].GetComponent<NodeScript>().ThisNodeClass);
				//Debug.Log(Nodes.ToArray()[i].GetComponent<NodeScript>().ThisNodeClass.Speed);
				//Debug.Log(NodesTest.ToArray()[i].Speed);
			}
		}

		//EditorApplication.update -= TestUpdate;

		//Play();
		float sum = 0f;
		foreach(GameObject node in Nodes){
			sum+=node.GetComponent<NodeScript>().Speed;
		}
		sum=sum/Nodes.Count;

		//sum_p = sum;

		if(sum < 12f){

			NodeThreshold = 0.35f;
			RotationSpeed = 30f;

		}else if(sum >= 20f && sum < 50f){

			NodeThreshold = 0.95f;
			RotationSpeed = 100f;

		}else if(sum >= 50f && sum < 100f){

			NodeThreshold = 1.35f;
			RotationSpeed = 120f;

		}else if(sum >= 100f && sum < 200f){

			NodeThreshold = 1.55f;
			RotationSpeed = 150f;

		}else if(sum >= 200 && sum < 300){

			NodeThreshold = 2.75f;
			RotationSpeed = 250f;

		}else if(sum >= 300){

			NodeThreshold = 2.5f;
			RotationSpeed = 350f;

		}


		//NodeThreshold = Mathf.Clamp(sum/20f,0.2f, 1.25f);
		//RotationSpeed = Mathf.Clamp(sum/1.5f, 20f, 200f);

		int n = 0;

		while(n < Nodes.ToArray().Length){

			if(Nodes.ToArray()[n].GetComponent<NodeScript>().CurvePoint == true){

				float t = 0f;
				Vector3 position = Vector3.zero;
				for(int i = 0; i < NumberOfTurnPoints; i++) 
				{
					t = i / (NumberOfTurnPoints - 1.0f);
					position = (1.0f - t) * (1.0f - t) * Nodes.ToArray()[n].GetComponent<NodeScript>().CurveStartPoint + 2.0f * (1.0f - t) * t * Nodes.ToArray()[n].transform.position + t * t * Nodes.ToArray()[n].GetComponent<NodeScript>().CurveEndPoint;

					points.Add(position);
				}

				n++;

			}else{

				points.Add(Nodes.ToArray()[n].transform.position);
				n++;

			}

		}

		if(ReverseDirections){
			points.Reverse ();
		}

		if(Nodes.Count >= 2){

			StartCoroutine(Following());

		}else{

			Debug.Log("Too few nodes to follow");

		}

		//IEnumerator e = Following();
		//while (e.MoveNext());

	}

	public void ReevaluatePath(){
		Nodes.Clear();// = Car.GetComponent<CarPath>().NodeParent.GetComponentsInChildren<GameObject>().ToList();

		foreach (Transform child in NodeParent.transform)
		{
			Nodes.Add(child.gameObject);
		}
			
		RecalculatePath();
	}

	float OriginalSpeed;

	public IEnumerator Following(){

		restart:

		int node_number = 0;

		int point_number = 0;

		foreach(Vector3 point in points){

			bool stopping = false;

			bool accelerating = false;

			//Debug.Log (Nodes.Count + " - Count");
			//Debug.Log (node_number);

	

			if (Nodes.ToArray () [node_number].GetComponent<NodeScript> ().StopPoint && Nodes.ToArray () [node_number].GetComponent<NodeScript> ().SmoothStopping) {

				stopping = true;

				OriginalSpeed = Nodes.ToArray () [node_number].GetComponent<NodeScript> ().Speed;

			} else if (node_number != 0) { 

				if (Nodes.ToArray () [node_number - 1].GetComponent<NodeScript> ().StopPoint && Nodes.ToArray () [node_number - 1].GetComponent<NodeScript> ().SmoothStopping) {

					accelerating = true;

					OriginalSpeed = Nodes.ToArray () [node_number].GetComponent<NodeScript> ().Speed;

					Nodes.ToArray () [node_number].GetComponent<NodeScript> ().Speed = 2f;

				}

			}

		

			while(Vector3.Distance(transform.position, point) > NodeThreshold){

				if(node_number < Nodes.Count) {
				transform.position = Vector3.MoveTowards(transform.position, point, Time.deltaTime * Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed);
				} else {
					transform.position = Vector3.MoveTowards(transform.position, point, Time.deltaTime * SingleSpeed);
				}
				if(points[point_number] - transform.position != Vector3.zero) {
					transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(points[point_number] - transform.position, Vector3.up), Time.deltaTime * RotationSpeed);
				}else if(point_number+1 < points.Count){
					transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(points[point_number+1] - transform.position, Vector3.up), Time.deltaTime * RotationSpeed);
				}


				if(stopping) {


					if(Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed <= 0.05f) {
						if(SingleSpeedForAllNodes) {
							Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed = SingleSpeed;
						} else {
							Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed = OriginalSpeed;
						}
						break;
					} else {

						float param = Mathf.InverseLerp(0f, Vector3.Distance(Nodes.ToArray()[node_number].transform.position, Nodes.ToArray()[node_number - 1].transform.position), Vector3.Distance(transform.position, Nodes.ToArray()[node_number - 1].transform.position));

						Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed = Mathf.Lerp(OriginalSpeed, 0f, param);

					}

				}else if(accelerating){

					if(Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed >= OriginalSpeed) {
						if(SingleSpeedForAllNodes) {
							Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed = SingleSpeed;
						} else {
							Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed = OriginalSpeed;
						}
						break;
					} else {

						float param = Mathf.InverseLerp(0f, Vector3.Distance(Nodes.ToArray()[node_number].transform.position, Nodes.ToArray()[node_number-1].transform.position), Vector3.Distance(transform.position, Nodes.ToArray()[node_number-1].transform.position));

						Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed = Mathf.Lerp(2f,OriginalSpeed,param);

					}

				}

				yield return new WaitForFixedUpdate();

			}

			if(Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed != OriginalSpeed && stopping){
				Nodes.ToArray()[node_number].GetComponent<NodeScript>().Speed = OriginalSpeed;
			}


			point_number++;

			foreach(GameObject node in Nodes){

				if(point == node.transform.position || point == node.GetComponent<NodeScript>().CurveEndPoint){

					if (node_number + 1 < Nodes.Count) {
						node_number++;
					}

					if(node.GetComponent<NodeScript>().StopPoint){
						yield return new WaitForSeconds(node.GetComponent<NodeScript>().StopTime);
					}

					break;

				}

			}

			if(points.Last() == point && Repeat){

				StopCoroutine(Following());
				StartCoroutine(Following());

			}

			//Debug.Log(node_number);

		}

		if(!Application.isPlaying){

			RecalculatePath();
			goto restart;
			
		}

	}

	
}

三、NodeScript

通过这个脚本,可以对车辆的每一小段路线的驶入速度和使出速度进行过渡,并能够设置转弯点和停止点,使得汽车的行驶更加平滑。

using UnityEngine;
using System.Collections;
using System.Linq;
using System.Collections.Generic;

[ExecuteInEditMode]
public class NodeScript : MonoBehaviour {

	public bool CurvePoint = false;

	[Tooltip("Stop Points don't work in Edit mode")]
	public bool StopPoint = false;

	public bool SmoothStopping = false;

	public float StopTime = 1f;

	[Range(0.0f, 100.0f)]
	public float EntryDistance = 2f;

	[Range(0.0f, 100.0f)]
	public float ExitDistance = 2f;

	[HideInInspector]
	public float Speed = 15f;

	[HideInInspector]
	public GameObject BefNode;
	[HideInInspector]
	public GameObject AftNode;

	[HideInInspector]
	public Vector3 CurveStartPoint;
	[HideInInspector]
	public Vector3 CurveEndPoint;

	[HideInInspector]
	public GameObject Car;
	[HideInInspector]
	public NodeListClass ThisNodeClass;

	void OnDestroy(){

		if(Car != null) {
			Car.GetComponent<CarPath>().Nodes.Remove(gameObject);
			Car.GetComponent<CarPath>().NodesProperties.Remove(ThisNodeClass);
		}
		if (BefNode != null) {
			BefNode.GetComponent<NodeScript> ().AftNode = AftNode;
		}
		if (AftNode != null) {
			AftNode.GetComponent<NodeScript> ().BefNode = BefNode;
		}

	}

	void OnValidate(){

		ThisNodeClass.Node = gameObject;
		ThisNodeClass.Speed = Speed;

		//List<NodeListClass> to_replace = Car.GetComponent<CarPath>().NodesTest;

		/*foreach(NodeListClass node_lc in Car.GetComponent<CarPath>().NodesTest) {

			if(node_lc.Node ==  gameObject){

				node_lc = ThisNodeClass;
				break;

			}

		}*/

		//Car.GetComponent<CarPath>().NodesTest.Find

		if(CurvePoint){

			StopPoint = false;

		}else if(StopPoint){

			CurvePoint = false;

		}

		//Car.GetComponent<CarPath>().NodeThreshold = Speed/

	}

	public void AddNode(){

		Car.GetComponent<CarPath>().AddNode();

	}

	public void RemoveNode(){

		//Car.GetComponent<CarPath>().RemoveNode();
		Car.GetComponent<CarPath>().Nodes.Remove(gameObject);
		Car.GetComponent<CarPath>().NodesProperties.Remove(ThisNodeClass);
		BefNode.GetComponent<NodeScript>().AftNode = AftNode;
		AftNode.GetComponent<NodeScript>().BefNode = BefNode;
		DestroyImmediate(gameObject);
		

	}

	void OnDrawGizmos(){

		if(BefNode != null){
			Gizmos.DrawLine(transform.position, BefNode.transform.position);
		}

		if(!CurvePoint && !StopPoint){
			Gizmos.color = Color.red;
			Gizmos.DrawCube(transform.position, new Vector3(0.5f, 0.5f, 0.5f));

			if(BefNode == null && Car.GetComponent<CarPath>().Repeat){

				BefNode = Car.GetComponent<CarPath>().Nodes.Last();

			}

			if(AftNode == null && Car.GetComponent<CarPath>().Repeat){

				AftNode = Car.GetComponent<CarPath>().Nodes.First();

			}

		}else if(CurvePoint && !StopPoint){

			List<Vector3> CurvePoints = new List<Vector3>();

			float t = 0f;
			Vector3 position = Vector3.zero;
			for(int i = 0; i < 50; i++) 
			{
				t = i / (50 - 1.0f);
				position = (1.0f - t) * (1.0f - t) * CurveStartPoint + 2.0f * (1.0f - t) * t * transform.position + t * t * CurveEndPoint;

				CurvePoints.Add(position);
			}

			foreach(Vector3 pos in CurvePoints){

				Gizmos.DrawCube(pos,new Vector3(0.1f, 0.1f, 0.1f));

			}


			Gizmos.color = Color.blue;
			Gizmos.DrawCube(transform.position, new Vector3(0.5f, 0.5f, 0.5f));

			CurveStartPoint = Vector3.MoveTowards(transform.position, BefNode.transform.position, EntryDistance);
			CurveEndPoint = Vector3.MoveTowards(transform.position, AftNode.transform.position, ExitDistance);

			Gizmos.DrawCube(CurveStartPoint, new Vector3(0.15f, 0.15f, 0.15f));
			Gizmos.DrawLine(transform.position, CurveStartPoint);

			Gizmos.DrawCube(CurveEndPoint,new Vector3(0.15f, 0.15f, 0.15f));
			Gizmos.DrawLine(transform.position, CurveEndPoint);



			if(AftNode == null){

				Debug.LogError("There must be another node after the curve node");

			}

		}else if(!CurvePoint && StopPoint){

			Gizmos.color = Color.yellow;
			Gizmos.DrawCube(transform.position, new Vector3(0.5f, 0.5f, 0.5f));

		}

	}

	void Start () {
		ThisNodeClass.Node = gameObject;
		ThisNodeClass.Speed = Speed;

		if(Car.GetComponent<CarPath>().Nodes.Contains(gameObject) == false){

			if(AftNode != null) {

				if(AftNode.GetComponent<NodeScript>().BefNode != gameObject){

					AftNode.GetComponent<NodeScript>().BefNode = gameObject;

				}

			}

			if(BefNode != null) {

				if(BefNode.GetComponent<NodeScript>().AftNode != gameObject){

					BefNode.GetComponent<NodeScript>().AftNode = gameObject;

				}

			}

			Car.GetComponent<CarPath>().Nodes.Clear();// = Car.GetComponent<CarPath>().NodeParent.GetComponentsInChildren<GameObject>().ToList();

			foreach (Transform child in Car.GetComponent<CarPath>().NodeParent.transform)
			{
				Car.GetComponent<CarPath>().Nodes.Add(child.gameObject);
			}

		}

	}
	

	void Update () {
	
	}
}

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LiuFangdi145

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

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

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

打赏作者

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

抵扣说明:

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

余额充值