Unity入门Unity中动态控制UI的常规思路以及UI反馈效果(3D画布透明位置交替)

Posted Black_To_White

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity入门Unity中动态控制UI的常规思路以及UI反馈效果(3D画布透明位置交替)相关的知识,希望对你有一定的参考价值。

一、动态控制UI的常规思路

思路:1、脚本中先声明UI对象;2、获取对象;3、监听或修改对象。

  了解了常规思路,对于UI组件,你只要知道它的类型名称,就可以直接替换我下列的代码中对应位置的内容。
(例如按钮就是Button,文本就是Text,滑动条则为Slider等等)

直接上代码:

public class CanvasManager : MonoBehaviour

	//声明按钮对象,私有对象需要通过路径获取,根据自己需求,也可以是public
    private Button btn_In;
    //声明文本对象,公有对象需要在unity中将按钮对象拖拽到脚本上,根据自己需求,也可以是private
    public Text txt_Mode;
    void Start()
    
    	获取按钮对象,括号内为相对路径,具体要看脚本挂载在哪个对象上
    	如下图,我这个脚本是挂载在LeftMenu的父级上的,btn_In是LeftMenu的子级
        btn_In = transform.Find("LeftMenu/btn_In").GetComponent<Button>();
        //添加按钮监听,即按钮点击时触发
        btn_In.onClick.AddListener(() =>
        
        	//这里插入要执行的语句,例如切换场景
            SceneManager.LoadScene(1);
            //又例如退出系统
            Application.Quit();
            //改变文本内容(别一股脑CV哈,这只是多举了几个例子)
            txt_Mode.text = "改变文字";
        );
    


如果要监听滑动条(Slider),同理:

public class CanvasManager : MonoBehaviour

	private Slider sli_Time;
    void Start()
    
    	//直接套用
        sli_Time = transform.Find("路径").GetComponent<Slider>();
    
	void Update()
    
    	//改变滑动条的数值
        sli_Time.value--;
    

二、UI组件的特殊反馈效果

UI组件的反馈效果能很大提升用户的体验。

  例如最基本的按钮变色,即光标或VR射线移动到按钮上以及点击的时候按钮会变换底色。除此之外还可以让考虑让画布整体移动来实现翻页的效果,例如游戏中常见的帮助选项,点击之后则会呈现另一个画布,而新画布的呈现形式如果太过生硬,就会影响观感。
1、最基本的颜色改变反馈(一般就改图中那两项就行):

2、画布切换——快速滑动“刷新”(位置更替)
先看效果

实现步骤:我用的是dotween插件(封装了许多方法,适合做动画,且对新手很友好,一个语句搞定),想不通过插件进行翻页效果的,可以直接用translate方法再加插值(百度搜一下插值移动,我这里就只通过dotween展开了)

1、dotween插件的导入

2、在代码中引入头文件

3、在此之前大家需要先了解这个动画的全过程,首先是当前A画布Z轴方向的上升,其次是另一个B画布Z轴方向的下降,所以两个画布的初始位置必须确保一个高一个低

4、但高位的画布不应在一开始就显示,因此还得实现画布交替“消失”的效果,此时就需要用到透明度的调节:
4.1在unity中先给每个画布添加Canvas Group组件

4.2与按钮对象的获取方法相同,获取到画布对象后,对画布的透明度进行调整:

在start函数中先让待唤醒的页面显示为透明,等到按下页面按钮时,再将alpha调整为1
Canvas_Help.GetComponent<CanvasGroup>().alpha = 0;//1表示完全显示,0为透明

5、为了让A画布与B画布在交替的过程中不会看到明显重叠,因此B画布的移动需要给定一个延迟,即让A画布上升了一定程度后再让B画布下降,这样看起来才有页面轮换的效果。

延迟函数:

延迟函数,放在类中,与start、update等方法并列
public static IEnumerator DelayToInvokeDo(Action action, float delaySeconds)

    yield return new WaitForSeconds(delaySeconds);
    action();

延迟函数的调用:

StartCoroutine(CanvasManager.DelayToInvokeDo(() =>

    这里放置执行语句
, 0.8f));0.8f即为延迟时间

画布移动:

//声明动画序列
Sequence s = DOTween.Sequence();
DOMoveY顾名思义就是Y轴上的移动,30代表移动距离,1代表移动时间
s.Append(WholeCanvas.DOMoveY(30, 1).SetRelative().SetEase(Ease.InOutQuad));

完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using System;

public class CanvasManager : MonoBehaviour

	//这里用了public,记得到unity中把画布对象拖到脚本上
    public Transform WholeCanvas;
    public Transform Canvas_Help;
    private Button btn_Help;
    public static IEnumerator DelayToInvokeDo(Action action, float delaySeconds)
    
        yield return new WaitForSeconds(delaySeconds);
        action();
    
    void Start()
    
        Canvas_Help.GetComponent<CanvasGroup>().alpha = 0;
        WholeCanvas.GetComponent<CanvasGroup>().alpha = 0;
        //声明动画序列
        Sequence s = DOTween.Sequence();
        btn_Help = transform.Find("LeftMenu/btn_Help").GetComponent<Button>();
        //帮助
        btn_Help.onClick.AddListener(() =>
        
            WakingHelp();
            //具体参数要自己根据场景调整
            s.Append(WholeCanvas.DOMoveY(30, 1).SetRelative().SetEase(Ease.InOutQuad));
            s.Append(Canvas_Help.DOMoveY(-37, 1).SetRelative().SetEase(Ease.InOutQuad));
            StartCoroutine(CanvasManager.DelayToInvokeDo(() =>
            
                WakingWhole();
            , 0.8f));
        );
    
    void WakingHelp()
    
        Canvas_Help.GetComponent<CanvasGroup>().alpha = 1;
    
    void WakingWhole()
    
        WholeCanvas.GetComponent<CanvasGroup>().alpha = 1;
    


PS:除了位置更替外,画布之间的切换还可以考虑左右翻转的效果(更适合2D界面使用),难度较大,有兴趣的可以先琢磨琢磨,后续有时间我再写写(毕竟这篇文章是上班摸鱼的时候赶的【doge】)

Unity-动态显示窗口制作思路

此教程来自siki学院的<<暗黑战神>>课程

 

这次需要记录的是动态显示窗口的制作方式,它的效果是弹出一条游戏Tips,上面可以显示你想显示的内容,随后消失。

显然,我们只需要制作一个动画,动画中改变Text组件的位置即可实现此效果。

然而现在的问题是,不能让这个动画立刻播放,我们需要在特定的时候去播放它,并在特定的时刻停止。

 

那么怎么实现呢?

我们可以在控制动态显示窗口的脚本中,设置一个方法,当要显示动态Tips时,设置改变Text内容,并激活对应的游戏物体,手动控制动画播放。注意,游戏物体不激活时即使动画是自动播放的,也不会播放,只有激活了,才会从头开始播放

动画播放后需要停止,因此需要把游戏物体取消激活,但我们在代码中怎么准确地控制它能延时取消激活呢?

这个问题和之前说的异步加载资源服务有点类似,传送门如下:

https://www.cnblogs.com/czw52460183/p/11044456.html

当时的思路是在Update中用委托实时查看加载进度,但这里就不行,因为我们没有什么方法可以实时获取到动画播放进度,怎么办呢?

答案是可以使用协程,我们只需要在开启协程后,让它在固定等待一段时间后调用回调方法即可,在这个回调方法中去关闭物体的激活状态即可,而这个等待的时间,就设置成动画的持续时长即可,这样,即使我们没有办法掌握动画的实际播放进度,也可以实现延时关闭。

参考代码如下:(由于只是讲述思路,这里代码中的细节就不详细叙述了,因为用到的一些方法都是之前制作的,这里没给出具体信息,只看一下思路即可)

/****************************************************
    文件:DynamicWnd.cs
    作者:czw52460183
    邮箱: [email protected]
    日期:2019/6/22 10:8:16
    功能:动态UI元素界面
*****************************************************/

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class DynamicWnd : WindowRoot 

    public Text txtTips;
    public Animation tipsAni;

    //本窗口初始化时需要:
    //1.使txtTips所属的游戏物体不激活
    //注意,游戏物体不激活时即使动画是自动播放的,也不会播放,只有激活了,才会从头开始播放
    protected override void InitWnd()
    
        base.InitWnd();
        SetActive(txtTips,false);
    

    /// <summary>
    /// 设置界面的展示内容
    /// </summary>
    public void SetTips(string tips)
    
        //激活txtTips所属的游戏物体
        SetActive(txtTips, true);
        //设置展示内容
        SetText(txtTips,tips);
        //播放动画
        tipsAni.Play();
        //利用协程实现延时关闭激活状态
        AnimationClip clip = tipsAni.clip;
        StartCoroutine(AniPlayDone(clip.length,()=>
            //延时到动画结束后关闭激活状态
            SetActive(txtTips,false);
        ));
    

    //协程,延时后调用回调方法
    private IEnumerator AniPlayDone(float sec, Action cb)
    
        yield return new WaitForSeconds(sec);
        if(cb != null)
        
            cb();
        
    

  Unity中是单线程的,我的理解是,开启协程后,系统会在每帧检查yield return的条件是否满足,满足则执行yield return后面的方法,不满足则继续执行开启协程语句后面的指令,这就使得虽然Unity是单线程的,但协程会给人一种多线程的感觉。

  但使用协程也会带来新的问题,就是由于它模拟出了多线程的效果,自然也会出现多线程有的问题,假如我们的系统在两个不同的地方同时调用了SetTips方法,会导致第一个SetTips在开启协程后继续执行下面的指令,此时第一个动画在播放,但是还没到进入yield return的时间,这时系统在执行后面的指令,即开始执行第二个SetTips指令,此时又会设置展示内容,因此就把第一个动画设置的展示内容覆盖掉了,导致的结果就是只展示了第二个SetTips的指令。

  怎么办呢?

  可以使用一个队列,当每一条SetTips请求进来时,把它加入到队列中,而在每一帧中对队列中的元素数量进行检查,若队列中有元素,就取出对应内容并进行展示内容的设置。注意,在向队列中添加或取出元素时,由于可能出现竞争,因此要加锁。  

  代码如下:

/****************************************************
    文件:DynamicWnd.cs
    作者:czw52460183
    邮箱: [email protected]
    日期:2019/6/22 10:8:16
    功能:动态UI元素界面
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DynamicWnd : WindowRoot 

    public Text txtTips;
    public Animation tipsAni;

    //本窗口初始化时需要:
    //1.使txtTips所属的游戏物体不激活
    //注意,游戏物体不激活时即使动画是自动播放的,也不会播放,只有激活了,才会从头开始播放
    protected override void InitWnd()
    
        base.InitWnd();
        SetActive(txtTips,false);
    

    //用队列来存放要展示的内容
    private Queue<string> tipsQueue = new Queue<string>();

    /// <summary>
    /// 把展示的内容添加到队列中
    /// </summary>
    public void AddTips(string tips)
    
        //添加前要为队列加锁,防止冲突
        lock(tipsQueue)
        
            tipsQueue.Enqueue(tips);
        
    

    //每一帧检测队列中是否有元素,有就取出并展示
    private void Update()
    
        if(tipsQueue.Count > 0)
        
            //取出前也要为队列加锁,防止冲突
            lock (tipsQueue)
            
                string tips = tipsQueue.Dequeue();
                SetTips(tips);
            
        
    

    /// <summary>
    /// 设置界面的展示内容
    /// </summary>
    private void SetTips(string tips)
    
        //激活txtTips所属的游戏物体
        SetActive(txtTips, true);
        //设置展示内容
        SetText(txtTips,tips);
        //播放动画
        tipsAni.Play();
        //利用协程实现延时关闭激活状态
        AnimationClip clip = tipsAni.clip;
        StartCoroutine(AniPlayDone(clip.length,()=>
            //延时到动画结束后关闭激活状态
            SetActive(txtTips,false);
        ));
    

    //协程,延时后调用回调方法
    private IEnumerator AniPlayDone(float sec, Action cb)
    
        yield return new WaitForSeconds(sec);
        if(cb != null)
        
            cb();
        
    

红色的是要修改的地方,这里相当于是把协程带来的冲突问题转移到了Update中。

 

但这样的修改是否能解决问题?显然不行,虽然冲突问题转移到了Update中,但Update是每一帧执行一次的,速度很快,所以面临的状况和之前其实一样,第一个设置内容请求(这里是AddTips)使协程开启后,由于Update执行间隔很短,第二个请求依然会在动画播放完成前进入SetTips。

 

我们可以在这基础上再加一个标志变量,标记当前是否有Tips在展示,只有没有展示时才从队列中取元素并展示,当动画播放完,即协程返回时,才将标记变量重新恢复为无展示状态。

代码如下:

/****************************************************
    文件:DynamicWnd.cs
    作者:czw52460183
    邮箱: [email protected]
    日期:2019/6/22 10:8:16
    功能:动态UI元素界面
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DynamicWnd : WindowRoot 

    public Text txtTips;
    public Animation tipsAni;

    //本窗口初始化时需要:
    //1.使txtTips所属的游戏物体不激活
    //注意,游戏物体不激活时即使动画是自动播放的,也不会播放,只有激活了,才会从头开始播放
    protected override void InitWnd()
    
        base.InitWnd();
        SetActive(txtTips,false);
    

    //标志变量,代表当前是否有Tips在展示
    private bool isTipsShow = false; 
    //用队列来存放要展示的内容
    private Queue<string> tipsQueue = new Queue<string>();

    /// <summary>
    /// 把展示的内容添加到队列中
    /// </summary>
    public void AddTips(string tips)
    
        //添加前要为队列加锁,防止冲突
        lock(tipsQueue)
        
            tipsQueue.Enqueue(tips);
        
    

    //每一帧检测
    private void Update()
    
        //只有当队列中有元素,且当前没有Tips在展示时才从队列中取元素展示
        if (tipsQueue.Count > 0 && isTipsShow == false)
        
            //取出前也要为队列加锁,防止冲突
            lock (tipsQueue)
            
                string tips = tipsQueue.Dequeue();
                //标记当前为正在展示状态
                isTipsShow = true;
                SetTips(tips);
            
        
    

    /// <summary>
    /// 设置界面的展示内容
    /// </summary>
    private void SetTips(string tips)
    
        //激活txtTips所属的游戏物体
        SetActive(txtTips, true);
        //设置展示内容
        SetText(txtTips,tips);
        //播放动画
        tipsAni.Play();
        //利用协程实现延时关闭激活状态
        AnimationClip clip = tipsAni.clip;
        StartCoroutine(AniPlayDone(clip.length,()=>
            //延时到动画结束后关闭激活状态
            SetActive(txtTips,false);
            //标记当前为无展示状态
            isTipsShow = false;
        ));
    

    //协程,延时后调用回调方法
    private IEnumerator AniPlayDone(float sec, Action cb)
    
        yield return new WaitForSeconds(sec);
        if(cb != null)
        
            cb();
        
    

红色为添加内容,具体不再介绍。

 

完毕。

 

以上是关于Unity入门Unity中动态控制UI的常规思路以及UI反馈效果(3D画布透明位置交替)的主要内容,如果未能解决你的问题,请参考以下文章

记一次Unity_UGUI入门

Unity-动态显示窗口制作思路

Unity ShaderSpecial EffectsDissolve 溶解(UI)

Unity ShaderSpecial EffectsDissolve 溶解(UI)

Unity ShaderSpecial EffectsDissolve 溶解(UI)

Unity ShaderSpecial EffectsDissolve 溶解(UI)