背景

游戏中的UI系统或者叫做GUI窗口系统主要有:主要装备窗口(背包,角色窗口也是一种特殊窗口)、确实提示窗口(如购买确认)、信息提示窗口(一遍没有按钮,ContexntMenu)和特殊窗口(聊天记录或者技能树),前篇已经介绍分析了Inventory Pro确认提示窗口的设计和实现方式,这篇主要讲一下信息提示窗口的实现。本以为提示窗口是比较简单的,毕竟没有按钮事件交互的问题,但是分析了下源代码还是让我有些惊讶,插件作者在提示窗口中考虑到了性能问题,由于本人一直在PC端开发程序没有移动端的经验,所以在移动端对于性能优化还是比较关注的。

通用对话框的实现

通用预设对象池的实现

声明:本文首发于蛮牛,次发博客园,本人原创。 原文链接1原文链接2

插件效果及使用

左下角即为信息提示窗口NoticeUI,当信息提示比较多时,具有滚动条和超出自动隐藏的功能,是通过对象池技术实现,提高性能和效率

通过拖拽的方式创建好UI界面,红框中我们看到了组件树的结构和类型

在NoticeUI上绑定NoticeUI脚本,设置好每一行显示的预设NoticeMessageUI,ScrollRect等相关属性,基本就已经完成了关于信息提示窗口的实现了

源代码分析

老规矩上类图

类图分析

经过这段时间的学习,我真的慢慢爱上了VS的类图分析了,希望新手同学也能习惯这点。VS的类图很强大能自动生成关联关系和继承接口等信息,是特别舒心的类图工具。

A、先看下Message模型(Model)类,InventoryNoticeMessage继承了InventoryMessage,继承后拥有的字段有,消息,标题,颜色,消失延时,时间看到这些字段我们大致也可以猜到信息提示窗口有哪些功能了吧(其实是可以扩展的),这里需要重点关注下Show方法(后面源码分析再表述)

B、NoticeUI和NoticeMessageUI都是MonoBehavior的子类,也就是说他们都是组件,分析其字段有具有ScrollRect和Text说明他们都是需要和UI进行绑定的。这里特变关注下VS使用双箭头表示组合关联,所以NoticeUI组合关联NoticeMessageUI,而继承了IPoolableObject接口顾名思义它具有入对象池的能力,也就是可以加入对象池,我们也看到了NoticeUI有一个InventoryPool<NoticeMessageUI>,我们大概可以猜到NoticeUI中List<NoticMessageUI>和InevntoryPool<NoticeMessageUI>猥琐的关系。

调用流程分析

调用的流程其实可以画一个流程图,这里只是简单的描述一下

1、InventoryNoticeMessage.Show() –>2、 全局NoticeUI.AddMessage()->3、InventoryPool池对象NoticeMessageUI.SetMessage()->4、NoticMessageUI通过setAictive(true)进行显示->5、NoticeUI.Update,通过循环调用NoticMessageUI的showTime.deltaTime做控制隐藏

1、InventoryNoticeMessage.Show()

通过以上代码我们看的出来其实notice也是一个全局的UI,所以才可以通过单例来访问,应该是有固定区域的。

2、 全局NoticeUI.AddMessage()

NoticeUI中的AddMessage就比较复杂了,主要要处理几个事情A、事件触发;B、滚动处理;C、对象池获取NoticeMessageUI并激活显示D、List<NoticeMessageUI>和InventoryPool<NoticeMessageUI>好基友的处理(对象池的回收及引用数组的移除)

3、InventoryPool池对象NoticeMessageUI.SetMessage()

SetMessage() 就像它的方法名一样好像什么也没有做的样子,只是设置了一些简单字段的内容以及显示时间,实际的显示激活却是在4对象池获取的时候置位的。

4、NoticMessageUI通过setAictive(true)进行显示

对象池的使用和回收是通过池对象的activeSelf属性来确定的,这个开关有一箭双雕的意思,既通过它来控制对象池的使用和回收,又用于控制UI对象的演示与否。

5、NoticeUI.Update,通过循环调用NoticMessageUI的showTime.deltaTime控制隐藏

通过显示时间来控制信息的隐藏

隐藏函数使用了动画效果,由于动画是有显示时间的,所以通过一个字段isHiding做为状态判断。

核心源码

NoticeUI

using UnityEngine;
using UnityEngine.EventSystems;
using System;
using System.Collections;
using System.Collections.Generic;
using Devdog.InventorySystem.Models;
using Devdog.InventorySystem.UI.Models;
using UnityEngine.UI; namespace Devdog.InventorySystem
{
/// <summary>
/// How long a message should last.
/// Parse to int to get time in seconds.
/// </summary>
public enum NoticeDuration
{
Short = ,
Medium = ,
Long = ,
ExtraLong =
} [AddComponentMenu("InventorySystem/Windows/Notice")]
public partial class NoticeUI : MonoBehaviour
{
#region Events /// <summary>
/// Note that it also fired when message == null or empty, even though the system won't process the message.
/// This is because someone might want to implement their own system and just use the event as a link to connect the 2 systems.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
/// <param name="duration"></param>
/// <param name="parameters"></param>
public delegate void NewMessage(InventoryNoticeMessage message, params System.Object[] parameters);
public event NewMessage OnNewMessage; #endregion [Header("General")]
public NoticeMessageUI noticeRowPrefab; [InventoryRequired]
public RectTransform container; public ScrollRect scrollRect;
public AudioClip onNewMessageAudioClip; /// <summary>
/// When more messages come in the last items will be removed.
/// </summary>
[Header("Messages")]
public int maxMessages = ; /// <summary>
/// Remove the item after the show time has passed, if false, the item will continue to exist.
/// </summary>
public bool destroyAfterShowTime = true; /// <summary>
/// All show times are multiplied by this value, if you want to increase all times, use this value.
/// </summary>
public float showTimeFactor = 1.0f; [NonSerialized]
protected List<NoticeMessageUI> messages = new List<NoticeMessageUI>();
private InventoryPool<NoticeMessageUI> pool; public virtual void Awake()
{
pool = new InventoryPool<NoticeMessageUI>(noticeRowPrefab, maxMessages);
} public virtual void Update()
{
if (destroyAfterShowTime == false)
return; foreach (var message in messages)
{
message.showTime -= Time.deltaTime;
if (message.showTime < 0.0f)
{
message.Hide();
}
}
} public virtual void AddMessage(string message, NoticeDuration duration = NoticeDuration.Medium)
{
AddMessage(message, duration);
} public virtual void AddMessage(string message, NoticeDuration duration, params System.Object[] parameters)
{
AddMessage(string.Empty, message, duration, parameters);
} public virtual void AddMessage(string title, string message, NoticeDuration duration, params System.Object[] parameters)
{
AddMessage(new InventoryNoticeMessage(title, message, duration));
} public virtual void AddMessage(InventoryNoticeMessage message)
{
// Fire even if we do the nullcheck, just incase other people want to use their own implementation.
if (OnNewMessage != null)
OnNewMessage(message, message.parameters); if (string.IsNullOrEmpty(message.message))
return; bool scrollbarAtBottom = false;
if (scrollRect != null && scrollRect.verticalScrollbar != null && scrollRect.verticalScrollbar.value < 0.05f)
scrollbarAtBottom = true; // Incase we don't actually want to display anything and just port the data to some other class through events.
if (noticeRowPrefab != null)
{
var item = pool.Get();
//var item = GameObject.Instantiate<NoticeMessageUI>(noticeRowPrefab);
item.transform.SetParent(container);
item.transform.SetSiblingIndex(); // Move to the top of the list
item.SetMessage(message); if (onNewMessageAudioClip != null)
InventoryUIUtility.AudioPlayOneShot(onNewMessageAudioClip); messages.Add(item);
} if (messages.Count > maxMessages)
{
StartCoroutine(DestroyAfter(messages[], messages[].hideAnimation.length));
messages[].Hide();
messages.RemoveAt();
} if (scrollbarAtBottom)
scrollRect.verticalNormalizedPosition = 0.0f;
} protected virtual IEnumerator DestroyAfter(NoticeMessageUI item, float time)
{
yield return new WaitForSeconds(time);
pool.Destroy(item);
}
}
}

NoticeMessageUI

using System;
using System.Collections;
using System.Collections.Generic;
using Devdog.InventorySystem.Models;
using UnityEngine;
using UnityEngine.UI; namespace Devdog.InventorySystem.UI.Models
{
/// <summary>
/// A single message inside the message displayer
/// </summary>
[RequireComponent(typeof(Animator))]
public partial class NoticeMessageUI : MonoBehaviour, IPoolableObject
{
public UnityEngine.UI.Text title;
public UnityEngine.UI.Text message;
public UnityEngine.UI.Text time; public AnimationClip showAnimation;
public AnimationClip hideAnimation; [HideInInspector]
public float showTime = 4.0f; public DateTime dateTime { get; private set; } [NonSerialized]
protected Animator animator;
[NonSerialized]
protected bool isHiding = false; // In the process of hiding public virtual void Awake()
{
animator = GetComponent<Animator>(); if (showAnimation != null)
animator.Play(showAnimation.name);
} public virtual void SetMessage(InventoryNoticeMessage message)
{
this.showTime = (int)message.duration;
this.dateTime = message.time; if (string.IsNullOrEmpty(message.title) == false)
{
if (this.title != null)
{
this.title.text = string.Format(message.title, message.parameters);
this.title.color = message.color;
}
}
else
title.gameObject.SetActive(false); this.message.text = string.Format(message.message, message.parameters);
this.message.color = message.color; if (this.time != null)
{
this.time.text = dateTime.ToShortTimeString();
this.time.color = message.color;
}
} public virtual void Hide()
{
// Already hiding
if (isHiding)
return; isHiding = true; if (hideAnimation != null)
animator.Play(hideAnimation.name);
} public void Reset()
{
isHiding = false;
}
}
}

InventoryNoticeMessage

using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine; namespace Devdog.InventorySystem.Models
{
[System.Serializable]
public partial class InventoryNoticeMessage : InventoryMessage
{
public DateTime time; public Color color = Color.white;
public NoticeDuration duration = NoticeDuration.Medium; /// <summary>
/// Required for PlayMaker...
/// </summary>
public InventoryNoticeMessage()
{ } public InventoryNoticeMessage(string title, string message, NoticeDuration duration, params System.Object[] parameters)
: this(title, message, duration, Color.white, DateTime.Now, parameters)
{ } public InventoryNoticeMessage(string title, string message, NoticeDuration duration, Color color, params System.Object[] parameters)
: this(title, message, duration, color, DateTime.Now, parameters)
{ } public InventoryNoticeMessage(string title, string message, NoticeDuration duration, Color color, DateTime time, params System.Object[] parameters)
{
this.title = title;
this.message = message;
this.color = color;
this.time = time;
this.parameters = parameters;
} public override void Show(params System.Object[] param)
{
base.Show(param); this.time = DateTime.Now; if (InventoryManager.instance.notice != null)
InventoryManager.instance.notice.AddMessage(this);
}
}
}

Unity3D 通用提示窗口实现分析(Inventory Pro学习总结)的更多相关文章

  1. Unity3d UGUI 通用Confirm确认对话框实现(Inventory Pro学习总结)

    背景 曾几何时,在Winform中,使用MessageBox对话框是如此happy,后来还有人封装了可以选择各种图标和带隐藏详情的MessageBox,现在Unity3d UGui就没有了这样的好事情 ...

  2. Unity3D 装备系统学习Inventory Pro 2.1.2 基础篇

    前言 前一篇 Unity3D 装备系统学习Inventory Pro 2.1.2 总结 基本泛泛的对于Inventory Pro 这个插件进行了讲解,主要是想提炼下通用装备系统结构和类体系.前两天又读 ...

  3. Unity3D 装备系统学习Inventory Pro 2.1.2 总结

    前言 写在最前面,本文未必适合纯新手,但有一些C#开发经验的还是可以看懂的,虽然本人也是一位Unity3D新人,但是本文只是自己在学习Inventory Pro的学习总结,而不是教程,本人觉得要读懂理 ...

  4. 通用窗口类 Inventory Pro 2.1.2 Demo1(下续篇 ),物品消耗扇形显示功能

    本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...

  5. 通用窗口类 Inventory Pro 2.1.2 Demo1(下)

    本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...

  6. 通用窗口类 Inventory Pro 2.1.2 Demo1(中)

    本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...

  7. Inventory Pro 装备拾取的实现

    以后都按照插件使用,提出问题,回答问题的方式来进行总结和学习 效果图 1.运行相关的例子,场景中出现4个矩形,这4个矩形是用来模拟物品掉落的包裹,移动Player靠近物品 2.使用鼠标点击物品正方体, ...

  8. LINUX内核分析第一周学习总结——计算机是如何工作的

    LINUX内核分析第一周学习总结——计算机是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/ ...

  9. LINUX内核分析第二周学习总结——操作系统是如何工作的

    LINUX内核分析第二周学习总结——操作系统是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...

随机推荐

  1. 动态样式语言Less学习笔记

    介绍资料参见:http://www.bootcss.com/p/lesscss/ LESS 将 CSS 赋予了动态语言的特性,如 变量, 继承,运算, 函数. LESS 既可以在 客户端 上运行 (支 ...

  2. 使用 html5 postMessage 实现跨域

    英文原文 中文翻译 因为web的安全机制,浏览器的同源策略.在不同域之间做数据交换就会涉及到跨域.A域如果要实现向B域发关消息,多多少少要有对B域有一定控制权,最起码人家B域要接收你的消息啊. 最近发 ...

  3. 20145235李涛 《Java程序设计》第3周学习总结

    类与对象 定义类 类是对象的“设计图”,对象是类的实际类型.另外,定义时用class,建实例用new. 通过书上的代码才有所理解: class Clothes { String color; char ...

  4. bug 发表文章不显示图片

    bug 描述: 现象是我们这不能发布图片, 测试说患教方向是可以正常发布图片的(还是要感激测试,正是他们鞭策我们不断挑战困难,解决之,从而提高自己姿势水平). 图片没上传上去, 服务端协助查找发现没调 ...

  5. Distinctive Image Features from Scale-Invariant

    http://nichol.as/papers/Lowe/Distinctive Image Features from Scale-Invariant.pdf Abstract This paper ...

  6. 八 mybatis查询缓存(一级缓存,二级缓存)和ehcache整合

    1       查询缓存 1.1     什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存.

  7. Bash 快捷键大全

    快捷键的一些说明: CTRL=C:这个键是指PC键盘上的Ctrl键 ALT=M:这个键是PC键盘上的ALT键,如果你键盘上没有这个键,可以尝试使用ESC键代替 SHIFT=S:此键是PC上的Shift ...

  8. Python基本数据类型之int 、 float

    首先要明确的是:在python中,一切皆为对象. 从底层角度看,对象就是保存在内存中的一个数据块.从抽象层看,对象就是我们的代码模拟出的一个类的独立个体. 在python中变量不需要声明类型,也不需要 ...

  9. Qt配置信息设置(QSettings在不同平台下的使用路径)

    在Windows操作系统中,大多把配置文件信息写在注册表当中,或写在*.ini文件中,对于这两种操作都有相应的Windows API函数,在以前的文章中都提及过,这里就不多说了~ 在Qt中,提供了一个 ...

  10. TEST指令

    In the x86 assembly language, the TEST instruction performs a bitwise AND on two operands. The flags ...