在遊戲中使用Tween+貝塞爾曲線實作一個抛物線的效果。
1、線性貝塞爾曲線公式:
給定點P0、P1,線性貝茲曲線隻是一條兩點之間的直線。這條線由下式給出:
2、二次貝塞爾曲線公式:
二次方貝茲曲線的路徑由給定點P0、P1、P2控制,這條線由下式給出:
3、三次貝塞爾曲線公式:
P0、P1、P2、P3四個點在平面或在三維空間中定義了三次方貝茲曲線。曲線起始于P0走向P1,并從P2的方向來到P3。一般不會經過P1或P2;這兩個點隻是用來充當控制點。P0和P1之間的間距,決定了曲線在轉而趨進P3之前,走向P2方向的“長度有多長”。
曲線的參數形式為:
這裡使用二次貝塞爾曲線實作抛物線運動:
代碼如下
using DG.Tweening;
using System;
using System.Collections;
using UnityEngine;
public class CurvedMoveEffect : MonoBehaviour
{
private Action callback = null;
[SerializeField]
private GameObject startPos, endPos;
private void Start()
{
MoveTo(startPos.transform.position, endPos.transform.position, 0.6f, 1, null);
}
private void OnComplete()
{
if (callback != null)
{
callback.Invoke();
}
Destroy(gameObject);
}
/// <param name="centerOffset">中心點偏移量,0.5為基準,調整幅度0.01,根據實際調整</param>
public void MoveTo(Vector3 startPos, Vector3 endPos, float centerOffset, float time = 0.5f, Action callback = null)
{
this.callback = callback;
gameObject.transform.position = startPos;
gameObject.SetActive(true);
StartCoroutine(Move(startPos, endPos, centerOffset, time, OnComplete));
}
//如果有拖尾效果,延遲一定時間再顯示,否則尾巴會有殘影
IEnumerator Move(Vector3 startPos, Vector3 endPos, float centerOffset, float time = 0.5f, Action onComplete = null)
{
TrailRenderer trail = gameObject.GetComponentInChildren<TrailRenderer>(true);
yield return new WaitForSeconds(trail ? trail.time : 0);
//如果路徑點較少,可以使用CatmullRom曲線插值類型運動,預設PathType.Linear
//同時可以設定easetype來使用不同的運動方式,其它easetype:https://blog.csdn.net/w0100746363/article/details/83587485
//如果想讓物體一直朝着路徑方向可以加上SetLookAt(0):DOPath(path, time).SetLookAt(0);
Tween tween = gameObject.transform.DOPath(Path(startPos, endPos, centerOffset, 6), time, PathType.CatmullRom).SetEase(Ease.OutSine);
if (onComplete != null)
{
tween.OnComplete(new DG.Tweening.Core.TweenCallback(onComplete));
}
if (trail)
trail.enabled = true;
}
private Vector3[] Path(Vector3 startTrans, Vector3 endTrans, float centerCtl, int segmentNum)
{
var startPoint = startTrans;
var endPoint = endTrans;
//中間點:起點和終點的向量相加再乘一個系數,系數可以根據實際調整
var bezierControlPoint = (startPoint + endPoint) * centerCtl;
//segmentNum為int類型,路徑點數量,值越大,路徑點越多,曲線越平滑
Vector3[] path = new Vector3[segmentNum];
for (int i = 0; i < segmentNum; i++)
{
var time = (i + 1) / (float)segmentNum;//歸化到0~1範圍
path[i] = GetBezierPoint(time, startPoint, bezierControlPoint, endPoint);//使用貝塞爾曲線的公式取得t時的路徑點
}
return path;
}
/// <param name="t">0到1的值,0擷取曲線的起點,1獲得曲線的終點</param>
/// <param name="start">曲線的起始位置</param>
/// <param name="center">決定曲線形狀的控制點</param>
/// <param name="end">曲線的終點</param>
private Vector3 GetBezierPoint(float t, Vector3 start, Vector3 center, Vector3 end)
{
return (1 - t) * (1 - t) * start + 2 * t * (1 - t) * center + t * t * end;
}
}
另外,相同的中間點偏移系數在場景中Canvas的不同Render Mode和MainCamera的不同Projection時都可能産生不同的效果,需要根據實際情況調整。