NGUI所見即所得之UITweener
一直沒有用過NGUI動畫的功能,之前的了解就是:設定始末兩個“位置”,然後就是從起始位置移到結束位置。至于中間是怎麼變化的,就感覺很神奇了,變化率怎麼設定才不會看起來很“傻”,這裡不是看“郭靖”,動畫一定要有驚奇,摸不着猜不透的感覺。對NGUI主要的幾個腳本都已經有點掌握了(猛點檢視),一直都沒有去”膜拜“Tweening檔案夾的各個大神,可能以前會覺得不就是一個動畫元件,自己都可以實作。但是看過裡面的代碼就後悔了,因為至少結合TweenFOV和TweenOrhoSize這兩個腳本就可以實作很多效果,竟然輕而易舉的內建了,看來人還是不要太看得起自己的好,這樣才會走的更快更遠。
每次都覺得前面吹水很寫,也寫不好(一直都有感覺自己的寫作水準太差了),那就來看下Tweening檔案夾下到底賣的是什麼藥——UITweener和它的“孩子”:

UITweener的Fields
看着很複雜,其實隻要把UITweener琢磨透了,其它都隻是重寫UITweener的OnUpdate方法和封裝了Begin方法。還是先看下主要的Field(作用看注釋):
bool mStarted = false; //是否開始動畫
float mStartTime = 0f; //動畫開始播放的時間, mStarted =true;mStartTime = time + delay;
float mDuration = 0f; //動畫長度(時間)
float mAmountPerDelta = 1000f; //機關時間動畫播放的長度,有點幀率的感覺
float mFactor = 0f; //目前動畫播放的進度
/// <summary>
/// Amount advanced per delta time.
/// </summary>
public float amountPerDelta
{
get
{
if (mDuration != duration)
{
mDuration = duration;
mAmountPerDelta = Mathf.Abs((duration > 0f) ? 1f / duration : 1000f);
}
return mAmountPerDelta;
}
}
通過Begin設定需要的參數:
static public T Begin<T> (GameObject go, float duration) where T : UITweener
{
T comp = go.GetComponent<T>();
#if UNITY_FLASH
if ((object)comp == null) comp = (T)go.AddComponent<T>();
#else
if (comp == null) comp = go.AddComponent<T>();
#endif
comp.mStarted = false;
comp.duration = duration;
comp.mFactor = 0f;
comp.mAmountPerDelta = Mathf.Abs(comp.mAmountPerDelta);
comp.style = Style.Once;
comp.animationCurve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f), new Keyframe(1f, 1f, 1f, 0f));
comp.eventReceiver = null;
comp.callWhenFinished = null;
comp.enabled = true;
return comp;
}
Update函數
然後再Update函數先計算出時間delta,進一步計算出目前動畫播放的mFactor,然後進行Sample采用,執行OnUpdate:
void Update ()
{
float delta = ignoreTimeScale ? RealTime.deltaTime : Time.deltaTime;
float time = ignoreTimeScale ? RealTime.time : Time.time;
if (!mStarted)
{
mStarted = true;
mStartTime = time + delay;
}
if (time < mStartTime) return;
// Advance the sampling factor
mFactor += amountPerDelta * delta;
// Loop style simply resets the play factor after it exceeds 1.
if (style == Style.Loop)
{
if (mFactor > 1f)
{
mFactor -= Mathf.Floor(mFactor);
}
}
else if (style == Style.PingPong)
{
// Ping-pong style reverses the direction
if (mFactor > 1f)
{
mFactor = 1f - (mFactor - Mathf.Floor(mFactor));
mAmountPerDelta = -mAmountPerDelta;
}
else if (mFactor < 0f)
{
mFactor = -mFactor;
mFactor -= Mathf.Floor(mFactor);
mAmountPerDelta = -mAmountPerDelta;
}
}
// If the factor goes out of range and this is a one-time tweening operation, disable the script
if ((style == Style.Once) && (mFactor > 1f || mFactor < 0f))
{
mFactor = Mathf.Clamp01(mFactor);
Sample(mFactor, true);
current = this;
// Notify the listener delegates
EventDelegate.Execute(onFinished);
// Deprecated legacy functionality support
if (eventReceiver != null && !string.IsNullOrEmpty(callWhenFinished))
eventReceiver.SendMessage(callWhenFinished, this, SendMessageOptions.DontRequireReceiver);
current = null;
// Disable this script unless the function calls above changed something
if (mFactor == 1f && mAmountPerDelta > 0f || mFactor == 0f && mAmountPerDelta < 0f)
enabled = false;
}
else Sample(mFactor, false);
}
Sample采樣函數
前面說的動畫要有摸不着猜不透的感覺,就是要考Sample的采樣函數來實作的,UITweener支援5種動畫曲線:
public enum Method
{
Linear,
EaseIn,
EaseOut,
EaseInOut,
BounceIn,
BounceOut,
}
采樣的函數,原理很簡單:根據目前播放的進度mFactor,計算出實際的動畫播放刻度,然後執行OnUpdate操作:
public void Sample (float factor, bool isFinished)
{
// Calculate the sampling value
float val = Mathf.Clamp01(factor);
if (method == Method.EaseIn)
{
val = 1f - Mathf.Sin(0.5f * Mathf.PI * (1f - val));
if (steeperCurves) val *= val;
}
else if (method == Method.EaseOut)
{
val = Mathf.Sin(0.5f * Mathf.PI * val);
if (steeperCurves)
{
val = 1f - val;
val = 1f - val * val;
}
}
else if (method == Method.EaseInOut)
{
const float pi2 = Mathf.PI * 2f;
val = val - Mathf.Sin(val * pi2) / pi2;
if (steeperCurves)
{
val = val * 2f - 1f;
float sign = Mathf.Sign(val);
val = 1f - Mathf.Abs(val);
val = 1f - val * val;
val = sign * val * 0.5f + 0.5f;
}
}
else if (method == Method.BounceIn)
{
val = BounceLogic(val);
}
else if (method == Method.BounceOut)
{
val = 1f - BounceLogic(1f - val);
}
// Call the virtual update
OnUpdate((animationCurve != null) ? animationCurve.Evaluate(val) : val, isFinished);
}
緩動函數(easing fuction)
上面說的動畫曲線,中文叫緩動函數(曲線):
通過上面這張圖可以很感性的認識不同函數的具體的效果,也可以自己嘗試推導一邊加深了解,不過D.S.Qiu已經有點“廉頗老矣”,憑着記憶“奇變偶不變,符号看象限”,慢的隻能到easeInSine,要想詳細了解可以參考②和③。
妙用mAmountPerDelta
mAmountPerDelta就是動畫播放速度,隻對mAmountPerData就可以有更多控制:Toggle,PlayForward,PlayResverse:
/// <summary>
/// Manually activate the tweening process, reversing it if necessary.
/// </summary>
public void Play (bool forward)
{
mAmountPerDelta = Mathf.Abs(amountPerDelta);
if (!forward) mAmountPerDelta = -mAmountPerDelta;
enabled = true;
Update();
}
/// <summary>
/// Manually start the tweening process, reversing its direction.
/// </summary>
public void Toggle ()
{
if (mFactor > 0f)
{
mAmountPerDelta = -amountPerDelta;
}
else
{
mAmountPerDelta = Mathf.Abs(amountPerDelta);
}
enabled = true;
}
『Bug修複和吐槽
之前用TweenRotation這個腳本,做遊戲等待轉圈等待界面,發現總是不能旋轉360度,總是一個小于180的角度,無論from和to如何設定:
public Vector3 from;
public Vector3 to;
後來無奈之下,隻好去看下TweenRotation的OnUpdate函數,發現使用的是 Quaternion.Slerp這個函數,發現确實是這樣,是以就做了下面的修改:
protected override void OnUpdate (float factor, bool isFinished)
{
//cachedTransform.localRotation = Quaternion.Slerp(Quaternion.Euler(from), Quaternion.Euler(to), factor);
//NGUI的實作是上一行,有Bug,不能達到要求
cachedTransform.localEulerAngles = Vector3.Lerp(from, to, factor);
}
NGUI提供了UIPlayTween,這個腳本管理一組Tween腳本的Play,提供了不同的Tirgger,然後在不同的事件函數中觸發Play(true):
void OnClick ()
{
if (enabled && trigger == Trigger.OnClick)
{
Play(true);
}
}
雖然UIPlayTween提供了很多參數都是沒有滿足要其交替地進行PlayForward和PlayReverse,因為其都是執行Play(true),開始的時候我想到了給其中一個UITween添加OnFinished委托,在播放結束的時候改變playDiretion的方向:
public Direction playDirection = Direction.Forward;
但是這個控制因為動畫是有時間的,會有問題。是以我隻能添加一個Trigger的類型:Trigger.None,然後自己去調用,就是自己管理動畫的播放,而不是在OnClick中觸發。』
增補于 2013,12,23 21:50
小結:
确實很簡單,主要是對緩動函數的了解,有了這些基礎可以做的事情(特效和動畫)就很多了——螢幕抖動和刀光劍影(下次自己動手嘗試下,哈哈),NGUI的ButtonScale等腳本也是通過UITweener來完成的。但是收獲蠻多的,又一點多了,晚安!
如果您對D.S.Qiu有任何建議或意見可以在文章後面評論,或者發郵件([email protected])交流,您的鼓勵和支援是我前進的動力,希望能有更多更好的分享。
轉載請在文首注明出處: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