NGUI所见即所得之UITweener

来源:互联网 发布:淘宝ka商家是什么意思 编辑:程序博客网 时间:2024/05/21 06:47
一直没有用过NGUI动画的功能,之前的理解就是:设置始末两个“位置”,然后就是从起始位置移到结束位置。至于中间是怎么变化的,就感觉很神奇了,变化率怎么设置才不会看起来很“傻”,这里不是看“郭靖”,动画一定要有惊奇,摸不着猜不透的感觉。对NGUI主要的几个脚本都已经有点掌握了(猛点查看),一直都没有去”膜拜“Tweening文件夹的各个大神,可能以前会觉得不就是一个动画组件,自己都可以实现。但是看过里面的代码就后悔了,因为至少结合TweenFOV和TweenOrhoSize这两个脚本就可以实现很多效果,竟然轻而易举的集成了,看来人还是不要太看得起自己的好,这样才会走的更快更远。

        每次都觉得前面吹水很写,也写不好(一直都有感觉自己的写作水平太差了),那就来看下Tweening文件夹下到底卖的是什么药——UITweener和它的“孩子”:

UITweener的Fields
        看着很复杂,其实只要把UITweener琢磨透了,其它都只是重写UITweener的OnUpdate方法和封装了Begin方法。还是先看下主要的Field(作用看注释):

C#代码 复制代码 收藏代码
  1.        bool mStarted = false//是否开始动画 
  2. float mStartTime = 0f;   //动画开始播放的时间, mStarted =true;mStartTime = time + delay; 
  3. float mDuration = 0f;    //动画长度(时间) 
  4. float mAmountPerDelta = 1000f;   //单位时间动画播放的长度,有点帧率的感觉 
  5. float mFactor = 0f;           //当前动画播放的进度 
  6.  
  7. /// <summary> 
  8. /// Amount advanced per delta time. 
  9. /// </summary> 
  10.  
  11. public float amountPerDelta 
  12.     get 
  13.     { 
  14.         if (mDuration != duration) 
  15.         { 
  16.             mDuration = duration; 
  17.             mAmountPerDelta = Mathf.Abs((duration > 0f) ? 1f / duration : 1000f); 
  18.         } 
  19.         return mAmountPerDelta; 
  20.     } 

        通过Begin设置需要的参数:

C#代码 复制代码 收藏代码
  1.        static public T Begin<T> (GameObject go,float duration) where T : UITweener 
  2.     T comp = go.GetComponent<T>(); 
  3. if UNITY_FLASH 
  4.     if ((object)comp ==null) comp = (T)go.AddComponent<T>(); 
  5. else 
  6.     if (comp == null) comp = go.AddComponent<T>(); 
  7. endif 
  8.     comp.mStarted = false
  9.     comp.duration = duration; 
  10.     comp.mFactor = 0f; 
  11.     comp.mAmountPerDelta = Mathf.Abs(comp.mAmountPerDelta); 
  12.     comp.style = Style.Once; 
  13.     comp.animationCurve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f),new Keyframe(1f, 1f, 1f, 0f)); 
  14.     comp.eventReceiver = null
  15.     comp.callWhenFinished = null
  16.     comp.enabled = true
  17.     return comp; 

Update函数    

然后再Update函数先计算出时间delta,进一步计算出当前动画播放的mFactor,然后进行Sample采用,执行OnUpdate:

C#代码 复制代码 收藏代码
  1. void Update () 
  2.     float delta = ignoreTimeScale ? RealTime.deltaTime : Time.deltaTime; 
  3.     float time = ignoreTimeScale ? RealTime.time : Time.time; 
  4.  
  5.     if (!mStarted) 
  6.     { 
  7.         mStarted = true
  8.         mStartTime = time + delay; 
  9.     } 
  10.  
  11.     if (time < mStartTime) return
  12.  
  13.     // Advance the sampling factor 
  14.     mFactor += amountPerDelta * delta; 
  15.  
  16.     // Loop style simply resets the play factor after it exceeds 1. 
  17.     if (style == Style.Loop) 
  18.     { 
  19.         if (mFactor > 1f) 
  20.         { 
  21.             mFactor -= Mathf.Floor(mFactor); 
  22.         } 
  23.     } 
  24.     else if (style == Style.PingPong) 
  25.     { 
  26.         // Ping-pong style reverses the direction 
  27.         if (mFactor > 1f) 
  28.         { 
  29.             mFactor = 1f - (mFactor - Mathf.Floor(mFactor)); 
  30.             mAmountPerDelta = -mAmountPerDelta; 
  31.         } 
  32.         else if (mFactor < 0f) 
  33.         { 
  34.             mFactor = -mFactor; 
  35.             mFactor -= Mathf.Floor(mFactor); 
  36.             mAmountPerDelta = -mAmountPerDelta; 
  37.         } 
  38.     } 
  39.  
  40.     // If the factor goes out of range and this is a one-time tweening operation, disable the script 
  41.     if ((style == Style.Once) && (mFactor > 1f || mFactor < 0f)) 
  42.     { 
  43.         mFactor = Mathf.Clamp01(mFactor); 
  44.         Sample(mFactor, true); 
  45.  
  46.         current = this
  47.  
  48.         // Notify the listener delegates 
  49.         EventDelegate.Execute(onFinished); 
  50.  
  51.         // Deprecated legacy functionality support 
  52.         if (eventReceiver != null && !string.IsNullOrEmpty(callWhenFinished)) 
  53.             eventReceiver.SendMessage(callWhenFinished, this, SendMessageOptions.DontRequireReceiver); 
  54.  
  55.         current = null
  56.  
  57.         // Disable this script unless the function calls above changed something 
  58.         if (mFactor == 1f && mAmountPerDelta > 0f || mFactor == 0f && mAmountPerDelta < 0f) 
  59.             enabled = false
  60.     } 
  61.     else Sample(mFactor, false); 

Sample采样函数

        前面说的动画要有摸不着猜不透的感觉,就是要考Sample的采样函数来实现的,UITweener支持5种动画曲线:

C#代码 复制代码 收藏代码
  1.        public enum Method 
  2.     Linear, 
  3.     EaseIn, 
  4.     EaseOut, 
  5.     EaseInOut, 
  6.     BounceIn, 
  7.     BounceOut, 

采样的函数,原理很简单:根据当前播放的进度mFactor,计算出实际的动画播放刻度,然后执行OnUpdate操作:

C#代码 复制代码 收藏代码
  1. public void Sample (float factor,bool isFinished) 
  2.     // Calculate the sampling value 
  3.     float val = Mathf.Clamp01(factor); 
  4.  
  5.     if (method == Method.EaseIn) 
  6.     { 
  7.         val = 1f - Mathf.Sin(0.5f * Mathf.PI * (1f - val)); 
  8.         if (steeperCurves) val *= val; 
  9.     } 
  10.     else if (method == Method.EaseOut) 
  11.     { 
  12.         val = Mathf.Sin(0.5f * Mathf.PI * val); 
  13.  
  14.         if (steeperCurves) 
  15.         { 
  16.             val = 1f - val; 
  17.             val = 1f - val * val; 
  18.         } 
  19.     } 
  20.     else if (method == Method.EaseInOut) 
  21.     { 
  22.         const float pi2 = Mathf.PI * 2f; 
  23.         val = val - Mathf.Sin(val * pi2) / pi2; 
  24.  
  25.         if (steeperCurves) 
  26.         { 
  27.             val = val * 2f - 1f; 
  28.             float sign = Mathf.Sign(val); 
  29.             val = 1f - Mathf.Abs(val); 
  30.             val = 1f - val * val; 
  31.             val = sign * val * 0.5f + 0.5f; 
  32.         } 
  33.     } 
  34.     else if (method == Method.BounceIn) 
  35.     { 
  36.         val = BounceLogic(val); 
  37.     } 
  38.     else if (method == Method.BounceOut) 
  39.     { 
  40.         val = 1f - BounceLogic(1f - val); 
  41.     } 
  42.  
  43.     // Call the virtual update 
  44.     OnUpdate((animationCurve != null) ? animationCurve.Evaluate(val) : val, isFinished); 

缓动函数(easing fuction)

        上面说的动画曲线,中文叫缓动函数(曲线):

        通过上面这张图可以很感性的认识不同函数的具体的效果,也可以自己尝试推导一边加深理解,不过D.S.Qiu已经有点“廉颇老矣”,凭着记忆“奇变偶不变,符号看象限”,慢的只能到easeInSine,要想详细了解可以参考②和③。

妙用mAmountPerDelta

         mAmountPerDelta就是动画播放速度,只对mAmountPerData就可以有更多控制:Toggle,PlayForward,PlayResverse:

C#代码 复制代码 收藏代码
  1. /// <summary> 
  2. /// Manually activate the tweening process, reversing it if necessary. 
  3. /// </summary> 
  4.  
  5. public void Play (bool forward) 
  6.     mAmountPerDelta = Mathf.Abs(amountPerDelta); 
  7.     if (!forward) mAmountPerDelta = -mAmountPerDelta; 
  8.     enabled = true
  9.     Update(); 
  10. /// <summary> 
  11. /// Manually start the tweening process, reversing its direction. 
  12. /// </summary> 
  13.  
  14. public void Toggle () 
  15.     if (mFactor > 0f) 
  16.     { 
  17.         mAmountPerDelta = -amountPerDelta; 
  18.     } 
  19.     else 
  20.     { 
  21.         mAmountPerDelta = Mathf.Abs(amountPerDelta); 
  22.     } 
  23.     enabled = true

『Bug修复和吐槽

        之前用TweenRotation这个脚本,做游戏等待转圈等待界面,发现总是不能旋转360度,总是一个小于180的角度,无论from和to如何设置:

C#代码 复制代码 收藏代码
  1. public Vector3 from; 
  2. public Vector3 to; 

后来无奈之下,只好去看下TweenRotation的OnUpdate函数,发现使用的是 Quaternion.Slerp这个函数,发现确实是这样,所以就做了下面的修改:

C#代码 复制代码 收藏代码
  1. protected overridevoid OnUpdate (float factor,bool isFinished) 
  2.     //cachedTransform.localRotation = Quaternion.Slerp(Quaternion.Euler(from), Quaternion.Euler(to), factor); 
  3.     //NGUI的实现是上一行,有Bug,不能达到要求 
  4.     cachedTransform.localEulerAngles = Vector3.Lerp(from, to, factor); 
  5.  

       NGUI提供了UIPlayTween,这个脚本管理一组Tween脚本的Play,提供了不同的Tirgger,然后在不同的事件函数中触发Play(true):

C#代码 复制代码 收藏代码
  1. void OnClick () 
  2.     if (enabled && trigger == Trigger.OnClick) 
  3.     { 
  4.         Play(true); 
  5.     } 

       虽然UIPlayTween提供了很多参数都是没有满足要其交替地进行PlayForward和PlayReverse,因为其都是执行Play(true),开始的时候我想到了给其中一个UITween添加OnFinished委托,在播放结束的时候改变playDiretion的方向:

C#代码 复制代码 收藏代码
  1. public Direction playDirection = Direction.Forward; 

       但是这个控制因为动画是有时间的,会有问题。所以我只能添加一个Trigger的类型:Trigger.None,然后自己去调用,就是自己管理动画的播放,而不是在OnClick中触发。』

                                                                                           增补于 2013,12,23  21:50

小结:

       确实很简单,主要是对缓动函数的理解,有了这些基础可以做的事情(特效和动画)就很多了——屏幕抖动和刀光剑影(下次自己动手尝试下,哈哈),NGUI的ButtonScale等脚本也是通过UITweener来完成的。但是收获蛮多的,又一点多了,晚安!

       如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。

        转载请在文首注明出处:http://dsqiu.iteye.com/blog/1974528

更多精彩请关注D.S.Qiu的博客和微博(ID:静水逐风)

参考:

①NGUI: Next-Gen UI kit  3.0.0:http://www.tasharen.com/ngui/docs/class_u_i_tweener.html

② 缓动函数:http://easings.net/zh-cn

③Easing Equations by Robbert Penner: http://www.gizma.com/easing/#sin2

④Unity3dPack: http://www.unity3dpack.com/?p=300

0 0
原创粉丝点击