以下代码中 using M = GameHelper.Math.Mathf; 为自定义类 可以替换为你们自己实现这些公式的类
1.三次Hermite插值余项:
(Unity中 动画曲线插值算法,d1 ,d2 分别为斜率)
文献:https://blog.csdn.net/luolei188/article/details/86563231
实现:
(注意避免出现 t1 == t2 ,否则会出现无穷大的问题)
///
/// 三次Hermite插值余项
///
/// 需求时间
/// 时间1
/// 时间2
/// 时间1上的值
/// 时间2上的值
/// 倒数1
/// 倒数2
///
public static float MathfHermite(float t, float t1, float t2, float v1, float v2, float d1, float d2)
{
float value1 = (t - t1) / (t2 - t1), value2 = (t - t2) / (t1 - t2);
float value3 = (t - t2) / (t1 - t2), value4 = (t - t1) / (t2 - t1);
float value5 = (t - t2) / (t1 - t2), value6 = (t - t1) / (t2 - t1);
float value = v1 * (1 + 2 * value1) * (value2 * value2)
+ v2 * (1 + 2 * value3) * (value4 * value4)
+ d1 * (t - t1) * (value5 * value5)
+ d2 * (t - t2) * (value6 * value6);
return value;
}
//如果是Unity动画插值计算,t:想要获取的时间,t1 关键帧1的时间,t2 关键帧2的时间, v1 关键帧1的值,v2关键帧2的值,d1关键帧1的出斜率,d2 关键帧2的入斜率
//可以通过修改 key1Out 和key2In 来控制控制柄角度 实现不同的曲线
//以下代码是在x ,z 平面内工作的,请忽略y轴
using UnityEngine;
using System.Collections;
using M = GameHelper.Math.Mathf;
public class Hermite : MonoBehaviour {
[Range(-180,180)]
public float key1Out = 0;
[Range(-180, 180)]
public float key2In = 0;
public Transform key1, key2, time,tValue;
private float t1, t2, v1, v2, t;
[Range(30,100)]
public int Step = 30;
void OnDrawGizmos()
{
Gizmos.DrawLine(Vector3.zero,Vector3.zero + new Vector3(1000,0,0));
Gizmos.DrawLine(Vector3.zero, Vector3.zero + new Vector3(0, 0, 1000));
if (key1 == null || key2 == null || time == null || tValue == null) return;
if (key1 != null) t1 = key1.position.x;
if (key1 != null) v1 = key1.position.z;
if (key2 != null) t2 = key2.position.x;
if (key2 != null) v2 = key2.position.z;
t = time.position.x;
//避免t1 == t2 ,否则可能会出现被除数为0的情况
if(t1==t2) t2+=0.00001f;
float value = M.MathfHermite(t,t1,t2,v1,v2, key1Out, key2In);
tValue.position = new Vector3(t,0,value);
float f1 = (t2 - t1) / Step;
Vector3 pre = new Vector3(t1,0,v1);
for (int i = 1; i <= Step; i++)
{
float x = t1 + i* f1;
float y = M.MathfHermite(x, t1, t2, v1, v2, key1Out, key2In);
Vector3 current = new Vector3(x,0,y);
Gizmos.DrawLine(pre,current);
pre = current;
}
}
}
2.贝塞尔曲线:
(游戏中常用的曲线绘制方法,例如(Handles.DrawBezier))
文献:https://blog.csdn.net/xiaozhangcsdn/article/details/98963937
一次贝塞尔曲线:
///
/// 一次贝塞尔曲线
///
/// 时间(0,1)
/// 起点
/// 终点
///
public static float MathfOneBezier(float t, float s, float e)
{
return (1 - t) * s + t * e;
}
///
/// 一次贝塞尔曲线
///
/// 比例值(0,1)
/// 起点
/// 终点
///
public static Vector3 MathfOneBezier(float t, Vector3 s, Vector3 e)
{
Vector3 tv = Vector3.zero;
tv.x = MathfOneBezier(t, s.x, e.x);
tv.y = MathfOneBezier(t, s.y, e.y);
tv.z = MathfOneBezier(t, s.z, e.z);
return tv;
}
二次贝塞尔曲线:
///
/// 二次贝塞尔曲线
///
/// 时间(0,1)
/// 起点
/// 终点
/// 控制点
///
public static float MathfTwoBezier(float t, float s, float e, float c)
{
float value1 = (1 - t);
return value1 * value1 * s + 2 * t * value1 * c + t * t * e;
}
///
/// 二次贝塞尔曲线
///
/// 时间(0,1)
/// 起点
/// 终点
/// 控制点
///
public static Vector3 MathfTwoBezier(float t, Vector3 s, Vector3 e, Vector3 c)
{
Vector3 tv = Vector3.zero;
tv.x = MathfTwoBezier(t, s.x, e.x, c.x);
tv.y = MathfTwoBezier(t, s.y, e.y, c.y);
tv.z = MathfTwoBezier(t, s.z, e.z, c.z);
return tv;
}
三次贝塞尔曲线:
///
/// 三次贝塞尔曲线
///
/// 时间(0,1)
/// 起点
/// 终点
/// 起点控制点
/// 终点控制点
///
public static float MathfThreeBezier(float t, float s, float e, float sc, float ec)
{
float value1 = 1 - t;
return s * value1 * value1 * value1 + 3 * sc * t * value1 * value1 + 3 * ec * t * t * value1 + e * t * t * t;
}
///
/// 三次贝塞尔曲线
///
/// 时间(0,1)
/// 起点
/// 终点
/// 起点控制点
/// 终点控制点
///
public static Vector3 MathfThreeBezier(float t, Vector3 s, Vector3 e, Vector3 sc, Vector3 ec)
{
Vector3 tv = Vector3.zero;
tv.x = MathfThreeBezier(t, s.x, e.x, sc.x, ec.x);
tv.y = MathfThreeBezier(t, s.y, e.y, sc.y, ec.y);
tv.z = MathfThreeBezier(t, s.z, e.z, sc.z, ec.z);
return tv;
}
附带 贝塞尔曲线测试代码:
(最终效果)
using UnityEngine;
using System.Collections;
using M = GameHelper.Math.Mathf;
public class Test : MonoBehaviour {
public enum BezierType
{
One,
Two,
Three
}
[Range(0,1)]
public float Range = 0;
public Transform S, E, SC, EC, Target;
public BezierType bt = BezierType.One;
private Vector3 SP, EP, SCP, ECP;
[Range(10,100)]
public float Step = 30;
void OnDrawGizmos()
{
if (S != null) SP = S.transform.position;
if (E != null) EP = E.transform.position;
if (SC != null) SCP = SC.transform.position;
if (EC != null) ECP = EC.transform.position;
if (bt == BezierType.One)
{
SetTargetPos(M.MathfOneBezier(Range, SP, EP));
Gizmos.DrawLine(SP, EP);
}
else if (bt == BezierType.Two)
{
SetTargetPos(M.MathfTwoBezier(Range, SP, EP, SCP));
DrawBizer( BezierType.Two);
}
else if (bt == BezierType.Three)
{
SetTargetPos(M.MathfThreeBezier(Range, SP, EP, SCP, ECP));
DrawBizer( BezierType.Three);
}
}
void DrawBizer(BezierType type)
{
Vector3 prePos = SP;
for (int i = 1; i <= Step; i++)
{
Vector3 current = type== BezierType.Two? M.MathfTwoBezier(i / Step, SP, EP, SCP):M.MathfThreeBezier(i/Step,SP,EP,SCP,ECP);
Gizmos.DrawLine(prePos, current);
prePos = current;
}
}
void SetTargetPos(Vector3 pos)
{
if (Target != null)
Target.position = pos;
}
}