遊戲中的數學公式(增錄中...)

以下代碼中 using M = GameHelper.Math.Mathf; 爲自定義類 可以替換爲你們自己實現這些公式的類
1.三次Hermite插值餘項:
(Unity中 動畫曲線插值算法,d1 ,d2 分別爲斜率)
文獻:https://blog.csdn.net/luolei188/article/details/86563231
實現:
(注意避免出現 t1 == t2 ,否則會出現無窮大的問題)

/// <summary>
/// 三次Hermite插值餘項 
/// </summary>
/// <param name="t">需求時間</param>
/// <param name="t1">時間1</param>
/// <param name="t2">時間2</param>
/// <param name="v1">時間1上的值</param>
/// <param name="v2">時間2上的值</param>
/// <param name="d1">倒數1</param>
/// <param name="d2">倒數2</param>
/// <returns></returns>
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
一次貝塞爾曲線:

/// <summary>
/// 一次貝塞爾曲線
/// </summary>
/// <param name="t">時間(0,1)</param>
/// <param name="s">起點</param>
/// <param name="e">終點</param>
/// <returns></returns>
public static float MathfOneBezier(float t, float s, float e)
{
    	return (1 - t) * s + t * e;
}

/// <summary>
/// 一次貝塞爾曲線
/// </summary>
/// <param name="t">比例值(0,1)</param>
/// <param name="s">起點</param>
/// <param name="e">終點</param>
/// <returns></returns>
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;
}

二次貝塞爾曲線:

/// <summary>
/// 二次貝塞爾曲線
/// </summary>
/// <param name="t">時間(0,1)</param>
/// <param name="s">起點</param>
/// <param name="e">終點</param>
/// <param name="c">控制點</param>
/// <returns></returns>
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;
}
/// <summary>
/// 二次貝塞爾曲線
/// </summary>
/// <param name="t">時間(0,1)</param>
/// <param name="s">起點</param>
/// <param name="e">終點</param>
/// <param name="c">控制點</param>
/// <returns></returns>
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;
}

三次貝塞爾曲線:

/// <summary>
/// 三次貝塞爾曲線
/// </summary>
/// <param name="t">時間(0,1)</param>
/// <param name="s">起點</param>
/// <param name="e">終點</param>
/// <param name="sc">起點控制點</param>
/// <param name="ec">終點控制點</param>
/// <returns></returns>
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;
}

/// <summary>
/// 三次貝塞爾曲線
/// </summary>
/// <param name="t">時間(0,1)</param>
/// <param name="s">起點</param>
/// <param name="e">終點</param>
/// <param name="sc">起點控制點</param>
/// <param name="ec">終點控制點</param>
/// <returns></returns>
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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章