Unity3D 通用提示窗口实现分析(Inventory Pro学习总结)
背景
游戏中的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学习总结)的更多相关文章
- Unity3d UGUI 通用Confirm确认对话框实现(Inventory Pro学习总结)
背景 曾几何时,在Winform中,使用MessageBox对话框是如此happy,后来还有人封装了可以选择各种图标和带隐藏详情的MessageBox,现在Unity3d UGui就没有了这样的好事情 ...
- Unity3D 装备系统学习Inventory Pro 2.1.2 基础篇
前言 前一篇 Unity3D 装备系统学习Inventory Pro 2.1.2 总结 基本泛泛的对于Inventory Pro 这个插件进行了讲解,主要是想提炼下通用装备系统结构和类体系.前两天又读 ...
- Unity3D 装备系统学习Inventory Pro 2.1.2 总结
前言 写在最前面,本文未必适合纯新手,但有一些C#开发经验的还是可以看懂的,虽然本人也是一位Unity3D新人,但是本文只是自己在学习Inventory Pro的学习总结,而不是教程,本人觉得要读懂理 ...
- 通用窗口类 Inventory Pro 2.1.2 Demo1(下续篇 ),物品消耗扇形显示功能
本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...
- 通用窗口类 Inventory Pro 2.1.2 Demo1(下)
本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...
- 通用窗口类 Inventory Pro 2.1.2 Demo1(中)
本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...
- Inventory Pro 装备拾取的实现
以后都按照插件使用,提出问题,回答问题的方式来进行总结和学习 效果图 1.运行相关的例子,场景中出现4个矩形,这4个矩形是用来模拟物品掉落的包裹,移动Player靠近物品 2.使用鼠标点击物品正方体, ...
- LINUX内核分析第一周学习总结——计算机是如何工作的
LINUX内核分析第一周学习总结——计算机是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/ ...
- LINUX内核分析第二周学习总结——操作系统是如何工作的
LINUX内核分析第二周学习总结——操作系统是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...
随机推荐
- ecshop数据库操作函数
ecshop数据库操作函数 分类: ecshop和dede2013-09-23 14:02 1716人阅读 评论(0) 收藏 举报 本章我们将结合eschop二次开发一些常见的开发例子.来谈谈ecsh ...
- java MVC架构-spring mvc,struct2(理解)
MVC架构实现基础: 基于filter或者servlet实现请求地址分析,如果需要控制类处理请求,则调用相应的控制类.调用控制类时,根据配置文件初始化控制类相关的参数.数据库连接可持久化存在.控制类处 ...
- ContentType Office
Office对应ContentType 当从浏览器返回一个文件时,需要指定ContentType,以下是Office2007对应的值: "application/vnd.openxmlfor ...
- Defining Stored Programs
ok DROP PROCEDURE IF EXISTS truncate_insert_rank_month; DELIMITER /w/ CREATE PROCEDURE truncate_inse ...
- ubuntu 工作区中拖动一个窗体到另一个工作区就卡住回不到桌面了
ubuntu 工作区中拖动一个窗体到另一个工作区就卡住回不到桌面了 解决方法: 按 alt + 回车 键直接就返回去了
- 在Delphi中如何控制其它应用程序窗口
在编写Delphi的应用程序中,常常涉及对其它Windows应用程序的操作.例如,在数据库的管理系统中,财务人员需要使用计算器,即可调用Windows内含的计算器功能,若每次使用,均通过“开始/程序/ ...
- 打造 PHP版本 1password
以前注册很多网站密码都使用简单密码,但是由于今年频繁曝出密码不安全问题,所以要使用更加复杂的密码.但是好多个账号,密码也不能设置成一样的,防止一个被盗全部不安全了,记密码就成了意见很头疼的事情. 在手 ...
- [收藏]Asp.net MVC生命周期
一个HTTP请求从IIS移交到Asp.net运行时,Asp.net MVC是在什么时机获得了控制权并对请求进行处理呢?处理过程又是怎样的? 以IIS7中asp.net应用程序生命周期为例,下图是来自M ...
- pro7
1.本次课学习到的知识点: 函数的作用 确定函数的功能 定义函数 调用函数 2.实验过程中遇到的问题及解决方法: 定义函数时 变量的定义会出现混乱 通过看例题 多练习 逐渐熟悉 需从数学角度解决问题时 ...
- Block的简单使用
代码块本质上是和其他变量类似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你可以像调用其他标准函数一样,传入参数,并得到返回值. 代码块本质上是变量,只不过它存储的数据是一个函数体,因此名字 ...