Unity-代码分离的UI系统


代码与资源分离是游戏程序设计的核心思想之一,被广大游戏公司多采用,相比于乱成一团的编码方式,它至少有一下几点优势
1.在游戏公司里,美术人员负责界面的设计和制作,程序人员负责界面功能的实现。代码分离有利于美术人员和程序人员的分工合作,两者互相配合,有互不干扰。
2.有利于代码的重复使用,功能相同但外观不同的界面只要一套代码即可。
3.为游戏的热更新提供可能性,若游戏需要更新界面团,只需要下载新的界面资源即可。

面板系统的设计

这套界面系统有面板基类(PanelBase)、面板管理器(PanelMgr)和多个具体的面板组成。所有面板都继承自PanelBase,而PanelMgr提供打开某个面板、关闭某个面板的方法。

先制作两个面板作为例子一个是开始界面(TitlePanel),另一个是游戏介绍(InfoPanel),如下图.
在这里插入图片描述
在这里插入图片描述
把这两个面板做成预设,放入Resources文件夹下。

面板基类的实现(PanelBase)

PanelBase是一个面板基类,所有的面板逻辑都要继承它,一些设计要点如下。
1.面板的资源(如上面做成的两个面板预设)称为皮肤(skin,为GameObject类型),皮肤的路径称为skinPath。面板管理器会根据skinPath去实例化skin。
2.某些面板有层级关系,比如提示框总要覆盖普通面板。可定义PanelLayer类型的枚举,来指定面板的层级。
3.某些面板需要通过参数来确定他的表现形式。比如提示框,显示的内容由调用它的语句来指定。定义Object类型的变量args可用于接收PanelMgr传来的参数。
下图为面板的生命周期
在这里插入图片描述

阶段说明
InitInit是面板初始化阶段,用于处理args参数
OnShowing面板显示前将会触发OnShowing,可用于处理面板中的监听事件
OnShowed面板显示后将会触发OnShowed
Update每帧更新
OnClosing关闭面板前调用OnClosing
OnClosed面板关闭后调用OnClosed

代码(PanelBase)

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

public class PanelBase : MonoBehaviour
{
    //皮肤路径
    public string skinPath;
    //皮肤
    public GameObject skin;
    //层级
    public PanelLayer layer;
    //面板参数
    public object[] args;
    
    #region 生命周期
    //初始化
    public virtual void Init(params object[] args)
    {
        this.args = args;
    }
    //开始面板前
    public virtual void OnShowing() { }
    //显示面板后
    public virtual void OnShowed() { }
    //帧更新
    public virtual void Update() { }
    //关闭前
    public virtual void OnClosing() { }
    //关闭后
    public virtual void OnClosed() { }
    
    #endregion
    
    #region 操作
    protected virtual void Close()
    {
        string name = this.GetType().ToString();
        PanelMgr.instance.ClosePanel(name);
    }
    
    #endregion
}

面板管理器PanelMgr

顾明思议,面板管理器的功能就是管理面板,他有一下三个功能。
1.层级管理
2.打开面板
3.关闭面板
在Unity中创建Canvas,并在画布上面添加名为Panel和Tips的两个空物体,有系统的所有面板都会放在这两个空物体下
在这里插入图片描述
代码

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

public class PanelMgr : MonoBehaviour
{
    public static PanelMgr instance;
    //画板
    private GameObject canvas;
    //面板  用于存放已经打开的面板
    public Dictionary<string, PanelBase> dict;
    //层级  存放各个层级所对应的父物体
    private Dictionary<PanelLayer, Transform> layerDict;
    
    //开始
    private void Awake()
    {
        instance = this;
        InitLayer();
        dict = new Dictionary<string, PanelBase>();
    }
    
    private void InitLayer()
    {
        canvas = GameObject.Find("Canvas");
        if (canvas == null)
            Debug.LogError("panelMgr.InitLayerfail,Canvas is null");
        //各个层级
        layerDict = new Dictionary<PanelLayer, Transform>();
        foreach(PanelLayer pl in Enum.GetValues(typeof(PanelLayer)))
        {
            string name = pl.ToString();
            Transform transform = canvas.transform.Find(name);
            layerDict.Add(pl, transform);
        }
    }
    
    //打开面板
    public void OpenPanel<T>(string skinpath,params object[] args) where T:PanelBase
    {
        //已经打开
        string name = typeof(T).ToString();
        if (dict.ContainsKey(name))
            return;
        //面板脚本
        PanelBase panel = canvas.AddComponent<T>();
        panel.Init(args);
        dict.Add(name, panel);
        //加载皮肤
        skinpath = (skinpath != "" ? skinpath : panel.skinPath);
        GameObject skin = Resources.Load<GameObject>(skinpath);
        if (skin == null)
            Debug.LogError("PanelMgr.OperPanelfail,skin is null,skinpath= " + skinpath);
        panel.skin = (GameObject)Instantiate(skin);
        //坐标
        Transform skinTrans = panel.skin.transform;
        PanelLayer layer = panel.layer;
        Transform parent = layerDict[layer];
        skinTrans.SetParent(parent, false);
        //panel的生命周期
        panel.OnShowing();
        panel.OnShowed();
    }
    
    //关闭面板
    public void ClosePanel(string name)
    {
        PanelBase panel = dict[name];
        if (panel == null)
            return;
        panel.OnClosing();
        dict.Remove(name);
        panel.OnClosed();
        Destroy(panel.skin);
        Destroy(panel);
    }
}

PanelLayer

public enum PanelLayer
{
   Panel,
   Tips,
}

上面就是这个Ui系统的基本代码了
下面就拿上面创建好的两个面板为例子。

例子

首先是创建好的TitlePanel面板
创建这样一个脚本

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

public class TitlePanel : PanelBase
{
    private Button startBtn;
    private Button infoBtn;
    
    #region 生命周期
    public override void Init(params object[] args)
    {
        base.Init(args);
        skinPath = "TitlePanel";
        layer = PanelLayer.Panel;
    }
    public override void OnShowing()
    {
        base.OnShowing();
        Transform skintrans = skin.transform;
        startBtn = skintrans.Find("btn_Start").GetComponent<Button>();
        infoBtn = skintrans.Find("btn_Info").GetComponent<Button>();
        startBtn.onClick.AddListener(OnStartClick);
        infoBtn.onClick.AddListener(OnInfoClick);
    }
    #endregion
    
    public void OnStartClick()
    {
        //开始游戏
        //PanelMgr.instance.OpenPanel<OptionPanel>("");
    }
    
    public void OnInfoClick()
    {
        PanelMgr.instance.OpenPanel<InfoPanel>("");
    }
}

不需要提前挂在物体上。
对于InfoPanel

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

public class InfoPanel : PanelBase
{
    private Button closeBtn;
    
    #region 生命周期
    
    public override void Init(params object[] args)
    {
        base.Init(args);
        skinPath = "InfoPanel";
        layer = PanelLayer.Panel;
    }
    public override void OnShowing()
    {
        base.OnShowing();
        Transform skintrans = skin.transform;
        closeBtn = skintrans.Find("btn_Close").GetComponent<Button>();
        closeBtn.onClick.AddListener(OnCloseClick);
    }
    
    #endregion
    
    public void OnCloseClick()
    {
        Close();
    }
}

界面系统的调用

最后编写如下脚本调用TitlePanel就行

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Root : MonoBehaviour
{
    private void Start()
    {
        PanelMgr.instance.OpenPanel<TitlePanel>("");
    }
}

在场景中创建一个名为Root的空物体挂在Root和PanelMgr
在这里插入图片描述
这样就完成了。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值