Unity--UI框架

这篇具有很好参考价值的文章主要介绍了Unity--UI框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

先说明该UI框架的作用是用来控制UI面板之间的相互跳转的,使用了UI框架后,最大的用处就可以避免页面切换时复杂的操作,使用UI框架可以更好的管理UI页面,控制页面的显示和关闭也分别只由一个函数控制,极大的优化了代码

先看一张UI框架图

unity ui框架,ui

 结合上方的图,开始逐步制作UI框架

1.首先将UI的每个面板单独制作好,然后放在Resources文件中当做预制件(这样做的目的是为了在加载的时候就可以直接获取所有的面板(具体的实现是通过json解析在字典中),具体实现在下面)

2.创建一个枚举类型UIPanelType来保存对应面板

unity ui框架,ui

创建一个UIPanelType的json文件来保存面板和其对应的路径,注意这里的panelType的值一定要和枚举类型中的值保持一致,后面才能解析成功(需要通过Resources文件进行加载,因此要把它放在Resources文件中(可以再创建一个Resources文件))

unity ui框架,ui

 3.创建UIManager类,在该类中有三个功能,

 第一个功能:解析保存面板信息(通过解析json文件,然后存放在字典中,键为枚举的UIType,值为路径)(注意再解析json时,还需要一个数据类来接收,因此创建一个UIPanelInfo类来接收json的解析数据)注意这里的UIPanelInfo类就收是对应的[{},{}],但是可以看出来这个结构解析出来是一个数组或者list,但是unity解析只能解析出类(在从版本0.9.0开始,LitJSON支持将List对象序列化为JSON字符串,无需后面多创建的类)(litjson可以解析成数组,jsonutility不能解析成数组,具体参照文章一,文章二,文章三),因此需要将这个结构变为{ [ {},{},{} ] },将数组放在{}中,也就是还需要另一个类UIPanelTypeJson来表示最外层的{},注意在解析json时要使用字符串转枚举类型(因为不能直接解析成枚举)详情见字符串和枚举的转换(目前功能迭代只用uimanager),看完第一个功能看步骤4

Json文件的内容也随之改变成这样

unity ui框架,ui

第二个功能:创建并保存面板的实例(通过字典,键为面板的枚举,值为面板的BasePanel组件(可以通过子类对象来获取父类,用BasePanel来表示页面)),并创建方法(GetPanel())通过面板枚举获取该面板的父类(获取时有扩展字典的方法,请参照对Dictory的扩展)

第三个功能:管理保存所有显示的面板(通过栈的方式来保存显示的页面,在栈中都是显示的页面,栈顶的是可以操作的页面,也就是可以点击的页面,栈顶下面的页面是只显示不能操作),因此有创建栈,出栈和入栈(这两个最重要,因为所有页面的显示和关闭都是依靠这个出栈和入栈)的操作(还有下方步骤5的状态添加)

4.创建一个面板的公共基类BasePanel,让所有的面板都继承自BasePanel,因为每个面板的脚本都继承自BasePanel(这里的面板类是自己创建的),这样在实例化完每个面板后,可以直接通过面板的实例化对象.GetComponent<BasePanel>(),因此就可以添加UIManager类的第二个功能

面板就是这些,根据自己项目创建,在最下方附上面板类代码:

unity ui框架,ui

5.每个页面存在四种状态分别是OnEnter(页面显示出来),OnPause(页面暂停,因为弹出了其它页面),OnResume(页面继续,覆盖在该页面上的其它页面移除,恢复本页面的交互),OnExit(页面移除,移除该页面的显示)详情见流程图,注意因为每个页面都有这四种状态,因此把这四种状态定义在BasePanel中,通过虚方法,然后让每个子类重写,其中可以参考背包面板,都涉及到了这四个状态(如果像添加显示和退出动画,那就在OnEnter和OnExit中添加)

6.为各个面板上的各个按钮添加点击事件(在继承自BasePanel的脚本中进行),比如我现在有个主菜单,脚本为MainPanel,主菜单上有任务,背包,等各种按钮,这个时候UI框架就发挥作用了,可以直接在按钮点击事件中通过UIManager中的入栈方法来显示页面,用出栈方法来关闭页面等,记得也需要通面板的枚举类型(这里点击事件不能传枚举类型的参数,因此还是需要字符串转枚举类型这一步),就像这样

unity ui框架,ui

7.就是GameRoot部分,就用来启动UI框架

 下面附上代码部分,按照上方介绍的先后顺序

UIPanelType类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum UIPanelType
{
    ItemMessage,
    Knapsack,
    MainMenu,
    Shop,
    Skill,
    System,
    Task

}

UIPanelType的json文件:

{

  "infoList": [
 
    {
      "panelTypeString": "ItemMessage",
      "path": "UIPanel/ItemMessagePanel"
    },
    {
      "panelTypeString": "Knapsack",
      "path": "UIPanel/KnapsackPanel"
    },
    {
      "panelTypeString": "MainMenu",
      "path": "UIPanel/MainMenuPanel"
    },
    {
      "panelTypeString": "Shop",
      "path": "UIPanel/ShopPanel"
    },
    {
      "panelTypeString": "Skill",
      "path": "UIPanel/SkillPanel"
    },
    {
      "panelTypeString": "System",
      "path": "UIPanel/SystemPanel"
    },
    {
      "panelTypeString": "Task",
      "path": "UIPanel/TaskPanel"
    }
    ]
}

UIManager类(框架的核心内容)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIManager 
{
    #region 第一部分功能
    private Dictionary<UIPanelType, string> panelPathDict;//面板信息字典,用来存放面板所对应的Prefab路径
    private static UIManager _instance;
    public static UIManager Instance//单例模式不是这章重点,不做讲解
    {
        get 
        {
            if(_instance == null)
            {
                _instance = new UIManager();
            }
            return _instance; 
        }

    }

    private UIManager()
    {
        ParseUIPanelTypeJson();
    }

    [Serializable]
    class UIPanelTypeJson
    {
        public List<UIPanelInfo> infoList;
    }
    private void ParseUIPanelTypeJson()//解析json文件
    {
        panelPathDict=new Dictionary<UIPanelType, string>();
        TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//json文件在unity中是属于TextAsset类,因此用这个类来接收
        //这里应该也可以用txt文件保存json数据,然后用读取流的形式读取
        UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//注意,如果这样的话就会报错,原因在解析json的时候,并不能解析成枚举类型,因为在UIPanelInfo类的panelType成员为枚举类型,因此就会报解析错误
        //将解析的json信息,填入panelPathDict字典中,键对应面板枚举值,值对应面板路径
        foreach (UIPanelInfo info in jsonObject.infoList)
        {
            panelPathDict.Add(info.panelType, info.path);

        }
    }
    #endregion

    #region 第二部分功能
    private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的脚本组件(继承自BasePanel的)
    private Transform canvasTransform;//获取面板
    public Transform CanvasTransform
    {
        get
        {
            if(canvasTransform == null)
            {
                canvasTransform = GameObject.Find("Canvas").transform;
            }
            return canvasTransform;
        }
    }
    private BasePanel GetPanel(UIPanelType panelType)//根据面板的枚举类型,得到实例化的面板
    {
        if (panelDict == null)//判断字典是否为空
        {
            panelDict = new Dictionary<UIPanelType, BasePanel>();//如果为空就创建字典
        }
        // BasePanel panel;
        //panelDict.TryGetValue(panelType, out panel);
        BasePanel panel = panelDict.TryGet(panelType);//字典扩展方法
        if (panel == null)//判断是否得到该面板
        {
            //如果为空就根据panelPathDict字典中路径实例化面板,并保存在panelDict中和放在Canvas画布下
            //string path;
            //  panelPathDict.TryGetValue(panelType, out path);
            string path = panelPathDict.TryGet(panelType);

            //GameObject instPanelPrefab = Resources.Load<GameObject>(path);//这是通过动态加载的形式获取到了面板预制件的引用
            //GameObject instPanel=GameObject.Instantiate(instPanelPrefab);//因为没有继承自MonoBehaviour,所有需要加GameObject
            GameObject instPanel = GameObject.Instantiate(Resources.Load<GameObject>(path));//这是简洁写法
            panelDict.Add(panelType, instPanel.GetComponent<BasePanel>());
            instPanel.transform.SetParent(CanvasTransform, false);//放在画布下
            return instPanel.GetComponent<BasePanel>();
        }
        else
        {
            return panel;
        }
    }
    #endregion

    #region 第三部分功能
    private Stack<BasePanel> panelStack;
    //都是根据页面的枚举类型来获取到页面,才能方便出栈入栈操作
    public void PushPanel(UIPanelType panelType )//把页面入栈,把某个页面显示在界面上
    {
        if(panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }
        //判断栈里面是否有页面
        if(panelStack.Count > 0 )
        {
            BasePanel topPanel=panelStack.Peek();//这是获取到栈顶元素Pop是出栈
            topPanel.OnPause();//将栈顶元素暂停
        }
        BasePanel panel=GetPanel(panelType);//这里就用到了第二部分功能中的获取面板的方法
        panelStack.Push(panel);//入栈
        panel.OnEnter();//这是新的栈顶元素显示
        //注意这里都只是统一调用BasePanel中的方法,因为BasePanel中的方法是虚方法没有具体实现,需要在子类中具体实现后,就能有具体的功能
    }
    public void PopPanel()//把页面出栈, 把页面从界面上移除
    {

        if (panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }
        if (panelStack.Count <= 0)
        {
            return;
        }
        BasePanel topPanel=panelStack.Pop();//栈顶出栈
        topPanel.OnExit();
        if (panelStack.Count <= 0)//这是如果关闭页面后下方还有页面就需要将栈顶页面恢复
        {
            return;
        }
        BasePanel topPanel2 = panelStack.Peek();
        topPanel2.OnResume();//这里同理,具体实现都是在子类脚本中重新,这里只负责调用
    }
    #endregion
}

 UIPanelInfo类:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class UIPanelInfo:ISerializationCallbackReceiver
{
    [NonSerialized]//因为json解析成不了枚举类型,因此就不解析成它
    public UIPanelType panelType;//注意,因为这个类时用来接收解析的json的,因此变量成员名一定要和json中的名字要相同

    public string panelTypeString;//json解析成字符串

    public string path;

    public void OnAfterDeserialize()//反序列化成功之后调用
    {
        // throw new NotImplementedException();
        //这里就是将字符串转化为枚举类型的过程,放在反序列化成功之后执行
        UIPanelType Type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
        panelType = Type;
    }

    public void OnBeforeSerialize()//反序列化成功之前调用
    {
      //  throw new NotImplementedException();
    }
}

DictionaryExtension类:字典扩展类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class DictionaryExtension //字典扩展类,对字典使用方法进行扩展
{
   
    public static Tvalue TryGet<Tkey,Tvalue>(this Dictionary<Tkey,Tvalue> dict,Tkey key)
    {
        Tvalue value;
        dict.TryGetValue(key, out value);
        return value;
    }
}

BasePanel类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BasePanel : MonoBehaviour
{
    /// <summary>
    /// 界面被显示出来
    /// </summary>
    public virtual void OnEnter()
    {

    }

    /// <summary>
    /// 界面暂停
    /// </summary>
    public virtual void OnPause()
    {

    }

    /// <summary>
    /// 界面继续
    /// </summary>
    public virtual void OnResume()
    {

    }

    /// <summary>
    /// 界面不显示,退出这个界面,界面被关系
    /// </summary>
    public virtual void OnExit()
    {

    }
}

GameRoot类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameRoot : MonoBehaviour
{
    private void Start()
    {
        UIManager.Instance.PushPanel(UIPanelType.MainMenu);
    }
}

展示一部分项目面板的类(继承自BasePanel),不属于框架部分

主菜单类(挂载在主菜单页面上),其中的点击事件方法,如点击主菜单中的任务按钮显示任务页面就需要自己写函数再绑定到按钮上,这里就是OnClickPushPanel()函数

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MainMenuPanel : BasePanel
{
    private CanvasGroup canvasGroup;
    private void Start()
    {
        canvasGroup = GetComponent<CanvasGroup>();
    }
    //这里是重新的MainMenuPanel页面枚举类型对应的BasePanel中的OnPause方法,因为在UIManager中的panelDict保存了枚举类型对应的BasePanel
    //因此这里的调用流程就是先调用下方的PushPanel,在PushPanel函数中根据panelType获取到BasePanel,然后执行 panel.OnPause();等方法,又因为
    //获取到的BasePanel就是 MainMenuPanel的父类这个 BasePanel,因此调用的panel.OnPause();方法就是执行的下方的内容
    public override void OnPause()//这里具体的暂停方法就是取消掉主页面中的所有可交互的东西,因此可以用CanvasGroup控制,详细使用方法见手册
    {
       canvasGroup.blocksRaycasts = false;//暂停该页面,让鼠标不再和该页面交互
    }
    public override void OnResume()
    {
        canvasGroup.blocksRaycasts = true;
    }
    public void OnClickPushPanel(string panelString )//定义点击事件,将要显示的页面添加进栈中,这里是因为要复用才通过字符串形式转换
    {
        UIPanelType panelType=(UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelString);
        UIManager.Instance.PushPanel( panelType );
    }
}

再比如背包类:

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KnapsackPanel : BasePanel
{
    private CanvasGroup canvasGroup;
    private void Start()
    {
        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
    }
    public override void OnEnter()
    {
        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();//这里是防止面板在实例化出来后,立马调用了OnEnter()方法,导致 canvasGroup还没赋值而报空
        canvasGroup.alpha = 1;
        canvasGroup.blocksRaycasts = true;
        // gameObject.SetActive(true); //这里也可以直接可以设置页面的active来表示进入和退出

        Vector3 temp = transform.localPosition;
        temp.x = 1300;
        transform.localPosition = temp;
        transform.DOLocalMoveX(0, 0.5f);
    }
    public override void OnExit()//关闭该页面的具体实现内容
    {
        //canvasGroup.alpha = 0;
        canvasGroup.blocksRaycasts = false;
        transform.DOLocalMoveX(1800, 0.5f).OnComplete(() => { canvasGroup.alpha = 0; });
        // gameObject.SetActive(false);
    }
    public override void OnPause()
    {
        canvasGroup.blocksRaycasts = false;
    }
    public override void OnResume()
    {
        canvasGroup.blocksRaycasts = true;
    }
    public void OnClosePanel()
    {
        UIManager.Instance.PopPanel();
    }
    public void OnItemButtonClick()//点击显示物品按钮
    {
        UIManager.Instance.PushPanel(UIPanelType.ItemMessage);
    }
}

因此总结一下,属于UI框架的类就7个

unity ui框架,ui

其余的像KnapsackPanel 这些类都是依据项目变化而变化的文章来源地址https://www.toymoban.com/news/detail-734874.html

到了这里,关于Unity--UI框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • unity 前端场景搭建UI框架的设计

    基础组件库:设计一套基础组件库,包括常用的 UI 控件,如文本、按钮、图像等,组件库的设计应该尽量简单易用,方便开发者快速搭建 UI 界面。 布局管理器:为了方便 UI 界面的排版,需要设计一套布局管理器,如水平布局、垂直布局、网格布局等,布局管理器应该支持自

    2024年02月16日
    浏览(46)
  • Unity–UI框架-Canvas-EventSystem-Panel

    画布canvas是控制一组UI元素如何呈现的组件, 所有UI元素必须是画布的子项 ,场景中可以有多个画布,但是UI元素至少需要一个可用的画布canvas, 每个画布都有不同的渲染模式,渲染模式可以使用Render Mode进行设置, 1.Screen Space-Overlay 其中,第一种是最常见的渲染模式,也是

    2024年02月03日
    浏览(53)
  • Unity Xlua热更新框架(五):Lua和UI管理

    :::info Lua存在两种加载器,一种默认加载器(env.DoString(\\\"require(‘test’)\\\"直接用了默认加载其),直接调用StreamingAssets中的脚本);一种是自定义加载器(env.AddLoader(Envpath)),优先于默认加载器(下文DoString就是从自定义加载器的路径读取的),并且当Lua代码执行require函数时,

    2024年02月06日
    浏览(55)
  • 【Unity小技巧】手戳一个简单易用的游戏UI框架(附源码)

    参考原视频链接: 【视频】:https://www.bilibili.com/video/BV1zT411b7L3/ 注意 :本文为学习笔记记录,推荐支持原作者,去看原视频自己手敲代码理解更加深入 开发一款游戏美术成本是极其高昂的,以我们常见的宣传片CG为例,动辄就要成百上千万的价格,因此这种美术物料一般只

    2024年02月11日
    浏览(52)
  • Unity用鼠标拖拽UI,UI跟随鼠标移动

    先上效果 继承几个拖拽的接口 IBeginDragHandler, IDragHandler,IEndDragHandler 计算下偏移量,转换下坐标系 限制下可拖拽的范围,我设置的是canvas的大小 欢迎大佬多多来给萌新指正,欢迎大家来共同探讨。 如果各位看官觉得文章有点点帮助,跪求各位给点个“一键三连”,谢啦~ 声明

    2024年02月06日
    浏览(82)
  • Unity UI 优化技巧

    问题:当 UI Canvas 的任何元素发生变化时,都会影响整个 Canvas。 Canvas 是 Unity UI 的重要组成部分。它创建一个网格来表示放置在其顶部的 UI 元素,在 UI 元素更改时重建网格,并调用 GPU 来渲染实际的用户界面。 创建这些网络可能非常昂贵。UI 元素应该写在组件中,以便可以

    2024年04月14日
    浏览(36)
  • unity 拖拽UI

    我们经常会使用拖拽UI的效果,untiy 为拖拽事件也提供了现成的API,我们只要简单的实现几个接口即可 我们用两种方式来实现拖拽代码,一种是使用MonoBehaviour里的方法,一种是实现UI事件接口,但不论是那种方法,拖拽的逻辑都是没有区别的。以下为拖拽核心代码 实现一:使

    2024年02月11日
    浏览(44)
  • Unity UI规范

    参考自:Unity 程序员UI编码规范 - 知乎 (zhihu.com) 大部分情况下,我们一套UI,能基本全部适配好,Unity UI让建立一个可以适应不同分辨率和宽高比屏幕调整位置和缩放UI很简单。然而,一种设计不总适合所有平台,所以创建多种版本的UI(或者说部分UI)来让每个设备上都有最佳

    2024年03月17日
    浏览(47)
  • Unity UI——UGUI

    包名: com.unity.ugui Canvas :   容纳所有UI元素的区域。   一种带有画布组件的游戏对象。   使用EventSystem对象来协助消息系统。 绘制顺序: 按照在 Hierarchy 中显示的顺序绘制,由上到下。 如果两个 UI 元素重叠,则后一个元素将显示在前一个元素之上 渲染模式:(Render Mode)

    2024年04月28日
    浏览(40)
  • Unity学习记录——UI设计

    ​ 本文是中山大学软件工程学院2020级3d游戏编程与设计的作业8 1.相关资源 ​ 本次项目之中的人物模型来自Starter Assets - Third Person Character Controller | 必备工具 | Unity Asset Store ​ 此处使用了以下路径的 PlayerArmature 预制,这个预制人物模型可以进行行走奔跑跳跃等动作,很适合

    2024年02月04日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包