在游戏中使用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时都可能产生不同的效果,需要根据实际情况调整。