Unity利用AnimationCurve做物體的各種運動

​之前一直都是自己學習Unity各種做Demo,最近開始正式使用Unity來做一個款2d的遊戲。

其中在做一個類似小球彈跳運動的時候遇到了點問題,查找了很多資料,無意間發現AnimationCurve,頓時那種心情啊!

然後苦心鑽研了一翻 拋磚引玉 的寫了個Move2D的類主要是個大家一個思路。

不多說上正菜:

直線平移運動效果:

 

曲線上升運動效果:

 

曲線上升然後下降的弧線運動效果:

 

小球彈跳運動效果:

 

 

下面是C#代碼,由於之前一直用Cocos2d-x所以有點cocos的風格:

複製代碼

using UnityEngine;
using System.Collections;


public class Move2D : MonoBehaviour
{
    public delegate void OverMoveEventHandler(GameObject gObj);
    public event OverMoveEventHandler OverMove;

    public delegate void RunTimeEventHandler(GameObject gObj,RunTimeEventArgs bte);
    public event RunTimeEventHandler RunTime;
    public class RunTimeEventArgs : System.EventArgs
    {
        public readonly float runTime;
        public readonly float totalTime;

        public RunTimeEventArgs (float rt,float tt)
        {
            runTime = rt;
            totalTime = tt;
        }
    }



    private float moveTime = 0;
    private Vector3 speed;
    private float timeDelta = 0;

    private AnimationCurve anmc = null;

    private bool isMoveRuning = false;
    public bool IsMoveRuning
    {
        get{return isMoveRuning;}
    }

    private Vector2 moveEndPosition;
    public Vector3 MoveEndPosition
    {
        get{return moveEndPosition;}
    }


    private static Move2D createMove(GameObject runObj)
    {
        Move2D move2d = runObj.GetComponent<Move2D>();

        if(move2d == null){
            move2d = runObj.AddComponent<Move2D>();
        } else {
            //可以在這地方做標誌達到動作序列,動作融合等等
        }
        
        return move2d;
    }


    /// <summary>
    /// 創建一個軌跡是直線的運動
    /// </summary>
    /// <returns>返回 Move2D 運動實例</returns>
    /// <param name="runObj">執行運動的物體</param>
    /// <param name="endPostion">運動的終點</param>
    /// <param name="moveTime">運動的時間</param>
    public static Move2D createMove_Line(GameObject runObj,Vector3 endPostion,float moveTime)
    {
        Move2D move2d = createMove(runObj);
        move2d.initMove_Line(endPostion,moveTime);
        
        return move2d;
    }

    private void initMove_Line (Vector3 endPostion,float moveTime)
    {
        //直線運動不需要曲線進行y方向位移
        anmc = null;

        
        moveEndPosition = endPostion;
        speed = endPostion / moveTime;
        timeDelta = 0;
        this.moveTime = moveTime;
        isMoveRuning = true;
    }


    /// <summary>
    /// 創建一個軌跡是曲線上升的運動
    /// </summary>
    /// <returns>返回 Move2D 運動實例</returns>
    /// <param name="runObj">執行運動的物體</param>
    /// <param name="endPostion">運動的終點</param>
    /// <param name="moveTime">運動的時間</param>
    /// <param name="maxHeight">曲線的最大高度,在這個運動中指的是物體最終停留的高度</param>
    /// <param name="curveCoefficient">曲線弧度係數.默認有一個係數計算方法,但是我只測試了高度爲1-10的情況.如果運動曲線不是你期望的那麼傳入你需要的參數</param>
    public static Move2D createMove_CurveUp(GameObject runObj,Vector3 endPostion,float moveTime,float maxHeight,float curveCoefficient = -999999f)
    {
        Move2D move2d = createMove(runObj);

        //測試範圍 1-10 都沒有問題,如果有問題的話自己傳入curveCoefficient
        if(curveCoefficient == -999999f){
            curveCoefficient = maxHeight * 0.5f;
        }

        move2d.initMove_CurveUp(endPostion,moveTime,maxHeight,curveCoefficient);

        return move2d;
    }

    private void initMove_CurveUp (Vector3 endPostion,float moveTime,float maxHeight,float curveCoefficient)
    {
        //以下注釋內容引用自 風宇衝的博客
        //http://blog.sina.com.cn/s/blog_471132920101f8nv.html
        //腳本創建AnimationCurve
        //AnimationCurve可以理解爲2部分 (1)鍵序列 (2)左右循環模式(又作左右包裹模式)
        //一:鍵序列
        //創建鍵序列:Keyframe[] ks = new Keyframe[3];
        //曲線中加入鍵序列:AnimationCurve curve = new AnimationCurve(ks);
        //獲取曲線中的鍵序列:curve[index]   或者 curve.keys[index]
        //添加單鍵:curve.Addkey(time,value)
        //刪除單鍵:curve.RemoveKey(index)
        //二:左右循環
        //anim.preWrapMode = WrapMode.Loop;
        //anim.postWrapMode = WrapMode.Once;
        //三:鍵
        //Keyframe kf = new Keyframe(time,value);
        //kf.inTangent = 45;
        //kf.outTangent = 45;


        Keyframe[] kfs = new Keyframe[2];

        kfs[0] = new Keyframe(0,0);
        kfs[0].outTangent = curveCoefficient;

        kfs[1] = new Keyframe(moveTime,maxHeight);

        anmc = new AnimationCurve(kfs);

        
        moveEndPosition = endPostion;
        speed = endPostion / moveTime;
        timeDelta = 0;
        this.moveTime = moveTime;
        isMoveRuning = true;
    }


    /// <summary>
    /// 創建一個軌跡是曲線上升到最大高度後下降的運動,當終點等於起點的時候就是原地跳躍
    /// </summary>
    /// <returns>返回 Move2D 運動實例</returns>
    /// <param name="runObj">執行運動的物體</param>
    /// <param name="endPostion">運動的終點</param>
    /// <param name="moveTime">運動的時間</param>
    /// <param name="maxHeight">曲線的最大高度,在這個運動中指的是物體運動軌跡的最大高度</param>
    /// <param name="curveCoefficient">曲線弧度係數.默認有一個係數計算方法,但是我只測試了高度爲1-10的情況.如果運動曲線不是你期望的那麼傳入你需要的參數</param>
    public static Move2D createMove_CurveUpDown(GameObject runObj,Vector3 endPostion,float moveTime,float maxHeight,float curveCoefficient = -999999f)
    {
        Move2D move2d = createMove(runObj);

        //測試範圍 1-10 都沒有問題,如果有問題的話自己傳入curveCoefficient
        if(curveCoefficient == -999999f){
            if(maxHeight >= 3)curveCoefficient = ((maxHeight-3f) * 0.5f) + 2.5f;
            else {
                curveCoefficient = maxHeight * 0.5f;
            }
        }

        move2d.initMove_CurveUpDown(endPostion,moveTime,maxHeight,curveCoefficient);
        
        return move2d;
    }
    
    private void initMove_CurveUpDown (Vector3 endPostion,float moveTime,float maxHeight,float curveCoefficient)
    {
        Keyframe[] kfs = new Keyframe[3];
        
        kfs[0] = new Keyframe(0,0);
        kfs[0].outTangent = curveCoefficient;

        kfs[1] = new Keyframe(moveTime/2,maxHeight);
        kfs[1].inTangent = 0f;
        kfs[1].outTangent = 0f;

        kfs[2] = new Keyframe(moveTime,0);
        kfs[2].inTangent = -curveCoefficient;
        
        anmc = new AnimationCurve(kfs);

        
        moveEndPosition = endPostion;
        speed = endPostion / moveTime;
        timeDelta = 0;
        this.moveTime = moveTime;
        isMoveRuning = true;
    }


    /// <summary>
    /// 創建一個軌跡是落地彈跳小球的運動
    /// </summary>
    /// <returns>返回 Move2D 運動實例</returns>
    /// <param name="runObj">執行運動的物體</param>
    /// <param name="endPostion">運動的終點</param>
    /// <param name="moveTime">運動的時間</param>
    /// <param name="maxHeight">曲線的最大高度,在這個運動中指的是物體運動軌跡的第一個波峯高度,第二個波峯高度是最大高度的1/3,第三個波峯最大高度是第一個的1/5</param>
    /// /// <param name="curveCoefficient">曲線弧度係數.默認有一個係數計算方法,但是我只測試了高度爲1-10的情況.如果運動曲線不是你期望的那麼傳入你需要的參數</param>
    public static Move2D createMove_Bounce3(GameObject runObj,Vector3 endPostion,float moveTime,float maxHeight,float curveCoefficient = -999999f)
    {
        Move2D move2d = createMove(runObj);


        //測試範圍 1-10 都沒有問題,如果有問題的話自己傳入curveCoefficient
        if(curveCoefficient == -999999f){
            curveCoefficient = maxHeight;
        }


        move2d.initMove_Bounce3(endPostion,moveTime,maxHeight,curveCoefficient);
        
        return move2d;
    }
    
    private void initMove_Bounce3 (Vector3 endPostion,float moveTime,float maxHeight,float curveCoefficient)
    {
        Keyframe[] kfs = new Keyframe[7];
        
        kfs[0] = new Keyframe(0,0);
        kfs[0].outTangent = curveCoefficient;

        kfs[1] = new Keyframe(moveTime*0.35f,maxHeight);
        kfs[1].inTangent = 0;
        kfs[1].outTangent = 0;
        
        kfs[2] = new Keyframe(moveTime*0.7f,0);
        kfs[2].inTangent = -curveCoefficient;
        kfs[2].outTangent = curveCoefficient;

        kfs[3] = new Keyframe(moveTime*0.8f,maxHeight/3);
        kfs[3].inTangent = 0;
        kfs[4].outTangent = 0;

        kfs[4] = new Keyframe(moveTime*0.9f,0);
        kfs[4].inTangent = -curveCoefficient;
        kfs[4].outTangent = curveCoefficient;

        kfs[5] = new Keyframe(moveTime*0.95f,maxHeight/6);
        kfs[5].inTangent = 0;
        kfs[5].outTangent = 0;
        
        kfs[6] = new Keyframe(moveTime,0);
        kfs[6].inTangent = -curveCoefficient;
        
        anmc = new AnimationCurve(kfs);

        
        moveEndPosition = endPostion;
        speed = endPostion / moveTime;
        timeDelta = 0;
        this.moveTime = moveTime;
        isMoveRuning = true;
    }



    void Update () {
        if(timeDelta <= moveTime){
            Vector3 ep = speed * timeDelta;
            
            if(anmc != null){
                ep.y += anmc.Evaluate(timeDelta);
            }
            
            transform.localPosition = ep;

            if(RunTime != null)RunTime(gameObject,new RunTimeEventArgs(timeDelta,moveTime));

            timeDelta += (Time.deltaTime);
        } else {
            if(RunTime != null)RunTime(gameObject,new RunTimeEventArgs(timeDelta,moveTime));
            if(OverMove != null)OverMove(gameObject);

            StopMove2D();
        }
    }


    public void StopMove2D()
    {
        Destroy(this);
        isMoveRuning = false;
    }

}

複製代碼

 

使用的時候只需要create你需要的運動就好:

OverMove事件在運動結束的時候發生;

RunTime事件在運動的每一幀發生,runTime是運動進行了多長時間,totalTime是傳進去的那個運動總時間;

兩個事件都會把gameObject傳過去,可以利用它做一些處理;

複製代碼

void Start () {

//        Move2D.createMove_Line(gameObject,new Vector3(10,5,0),5f);
//        Move2D.createMove_CurveUp(gameObject,new Vector3(10,2,0),5f,2f);
//        Move2D.createMove_CurveUpDown(gameObject,new Vector3(10,3,0),5f,3f);
        Move2D mv2d = Move2D.createMove_Bounce3(gameObject,new Vector3(20,1,0),5f,4f);
        mv2d.OverMove += new Move2D.OverMoveEventHandler(overMove);
//        mv2d.RunTime += new Move2D.RunTimeEventHandler(moveTime);
    }


    private void overMove(GameObject gObj)
    {
        Debug.Log("結束移動");
    }

    private void moveTime(GameObject gObj,Move2D.RunTimeEventArgs rte)
    {
        Debug.Log("還有"+(rte.totalTime-rte.runTime)+"秒結束");
    }

複製代碼

沒有什麼難點主要是對AnimationCurve的運用

我只是 拋磚引玉 具體的運動效果是要你項目需求來做的。

博客園不知道怎麼上傳附件,如果需要看Demo的話去這裏​的附件下載把,如果有後面有更新我也會放在這裏的。

最後感謝風宇衝的博客提供的幫助,如果有具體不知道怎麼操作的小夥伴可以去看這個博客,裏面詳細的介紹了怎麼用AnimationCurve。

歡迎轉載收藏,轉載著名出處!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章