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。