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 ...
随机推荐
- C#winform中ListView的使用
使用ListView模仿Windows系统的资源管理器界面,实现文件(夹)的浏览.重命名.删除及查询等功能,主要功能界面展示如下: 1.MainForm.cs及MainForm.Designer.cs ...
- PHP学习(三)----面向对象
首先,还是建立一个好的理解模型: 1.什么是面向对象? 面向对象分为两个部分,那就是:什么是对象和什么是面向? 什么是对象: 对象的出现就是为了用代码更好的绘制我们现有的世界.那到底什么是对象呢? 一 ...
- px_ipc_name.c
/* include px_ipc_name */ #include "unpipc.h" char * px_ipc_name(const char *name) { char ...
- nginx搭建http和rtmp协议的流媒体服务器
nginx搭建http和rtmp协议的流媒体服务器 时间:2013-09-23 23:52来源:佚名 作者:本站 举报 点击:232次 实验目的:让Nginx支持flv和mp4格式文件,同时支持Rtm ...
- apex-utility-ai-unity-survival-shooter
The AI has the following actions available: Action Function Shoot Fires the Kalashnikov Reload Reloa ...
- vbox共享文件 挂载
环境:主机操作系统是Windows 7,虚拟机是open suse 12.0,虚拟机是VirtualBox 4.2.1. 1. 安装增强功能包(Guest Additions) 安装好open sus ...
- HBase学习笔记-高级(一)
HBase1. hbase.id记录了集群的唯一标识:hbase.version记录了文件格式的版本号2. split和.corrupt目录在日志分裂过程中使用,以便保存一些中间结果和损坏的日志在表目 ...
- 修改Wordpress目录
安装完wordpress,此时访问地址为:http://localhost/wordpress 1.不想让人知道我的文件目录,于是我把原来的wordpress改为w, 2.后台设置 3.WordPre ...
- Arcgis for Javascript API下类似于百度搜索A、B、C、D marker的实现方式
原文:Arcgis for Javascript API下类似于百度搜索A.B.C.D marker的实现方式 多说无益,首先贴两张图让大家看看具体的效果: 图1.百度地图搜索结果 图2.Arcgis ...
- CentOS7 + linux kernel 3.10.94 compile 简记
Linux kernel 一直以其开源著称,可以自己编译选择合适的模块,针对特定的系统可以有不同的编译选项 来源 此次编译的内核版本为3.10.94,从官网www.kernel.org下载而来,自己虚 ...