之前一直都是自己學習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。