Unity ベジエ曲線の作成
効果
ベジエ曲線の原理
一般的に使用されるベジエ曲線は、2 次元グラフィックス アプリケーション用の数学的曲線です。曲線の定義: 始点、終点、制御点。コントロールポイントを調整することで、ベジエ曲線の形状が変化します。
一次直線
下の画像にCCを追加する方法C点は設定時間内、AA点A がBBに移動ポイントB ? ここで時刻t , t ∈ ( 0 , 1 ) t ,t \in(0,1) をt 、tε( 0 ,1 )、
設定するOA ⃗ \vec{OA}in time tおあ_同様にOE ⃗ \vec{OE}に短縮されます大江、OB ⃗ \vec{OB}OBの単位ベクトルはOF ⃗ \vec{OF}に拡張されますのOE ⃗ = OA ⃗ ( 1 − t
) \vec{OE}=\vec{OA}(1-t)大江=おあ_( 1−t )
OF ⃗ = OB ⃗ × t \vec{OF}=\vec{OB}\times tの=OB×t
OC ⃗ = OA ⃗ ( 1 − t ) + OB ⃗ × t \vec{OC}=\vec{OA}(1-t)+\vec{OB}\times tOC=おあ_( 1−t )+OB×t
これでCCを取得できますC点は設定時間内、AA点A がBBに移動点Bの補間。
Vector3 AB = (1 - t) * OA + t * OB;
二次曲線
2 次は、AB ⃗ \vec{AB}を個別に処理する 1 次アルゴリズムに基づいています。A B和BC ⃗ \vec{BC}紀元前
Vector3 AB = (1 - t) * OA + t * OB;
Vector3 BC = (1 - t) * OB + t * OC;
次にAB ⃗ \vec{AB}A B和BC ⃗ \vec{BC}紀元前一次アルゴリズム処理も行う
Vector3 ABC = (1 - t) * AB + t * BC;
三次曲線
3 次は 2 次と同様で、蓄積し続けます。
Vector3 AB = (1 - t) * OA + t * OB;
Vector3 BC = (1 - t) * OB + t * OC;
Vector3 CD = (1 - t) * OC + t * OD;
Vector3 ABC = (1 - t) * AB + t * BC;
Vector3 BCD = (1 - t) * BC + t * CD;
result = (1 - t) * ABC + t * BCD;
次の 4 つの高レベルなど。三次は日常生活でよく使われます。つまり、4 点で曲線が決まります。
団結して適用
目的は、モーション パス データを確立するために、ベジエ曲線を介して曲線上のポイントを取得することです。
インターフェイスの編集拡張ツールを使用してベジエ ツールを作成します。
#if UNITY_EDITOR
public class BesselPathCreat : ScriptableWizard
{
public string Name = "Path";
// 点的半径
public float radius = 1;
// 曲线取点的密度
public int densityCurve = 1;
/// <summary>
/// 绘制曲线控制点 -- 此脚本的子物体
/// </summary>
public List<GameObject> PathPointList = new List<GameObject>();
DrawGizmosLine drawGizmosLint;
GameObject game;
[MenuItem("FrameWorkSong//FrameWork/3.创建贝塞尔路径", false, 3)]
static void CreateBasselPath()
{
ScriptableWizard.DisplayWizard<BesselPathCreat>("CreateBasselPath", "创建", "加点");
}
/// <summary>
/// 创建按钮触发
/// </summary>
void OnWizardCreate()
{
if (PathPointList.Count > 4)
{
var level = ScriptableObject.CreateInstance<BasselPathTemplet>();
level.BasselPathPoints = drawGizmosLint.CurvePoints;
AssetDatabase.CreateAsset(level, @"Assets/FrameWorkSong/Data/" + Name + ".asset");//在传入的路径中创建资源
AssetDatabase.SaveAssets(); //存储资源
AssetDatabase.Refresh(); //刷新
}
DestroyObject();
}
/// <summary>
/// 关闭触发
/// </summary>
void DestroyObject()
{
if (game)
{
DestroyImmediate(game);
}
for (int i = 0; i < PathPointList.Count; i++)
{
DestroyImmediate(PathPointList[i]);
}
}
void OnWizardUpdate()
{
}
/// <summary>
/// 加点按钮触发
/// </summary>
// When the user presses the "Apply" button OnWizardOtherButton is called.
void OnWizardOtherButton()
{
if (PathPointList.Count == 0)
{
game = new GameObject();
drawGizmosLint = game.AddComponent<DrawGizmosLine>();
}
BasselPath basselPath = new BasselPath();
DrawGizmosPointLine drawGizmos = basselPath.MianPiont.AddComponent<DrawGizmosPointLine>();
PathPointList.Add(basselPath.FrontPiont);
PathPointList.Add(basselPath.MianPiont);
PathPointList.Add(basselPath.BackePiont);
drawGizmos.SelfPoint = basselPath.Piont;
drawGizmosLint.SelfPoint = PathPointList;
drawGizmosLint.densityCurve = densityCurve;
drawGizmosLint.radius = radius;
}
void OnDestroy()
{
DestroyObject();
}
}
#endif
ここで使用されるクラスは、エディター ウィザードを作成するために派生されます。詳細については、公式 API https://docs.unity3d.com/cn/current/ScriptReference/ScriptableWizard.html を参照してください。
ベジエの作り方
public class BasselPath
{
List<GameObject> piont = new List<GameObject>();
GameObject mianPiont;
GameObject frontPiont;
GameObject backePiont;
public BasselPath()
{
mianPiont = new GameObject();
mianPiont.transform.position = Vector3.zero;
frontPiont = new GameObject();
frontPiont.transform.position = Vector3.zero + (Vector3.right * 5);
backePiont = new GameObject();
backePiont.transform.position = Vector3.zero + (Vector3.left * 5);
piont.Add(frontPiont);
piont.Add(mianPiont);
piont.Add(backePiont);
backePiont.transform.SetParent(mianPiont.transform);
frontPiont.transform.SetParent(mianPiont.transform);
mianPiont.AddComponent<DrawGizmosPoint>();
frontPiont.AddComponent<DrawGizmosPoint>();
backePiont.AddComponent<DrawGizmosPoint>();
}
public GameObject MianPiont { get => mianPiont; set => mianPiont = value; }
public GameObject FrontPiont { get => frontPiont; }
public GameObject BackePiont { get => backePiont; }
public List<GameObject> Piont { get => piont; }
}
/// <summary>
/// 绘制节点
/// </summary>
public class DrawGizmosPoint : MonoBehaviour
{
public float radius = 1;
private void OnDrawGizmos()
{
//绘制点
Gizmos.color = Color.blue;
Gizmos.DrawSphere(transform.position, radius * 0.5f);
}
}
/// <summary>
/// 绘制节点线
/// </summary>
public class DrawGizmosPointLine : MonoBehaviour
{
public float radius = 1;
public List<GameObject> SelfPoint = new List<GameObject>();
private void OnDrawGizmos()
{
List<Vector3> controlPointPos = SelfPoint.Select(point => point.transform.position).ToList();
//绘制曲线控制点连线
Gizmos.color = Color.red;
for (int i = 0; i < controlPointPos.Count - 1; i += 3)
{
Gizmos.DrawLine(controlPointPos[i], controlPointPos[i + 1]);
Gizmos.DrawLine(controlPointPos[i + 1], controlPointPos[i + 2]);
}
}
}
/// <summary>
/// 绘制曲线
/// </summary>
public class DrawGizmosLine : MonoBehaviour
{
public float radius;
public int densityCurve;
public List<GameObject> SelfPoint;
public List<Vector3> CurvePoints;
private void OnDrawGizmos()
{
List<Vector3> controlPointPos = SelfPoint.Select(point => point.transform.position).ToList();
if (controlPointPos != null)
{
CurvePoints = GetDrawingPoints(controlPointPos, densityCurve);
}
//绘制曲线
if (CurvePoints.Count >= 4)
{
Gizmos.color = Color.green;
//点密度
foreach (var item in CurvePoints)
{
Gizmos.DrawSphere(item, radius * 0.5f);
}
//曲线
for (int i = 0; i < CurvePoints.Count - 1; i++)
{
Gizmos.DrawLine(CurvePoints[i], CurvePoints[i + 1]);
}
}
}
/// <summary>
/// 获取绘制点
/// </summary>
/// <param name="controlPoints"></param>
/// <param name="segmentsPerCurve"></param>
/// <returns></returns>
public List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
{
List<Vector3> points = new List<Vector3>();
// 下一段的起始点和上段终点是一个,所以是 i+=3
for (int i = 1; i < controlPoints.Count - 4; i += 3)
{
var p0 = controlPoints[i];
var p1 = controlPoints[i + 1];
var p2 = controlPoints[i + 2];
var p3 = controlPoints[i + 3];
float dis = Vector3.Distance(p0, p3);
int count = Mathf.CeilToInt(segmentsPerCurve * dis);
if (count < segmentsPerCurve)
{
count = segmentsPerCurve;
}
for (int j = 0; j <= count; j++)
{
var t = j / (float)count;
points.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
}
}
return points;
}
// 三阶公式
Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
Vector3 result;
Vector3 p0p1 = (1 - t) * p0 + t * p1;
Vector3 p1p2 = (1 - t) * p1 + t * p2;
Vector3 p2p3 = (1 - t) * p2 + t * p3;
Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;
result = (1 - t) * p0p1p2 + t * p1p2p3;
return result;
}
}