Unity3d可配置UI动画
前言
这是我在项目中用来配置UI动画的View层组件,主要负责UI按钮的对应事件动画效果,以及各个窗口切换时的视觉表现,例如缩放,淡入淡出,回弹.这里面并没有涉及到项目的核心代码或商业机密,而且个人感觉很好用,所以将其独立出来,可以作为一个插件使用.代码我已经在GitHub上开源,希望对大家有所助益.下面将对整个UI动画配置组件进行复盘:
这是我在项目中用来配置UI动画的View层组件,主要负责UI按钮的对应事件动画效果,以及各个窗口切换时的视觉表现,例如缩放,淡入淡出,回弹.这里面并没有涉及到项目的核心代码或商业机密,而且个人感觉很好用,所以将其独立出来,可以作为一个插件使用.代码我已经在GitHub上开源,希望对大家有所助益.下面将对整个UI动画配置组件进行复盘:
0x00 创建Unity项目
因为一开始就打算将UI动画组件独立出来成为插件,所以就从Unity编辑器上新建一个名为UiAnimationAgent的项目,路径大家可以选择在自己习惯的位置即可,如图1所示,点击Create
project(创建项目)即可.
图 1 创建Unity项目
因为一开始就打算将UI动画组件独立出来成为插件,所以就从Unity编辑器上新建一个名为UiAnimationAgent的项目,路径大家可以选择在自己习惯的位置即可,如图1所示,点击Create project(创建项目)即可.
图 1 创建Unity项目
0x01 导入DoTween插件
因为这个UI动画系统是基于DoTween动画引擎来开发的,所以先导入该插件,方法是打开Unity编辑器的Asset Store资源商店,搜索DOTween插件名即可找到该插件,如图2所示,然后点击Download下载,点击Import导入,完成导入后会弹出设置菜单,点击Setup DOTween即可完成设置,关于DOTween的使用可以点击设置向导里面的Documentation来查看,网上也有不少关于DOTween的使用文章,这里就不再赘述.
图 2 DOTween插件
DOTween设置完成后,会自动弹出如图3窗口,点击Ok即可.
图 3 DOTween第三方库导入或更新
因为这个UI动画系统是基于DoTween动画引擎来开发的,所以先导入该插件,方法是打开Unity编辑器的Asset Store资源商店,搜索DOTween插件名即可找到该插件,如图2所示,然后点击Download下载,点击Import导入,完成导入后会弹出设置菜单,点击Setup DOTween即可完成设置,关于DOTween的使用可以点击设置向导里面的Documentation来查看,网上也有不少关于DOTween的使用文章,这里就不再赘述.
图 2 DOTween插件
DOTween设置完成后,会自动弹出如图3窗口,点击Ok即可.
图 3 DOTween第三方库导入或更新
0x02 创建Main场景
在Assets目录下新建Scene,Scripts和StreamingAssets文件夹,并在Scene文件夹下新建Main场景,然后双击打开Main场景,再在Main场景中新建一个按钮,并在该按钮上添加Main脚本,如图4所示,该脚本即是整个项目的入口,点击后即可进入游戏场景,这里Main对应你游戏的初始化场景即可,用来完成整个UiAnimationAgent的初始化.
图 4 创建Main场景
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace CLOUDHU.UIAnimationAgent {
public class Main : MonoBehaviour {
// Use this for initialization
void Start() {
//0.初始化DOTween动画组件
DG.Tweening.DOTween.Init(true, true, DG.Tweening.LogBehaviour.ErrorsOnly).SetCapacity(200, 10);
//1.读取Excel动画配置表
CSVHelper.Instance().ReadCSVFile("uiAnimationConfig", (table) => {
Debug.Log("读取动画配置表成功!");
});
//2.给按钮添加点击事件监听
GetComponent
在Assets目录下新建Scene,Scripts和StreamingAssets文件夹,并在Scene文件夹下新建Main场景,然后双击打开Main场景,再在Main场景中新建一个按钮,并在该按钮上添加Main脚本,如图4所示,该脚本即是整个项目的入口,点击后即可进入游戏场景,这里Main对应你游戏的初始化场景即可,用来完成整个UiAnimationAgent的初始化.
图 4 创建Main场景
using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; namespace CLOUDHU.UIAnimationAgent { public class Main : MonoBehaviour { // Use this for initialization void Start() { //0.初始化DOTween动画组件 DG.Tweening.DOTween.Init(true, true, DG.Tweening.LogBehaviour.ErrorsOnly).SetCapacity(200, 10); //1.读取Excel动画配置表 CSVHelper.Instance().ReadCSVFile("uiAnimationConfig", (table) => { Debug.Log("读取动画配置表成功!"); }); //2.给按钮添加点击事件监听 GetComponent
0x03 异步加载配置文件
上面Main脚本中,CSVHelper单例是用来加载动画的配置文件,这里借用了kashiwa同学的博客(Unity开发中异步加载配置文件,像读取数据库一样读取配置信息),非常的方便实用,这里就直接用了,详细的方法论大家不妨去kashiwa博客看看,这里不做重复解析.
使用时只需要在StreamingAssets文件夹下新建子文件夹GameConfig,然后把策划写好的Excel拖放进去即可.
策划大佬的配置表大致是这样的,如图5所示,打开UI动画配置文件uiAnimationConfig.csv,id表示UI动画唯一名称,这里把按钮和窗口大致分为大中小三种,每种类型又分为开始Start,End结束等阶段,不同阶段配置不同的time时间(以秒为单位),scale缩放,alpha透明度(淡入淡出的效果).
那么只需要在按钮和窗口(Panel或Canvas)上面添加对应的UiAnimationAgent组件即可,在这些按钮或窗口初始化的时候就会配置对应id的UI动画效果,当然,如果你觉得CSV不方便,还可以使用XML或者JSon来配置,只是解析时要改用对应的XML解析或LitJSon里面的JsonMapper.
图 5 uiAnimationConfig.csv动画配置文件
上面Main脚本中,CSVHelper单例是用来加载动画的配置文件,这里借用了kashiwa同学的博客(Unity开发中异步加载配置文件,像读取数据库一样读取配置信息),非常的方便实用,这里就直接用了,详细的方法论大家不妨去kashiwa博客看看,这里不做重复解析.
使用时只需要在StreamingAssets文件夹下新建子文件夹GameConfig,然后把策划写好的Excel拖放进去即可.
策划大佬的配置表大致是这样的,如图5所示,打开UI动画配置文件uiAnimationConfig.csv,id表示UI动画唯一名称,这里把按钮和窗口大致分为大中小三种,每种类型又分为开始Start,End结束等阶段,不同阶段配置不同的time时间(以秒为单位),scale缩放,alpha透明度(淡入淡出的效果).
那么只需要在按钮和窗口(Panel或Canvas)上面添加对应的UiAnimationAgent组件即可,在这些按钮或窗口初始化的时候就会配置对应id的UI动画效果,当然,如果你觉得CSV不方便,还可以使用XML或者JSon来配置,只是解析时要改用对应的XML解析或LitJSon里面的JsonMapper.
图 5 uiAnimationConfig.csv动画配置文件
0x04 按钮动画组件
//// UiAnimationAgent // //胡良云(CloudHu) //中文注释:胡良云(CloudHu) 7/10/2017 // -------------------------------------------------------------------------------------------------------------------- using DG.Tweening; using UnityEngine; using UnityEngine.UI; namespace CLOUDHU.UIAnimationAgent { public enum eBtnAnimationType //按钮类型枚举 { small, medium, big, }; ////// FileName: BtnAnimation.cs /// Author: 胡良云(CloudHu) /// Corporation: /// Description: 基于DOTween,按钮动画控制组件 /// DateTime: 7/10/2017 /// public class BtnAnimation : MonoBehaviour { #region Public Variables //公共变量区域 [Tooltip("按钮大小类型")] public eBtnAnimationType btnType = eBtnAnimationType.small; [Tooltip("按下动画持续时间:秒")] public float timeDown = 0.1f; [Tooltip("按钮按下缩放比例")] public float scaleDown = 0.85f; [Tooltip("Alpha值")] public float alphaDown = 0.8f; public float timeDownStep1 = 0.1f; public float scaleDownStep1 = 1; public float alphaDownStep1 = 1; [Tooltip("松开动画持续时间:秒")] public float timeUp = 0.05f; [Tooltip("按钮松开缩放比例")] public float scaleUp = 1.2f; [Tooltip("Alpha值")] public float alphaUp = 1f; //[Tooltip("动画延迟多少秒后开始")] //public float delay = 0; [Tooltip("动画曲线类型")] public string easetype = "spring"; public float timeUpStep1 = 0.1f; public float scaleUpStep1 = 1; public float alphaUpStep1 = 1; #endregion #region Private Variables //私有变量区域 bool m_bIfDownStep1 = false;//按下动画是否有额外步骤 bool m_bIfUpStep1 = true;//松开动画是否有额外步骤 Image m_imgComponent;//图片组件 private bool m_gray = false;// 默认可用,变灰不可以再变色 //private bool m_init = false; Button m_BtnComponent; private enum eBtnEffectTpye { None, Light, Dark, Gray, } #endregion #region MonoBehaviour CallBacks //MonoBehaviour回调函数区域 private void Awake() { m_imgComponent = transform.GetComponent(); m_BtnComponent = transform.GetComponent
0x05 窗口动画组件
//// UiAnimationAgent // //胡良云(CloudHu) //中文注释:胡良云(CloudHu) 7/10/2017 // -------------------------------------------------------------------------------------------------------------------- using DG.Tweening; using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; namespace CLOUDHU.UIAnimationAgent { ////// FileName: WindowAnimation.cs /// Author: 胡良云(CloudHu) /// Corporation: /// Description: 基于DOTween,窗口动画控制组件 /// DateTime: 7/10/2017 /// /// public enum WindowAnimationType //按钮类型 { small, medium, big } public class WindowAnimation : MonoBehaviour { #region Public Variables //公共变量区域 float alphaInit = 0; float scaleInit = 0; [Tooltip("按钮大小类型")] public WindowAnimationType windowType = WindowAnimationType.small; [Tooltip("动画持续时间:秒")] public float timeOpen = 0.1f; [Tooltip("缩放比例")] public float scaleOpen = 0.95f; [Tooltip("Alpha值")] public float alphaOpen = 0.8f; public float timeOpenStep1 = 0; public float alphaOpenStep1 = 0; public float scaleOpenStep1 = 0; [Tooltip("松开动画持续时间:秒")] public float timeClose = 0.05f; [Tooltip("按钮松开缩放比例")] public float scaleClose = 1f; [Tooltip("Alpha值")] public float alphaClose = 1f; //[Tooltip("动画延迟多少秒后开始")] //public float delay = 0; [Tooltip("动画曲线类型")] public string easetype = "linear"; //[Tooltip("动画循环类型")] //public iTween.LoopType looptype = iTween.LoopType.none; public float timeCloseStep1 = 0; public float alphaCloseStep1 = 0; public float scaleCloseStep1 = 0; public delegate void CloseWindowAnimationCallBack(GameObject callback); public Image m_imgBG = null; #endregion #region Private Variables //私有变量区域 bool m_bIfOpenStep1 = false;//按下动画是否有额外步骤 bool m_bIfCloseStep1 = false;//松开动画是否有额外步骤 bool m_IfInitFinished = false; //是否完成动画配置初始化 CloseWindowAnimationCallBack closeWindowAnimationCallBackTmp; CanvasGroup m_pcCavasGroup; float m_InitAlpha = float.NaN; #endregion #region MonoBehaviour CallBacks //MonoBehaviour回调函数区域 // Use this for initialization void Awake() { m_pcCavasGroup = GetComponent(); if (null == m_pcCavasGroup) { m_pcCavasGroup = gameObject.AddComponent (); } if (m_imgBG == null) { m_imgBG = this.GetComponent(); if (m_imgBG == null) { Image[] listImages = this.transform.GetComponentsInChildren(); for (int i = 0; i < listImages.Length; i ) { if (listImages[i].sprite != null && listImages[i].sprite.name.Contains("mengban")) { m_imgBG = listImages[i]; break; } } } } } private void Start() { CSVTable table;//配置表 table = CSVHelper.Instance().SelectFrom("uiAnimationConfig"); if (null != table) { string prefix = "";//窗口大小前缀 switch (windowType) { case WindowAnimationType.small: prefix = "small"; break; case WindowAnimationType.medium: prefix = "medium"; break; case WindowAnimationType.big: prefix = "big"; break; default: break; } //Debug.Log(prefix "WindowOpenStart"); //初始化值 alphaInit = float.Parse(table[prefix "WindowOpenStart"]["alpha"]); scaleInit = float.Parse(table[prefix "WindowOpenStart"]["scale"]); if (table.ContainsKey(prefix "WindowOpenStep1")) { m_bIfOpenStep1 = true; timeOpen = float.Parse(table[prefix "WindowOpenStep1"]["time"]); scaleOpen = float.Parse(table[prefix "WindowOpenStep1"]["scale"]); alphaOpen = float.Parse(table[prefix "WindowOpenStep1"]["alpha"]); alphaOpenStep1 = float.Parse(table[prefix "WindowOpenEnd"]["alpha"]); timeOpenStep1 = float.Parse(table[prefix "WindowOpenEnd"]["time"]); scaleOpenStep1 = float.Parse(table[prefix "WindowOpenEnd"]["scale"]); } else { timeOpen = float.Parse(table[prefix "WindowOpenEnd"]["time"]); scaleOpen = float.Parse(table[prefix "WindowOpenEnd"]["scale"]); alphaOpen = float.Parse(table[prefix "WindowOpenEnd"]["alpha"]); } if (table.ContainsKey(prefix "WindowCloseStep1")) { //Debug.Log(table["smallWindowCloseStep1"]["time"]); m_bIfCloseStep1 = true; timeClose = float.Parse(table[prefix "WindowCloseStep1"]["time"]); scaleClose = float.Parse(table[prefix "WindowCloseStep1"]["scale"]); alphaClose = float.Parse(table[prefix "WindowCloseStep1"]["alpha"]); alphaCloseStep1 = float.Parse(table[prefix "WindowCloseEnd"]["alpha"]); timeCloseStep1 = float.Parse(table[prefix "WindowCloseEnd"]["time"]); scaleCloseStep1 = float.Parse(table[prefix "WindowCloseEnd"]["scale"]); } else { timeClose = float.Parse(table[prefix "WindowCloseEnd"]["time"]); scaleClose = float.Parse(table[prefix "WindowCloseEnd"]["scale"]); alphaClose = float.Parse(table[prefix "WindowCloseEnd"]["alpha"]); } //Debug.Log(gameObject.name "=>prefix:" prefix " >>> " "timeOpen:" timeOpen " scaleOpen:" scaleOpen " alphaOpen:" alphaOpen " alphaOpenStep1:" alphaOpenStep1 " timeOpenStep1: " timeOpenStep1 " timeOpenStep1" timeOpenStep1 " scaleOpenStep1:" scaleOpenStep1 " timeSinceLevelLoad: " Time.timeSinceLevelLoad); m_IfInitFinished = true; OnWindowOpen(); Debug.Log("读取动画配置表成功" prefix); } table = null; } private void OnEnable() { if (m_imgBG != null) { if (float.IsNaN(m_InitAlpha)) { m_InitAlpha = m_imgBG.color.a; } Color _color = new Color(m_imgBG.color.r, m_imgBG.color.g, m_imgBG.color.b, m_InitAlpha); m_imgBG.color = _color; } if (m_IfInitFinished) { OnWindowOpen(); } } #endregion #region Public Methods //公共方法区域 /// /// 关闭窗口 /// public void OnWindowClose(CloseWindowAnimationCallBack windowAnimationCallBack) { if (!m_IfInitFinished) { //Debug.Log(m_IfInitFinished); windowAnimationCallBack.Invoke(gameObject); return; } if (m_imgBG != null) { Color _color = new Color(m_imgBG.color.r, m_imgBG.color.g, m_imgBG.color.b, 0); m_imgBG.color = _color; } if (m_bIfCloseStep1) { closeWindowAnimationCallBackTmp = windowAnimationCallBack; transform.DOScale(scaleClose, timeClose).OnComplete(OnCompleteClose); } m_pcCavasGroup.DOFade(alphaClose, timeClose); if (!m_bIfCloseStep1 && null != windowAnimationCallBack) { windowAnimationCallBack.Invoke(gameObject); } } #endregion #region Private Methods //私有方法区域 ////// 打开窗口 /// private void OnWindowOpen() { if (!m_IfInitFinished) { OnCompleteWindowAdjust(); return; } m_pcCavasGroup.alpha = alphaInit; anchorAjust(); transform.localScale = Vector3.one * scaleInit; if (m_bIfOpenStep1) { transform.DOScale(scaleOpen, timeOpen).OnComplete(OnCompleteWindowOpen); } else { transform.DOScale(scaleOpen, timeOpen).OnComplete(OnCompleteWindowAdjust); } m_pcCavasGroup.DOFade(alphaOpen, timeOpen); } ////// 窗口动画完成后进行自适应 /// void OnCompleteWindowAdjust() { anchorAjust(); } void anchorAjust() { RectTransform rect = GetComponent(); rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; } void OnCompleteWindowOpen() { if (!m_IfInitFinished) { return; } transform.DOScale(scaleOpenStep1, timeOpenStep1).OnComplete(OnCompleteWindowAdjust); m_pcCavasGroup.DOFade(alphaOpenStep1, timeOpenStep1); } /// /// 回弹效果 /// void OnCompleteClose() { if (!m_IfInitFinished) { return; } transform.DOScale(scaleCloseStep1, scaleCloseStep1).OnComplete(CloseWindowCallbackMethod); m_pcCavasGroup.DOFade(alphaCloseStep1, scaleCloseStep1); } ////// 关闭窗口回调 /// private void CloseWindowCallbackMethod() { if (null != closeWindowAnimationCallBackTmp) { closeWindowAnimationCallBackTmp.Invoke(gameObject); } } #endregion } }
0x06 测试场景
using CLOUDHU.UIAnimationAgent;//引用动画组件的命名空间 using UnityEngine; using UnityEngine.UI; ////// 测试脚本 /// public class Test : MonoBehaviour { public GameObject m_widowPrefab;//窗口预设 Button _btnOpen,_btnClose;//打开和关闭按钮 // Use this for initialization void Start () { _btnOpen = transform.FindChild("BtnOpen").GetComponent
最后,整个项目的代码在GitHub上开源:https://github.com/cloudhu/UiAnimationAgent
有兴趣的朋友不妨点星Fork一下!其实利用DOTween还可以配置更多炫酷的UI动画效果,例如按钮抖动,窗口旋转等等,大家不妨对源码进行改进,如果有什么问题可以提Issue,谢谢!
PS:对VR感兴趣的朋友可以看看我的书,卡卡西老师也爱看的小黄书!
最后,整个项目的代码在GitHub上开源:https://github.com/cloudhu/UiAnimationAgent
有兴趣的朋友不妨点星Fork一下!其实利用DOTween还可以配置更多炫酷的UI动画效果,例如按钮抖动,窗口旋转等等,大家不妨对源码进行改进,如果有什么问题可以提Issue,谢谢!
PS:对VR感兴趣的朋友可以看看我的书