Unity3d 繪製並獲取通過任意點的曲線

最終幻想發表於2014-10-17

為了平滑遊戲物件在 Unity 中的行進路線,我們經常會在 Unity 中使用 ITweenPath 外掛,但有時候我們或許只會使用到通過 ITweenPath 繪製出來的點(比如把這些點放到配置檔案中),並不希望載入 ITweenPath 外掛或者通過自己的函式去實現遊戲物件的移動,通過檢視 ITweenPath 的程式碼,很容易就把 ITweenPath 繪製曲線點的方法給提取出來了,主函式並不多,只有三個方法,希望能給你帶來幫助!

先來看看最終的 Demo 實現方式:

Demo 程式碼:

using UnityEngine;
using System.Collections.Generic;

public class PointPath : MonoBehaviour 
{
	private Vector3[] pathList;

	public GameObject sphere;
	public GameObject path;

	void Awake()
	{
		Transform[] transformList = path.GetComponentsInChildren<Transform> ();
		int length = transformList.Length;

		pathList = new Vector3[length];
		for (int index = 0; index < length; index ++) 
		{
			pathList[index] = transformList[index].transform.position;
		}

		Vector3[] resultList = PointController.PointList (this.pathList, 20);

		foreach (Vector3 point in resultList) 
		{
			GameObject gameObject = (GameObject)Instantiate(sphere);
			gameObject.transform.localPosition = point;
			gameObject.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
		}
	}
}

PointController.cs

using UnityEngine;
using System;
using System.Collections;

public class PointController
{
	/// <summary>
	/// 獲取曲線上面的所有點
	/// </summary>
	/// <returns>The list.</returns>
	/// <param name="path">需要穿過的點列表</param>
	/// <param name="pointSize">兩個點之間的節點數量</param>
	public static Vector3[] PointList(Vector3[] path, int pointSize)
	{
		Vector3[] controlPointList = PathControlPointGenerator(path);

		int smoothAmount = path.Length * pointSize;
		Vector3[] pointList = new Vector3[smoothAmount];

		for (int index = 1; index <= smoothAmount; index++) 
		{
			Vector3 currPt = Interp(controlPointList, (float) index / smoothAmount);
			pointList[index - 1] = currPt;
		}
		return pointList;
	}	

	/// <summary>
	/// 獲取控制點
	/// </summary>
	/// <returns>The control point generator.</returns>
	/// <param name="path">Path.</param>
	private static Vector3[] PathControlPointGenerator(Vector3[] path)
	{
		int offset = 2;
		Vector3[] suppliedPath = path;
		Vector3[] controlPoint = new Vector3[suppliedPath.Length + offset];
		Array.Copy(suppliedPath, 0, controlPoint, 1, suppliedPath.Length);

		controlPoint[0] = controlPoint[1] + (controlPoint[1] - controlPoint[2]);
		controlPoint[controlPoint.Length - 1] = controlPoint[controlPoint.Length - 2] + (controlPoint[controlPoint.Length - 2] - controlPoint[controlPoint.Length - 3]);

		if(controlPoint[1] == controlPoint[controlPoint.Length - 2])
		{
			Vector3[] tmpLoopSpline = new Vector3[controlPoint.Length];
			Array.Copy(controlPoint, tmpLoopSpline, controlPoint.Length);
			tmpLoopSpline[0] = tmpLoopSpline[tmpLoopSpline.Length - 3];
			tmpLoopSpline[tmpLoopSpline.Length - 1] = tmpLoopSpline[2];
			controlPoint = new Vector3[tmpLoopSpline.Length];
			Array.Copy(tmpLoopSpline, controlPoint, tmpLoopSpline.Length);
		}	

		return(controlPoint);
	}

	/// <summary>
	/// 根據 T 獲取曲線上面的點位置
	/// </summary>
	/// <param name="pts">Pts.</param>
	/// <param name="t">T.</param>
	private static Vector3 Interp(Vector3[] pts, float t)
	{
		int numSections = pts.Length - 3;
		int currPt = Mathf.Min(Mathf.FloorToInt(t * (float) numSections), numSections - 1);
		float u = t * (float) numSections - (float) currPt;

		Vector3 a = pts[currPt];
		Vector3 b = pts[currPt + 1];
		Vector3 c = pts[currPt + 2];
		Vector3 d = pts[currPt + 3];

		return .5f * (
			(-a + 3f * b - 3f * c + d) * (u * u * u)
			+ (2f * a - 5f * b + 4f * c - d) * (u * u)
			+ (-a + c) * u
			+ 2f * b
			);
	}	
}

相關文章