Unity3D通⽤提⽰窗⼝实现分析(InventoryPro学习总结)
背景
游戏中的UI系统或者叫做GUI窗⼝系统主要有:主要装备窗⼝(背包,⾓⾊窗⼝也是⼀种特殊窗⼝)、确实提⽰窗⼝(如购买确认)、信息提⽰窗⼝(⼀遍没有按钮,ContexntMenu)和特殊窗⼝(聊天记录或者技能树),前篇已经介绍分析了Inventory Pro确认提⽰窗⼝的设计和实现⽅式,这篇主要讲⼀下信息提⽰窗⼝的实现。本以为提⽰窗⼝是⽐较简单的,毕竟没有按钮事件交互的问题,但是分析了下源代码还是让我有些惊讶,插件作者在提⽰窗⼝中考虑到了性能问题,由于本⼈⼀直在PC端开发程序没有移动端的经验,所以在移动端对于性能优化还是⽐较关注的。
声明:本⽂⾸发于蛮⽜,次发博客园,本⼈原创。,
插件效果及使⽤
左下⾓即为信息提⽰窗⼝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;
unity3d animationusing 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 = 2,
Medium = 4,
Long = 6,
ExtraLong = 8
}
[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 = 50;
///<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>(8);
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.ssage))
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.SetMessage(message);
if (onNewMessageAudioClip != null)
InventoryUIUtility.AudioPlayOneShot(onNewMessageAudioClip);
messages.Add(item);
}
if (messages.Count > maxMessages)
{
StartCoroutine(DestroyAfter(messages[0], messages[0].hideAnimation.length));
messages[0].Hide();
messages.RemoveAt(0);
}
if (scrollbarAtBottom)
scrollRect.verticalNormalizedPosition = 0.0f;
}
protected virtual IEnumerator DestroyAfter(NoticeMessageUI item, float time)
{
yield return new WaitForSeconds(time);
pool.Destroy(item);
}
}
}
View Code
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)
{
= string.Format(message.title, message.parameters);
lor = lor;
}
}
else
title.gameObject.SetActive(false);
< = string.ssage, message.parameters);
if (this.time != null)
{
= dateTime.ToShortTimeString();
lor = lor;
}
}
public virtual void Hide()
{
// Already hiding
if (isHiding)
return;
isHiding = true;
if (hideAnimation != null)
animator.Play(hideAnimation.name);
}
public void Reset()
{
isHiding = false;
}
}
}
View Code
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
///</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.time = time;
this.parameters = parameters;
}
public override void Show(params System.Object[] param)
{
base.Show(param);
this.time = DateTime.Now;
if (ice != null)
ice.AddMessage(this);
}
}
}
View Code
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论