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();
        }
    }
}

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

完毕。

原文地址:https://www.cnblogs.com/czw52460183/p/11071261.html

时间: 2024-10-19 15:49:29

Unity-动态显示窗口制作思路的相关文章

Unity KGFMapSystem插件制作小地图

KGFMapSystem版本:2.3 在我们开发游戏或者虚拟现实中,一般都会用到小地图,如果要我们去写小地图,可以用到unity 3d中就有一个插件,是专门开发小地图用的,这个插件就是KGFMapSystem.这个是它的官网:http://www.kolmich.at/documentation/ 还有一个介绍用KGFMapSystem制作小地图的台湾网站,内容比较详细:http://www.cg.com.tw/KGFMapSystem/Content/KGFMapSystem_001.asp

怎样将unity资源窗口中的文件一下全部折叠/打开

1.选中父物体 2.按住alt 3.再按下键盘上的左键/右键:此父物体下的全部折叠/打开 或者 alt + LMB  点击所要折叠/打开的父物体左边的小三角 怎样将unity资源窗口中的文件一下全部折叠/打开

Unity 使用BMFont制作字体

参考链接:http://blog.csdn.net/huang9012/article/details/32333913,作者:CSDN huang9012 NGUI版本:3.6.5 要自己制作字体,需要下载软件BMFont,下载BMFont 要制作的字体,这里以GOUDX-EXTRABOLD为例,下载字体 下载后安装,界面是这样的: 接下来开始制作字体: 1.打开Options——Font settings设置字体: 2.在Font Settings界面中设置如下: Font:要制作的字体名称

Unity进度条场景载入的制作思路

有两种做法,如果全部都是AssetsBundle动态载入,可以自己管控加载和释放.还有一种是借助Unity的Level(场景). 如果直接A-B场景切换,我比较担心加载峰值过高,所以采取这种做法:A-L-B.L是进度条专用场景 第一个问题是如何检测,OnLevelWasLoaded可以判断是否新场景加载完了,并调用Application.LoadLevelAsync(...)进行加载,可以通过返回对象获取到加载进度 使用时发现一个比较奇怪的事情,当场景很小秒载的时候,加载进度条执行OnDestr

unity使用 NGUI制作技能冷却效果的思路

把NGUI精灵的type改为filled 然后更改fill Amount 这个值 就会实现效果

Unity Game窗口中还原Scene窗口摄像机操作

最近在弄AI,调试程序的时候总是要调整摄像机的视角.灰常不爽然后自己写了个脚本.比较习惯Scene窗口下的摄像机操作所以就仿造了一个一样的操作脚本. 首相我们要知道Scene下的摄像机的操作方式 1.WASD分别控制前后左右的位移,注意:位移是已当前视角为基准的. 2.QE分别控制上下,注意:这个是针对世界坐标的. 3.鼠标右键控制自由视角旋转.(难点) 对于第一条来说,主要的问题就是现在玩家朝向问题.我怎么知道我面向哪里. Vector3 Face = transform.rotation *

分享一种兼具UD、U+V2高端隐藏,支持UEFI和4GB大文件的U启制作思路

(zyphio原创博文,转载请注明作者和出处) 目前万能U启的主流技术有量产.UD.U+V2,个人不喜欢量产(相对UD.U+V2来说,量产兼容性也不算高,还不易编辑.写入损耗大,常常得纠结主控),U+V2更新镜像最自由灵活,但觉得兼容略逊于UD.UD的Fbinst技术还是GPL开源,作为有开源情结的我,个人还是最推崇UD,但也遇到过问题和无奈,所以就有此思路. --具体如下: 分区基本结构: UD+exFAT+FAT16(仅两个常规可见分区) 特殊的制作方法: 1.先分在U盘末端分出FAT16主

unity 小地图的制作

说到小地图,大家可能有些不是很了解,但是说到英雄联盟大家可能眼前一亮,这个我知道! Q:英雄联盟右下角的小地图看到了吗? A:有些人会说那是小地图吗? Q:是的. 只是他们做的太好了,看起来不觉得像小地图罢了!——所以相信团队的力量! 不扯了,言归正传,开始咱们的小地图学习了! 1,打开Unity 建一个空的工程,结构如下. 2,开始写脚本了. miniMap.cs 1 using UnityEngine; 2 using System.Collections; 3 4 public class

在unity使用ScriptableObject制作配置文件

今天是我发布的第一篇博文希望大家会喜欢,现在不像以前那么忙了,所以借着这些这个机会我会渐渐的把一切所学或者自己总结的东西慢慢地分享出来也希望大家能在技术的道路上越走越远. 好了废话不多说了,今天我就来讲一下如何使用ScriptableObject这个东西来制作配置文件,我们一般在做游戏开发的时候经常有需要制作配置文件的需求,这样可以给策划或者程序提供一个文件,他们只需要修改上面的值接可以达到修改游戏一些设置的目的,这样不用不断地去修改代码了. 很多人都会用到一些保存数据的文件,例如json,xm