`

NGUI所见即所得之UITweener

 
阅读更多



 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有任何建议或意见可以在文章后面评论,或者发邮件(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

 

1
1
分享到:
评论

相关推荐

    NGUI插件3.11.2版本

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    NGUI_3.11.3.unitypackage

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    NGUI3.11.4

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    CustomGUI.unitypackage

    使用unity原生GUI封装,来达到UGUI,NGUI所见即所得的效果和部分功能,目的是由此来了解高级UI的原理。

    NGUI Next-Gen UI 2020.1.5

    - 编辑器集成,所见即所得 - 本地化、数据绑定、委托、事件 - 支持所有平台 - 制作进行 1 次绘制调用的 UI - 随附完整的 C# 源代码 - 已广泛优化 - 专门团队支持 2020.1.5 - NEW: You can now specify per-symbol ...

    NGUI插件大全

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    NGUI 3.5.9

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。  基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。  全面支持iOS/Android和Flash。  灵活的事件系统。...

    NGUI 2020.1.5.unitypackage

    NGUI 是一款非常强大的 UI 系统和事件通知框架。...- 编辑器集成,所见即所得 - 本地化、数据绑定、委托、事件 - 支持所有平台 - 制作进行 1 次绘制调用的 UI - 随附完整的 C# 源代码 - 已广泛优化 - 专门团队支持

    NGUI3.5.9 Unity3d UI开发神器

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。  基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。  全面支持iOS/Android和Flash。  灵活的事件系统。...

    最新版本的NGUI插件NGUI Next-Gen UI 覆盖unity多个版本

    NGUI Next-Gen UI是一款功能强大、灵活性高的UI插件,是当前最新版本的NGUI插件。它可以覆盖Unity的多个版本,包括Unity 5、Unity 2017和Unity 2018等。与其他UI插件相比,NGUI Next-Gen UI具有高效的性能和优秀的...

    NGUI 2019_3_0.unitypackage.zip

    NGUI是一个非常强大的UI系统和事件通知框架。 特征 -编辑器集成,所见即所得 -本地化,数据绑定,委托,事件 -支持所有平台 -进行1次抽签的UI -随附完整的C#源代码 -广泛优化 -专用支持

    Unity插件 NGUI各种版本合集

    本包中共有六个版本的NGUI,大家可以自己选择版本。 NGUI Next-Gen UI 3.6.0.unitypackage NGUI Next-Gen UI 3.12.1(u5.6.5).unitypackage NGUI Next-Gen UI 2019.3.0.unitypackage NGUI Next-Gen UI v2018.3.0....

    NGUI3.6.4 最新版本NGUI ngui

    大名鼎鼎的U3D插件NGUI,移动开发做界面都用它.最新官方版本,包含API文档.

    NGUI Next-Gen UI v3.6.0

    最新版unity3d扩展插件:NGUI Next-Gen UI3.6.0 运行Unity3D,解压后此压缩包,在菜单Assets中选择自定义导入。 如果无法导入时,请检查导入目录中是否存在中文字符。...所见即所得的集成编辑器,支持所有平台。

    NGUI v3.11.2

    NGUI v3.11.2,适合Unity2017 NGUI 是一款非常强大的...- 编辑器集成,所见即所得 - 本地化、数据绑定、委托、事件 - 支持所有平台 - 制作进行 1 次绘制调用的 UI - 随附完整的 C# 源代码 - 已广泛优化 - 专门团队支持

    NGUI Next-Gen UI v3.5.8(unity3d插件最新版).rar

    软件介绍: NGUI Next-Gen UI v3.5.8最新完整版,本版本需要Unity 3.5.7或者更高版本。NGUI是一款功能强大的UI系统和事件通知框架。所见即所得的集成编辑器,支持所有平台。本资源来源于网络仅供测试。

    NGUI离线文档

    NGUI离线文档 基于3.7.5

    NGUI v3.12.1 2018 最新版NGUI

    2018最新版NGUI 版本 v3.12.1 2018-6-28 更新 NGUI Next-Gen UI v3.12.1.unitypackage

    NGUI四个版本集合

    NGUI3.0 NGUI3.4 NGUI3.5 NGUI3.6

Global site tag (gtag.js) - Google Analytics