【Unity小技巧】手戳一个简单易用的游戏UI框架(附源码)

文章目录

前言

开发一款游戏美术成本是极其高昂的,以我们常见的宣传片CG为例,动辄就要成百上千万的价格,因此这种美术物料一般只会放在核心剧情节点,引爆舆论,做高潮展示!

而另外一种表意方武:则是通过玩法设计,层层导玩家深入探讨游戏世界,这里则需要策划的精心设计和程序的秃头爆肝,因此对于绝大多数游戏而言,选择UI来进行剧情展示、玩法交互和核心表达才是最物美价廉的选择。

unity在4.6版本之后,引入了自己的界面显示系统,全称unity graphic user interface,即我们所熟知的ugui,毕竟是unity的亲儿子,这个系统一经推出,就与其灵活快速可视化。

迅速抢占用户市场,逐渐成为unity ui的主流系统,但是它也并不是完美的,对于开发人员来说,使用这套系统往往需要面对如下困境,比如缺乏跨场景的u管理器,界面的上下层关系紊乱三,界面之间的通信手段贫乏等等,上述几个问题大大影响到我们的开发效率。

上述几个问题大大影响到我们的开发效率,因此针对上述问题,我在此分享一套简单易用的框架,这套框架是b站up小棋参考了网上的大量资料,并结合个人工作经验,一个代码一个代码敲出来的,绝对是良心干货,所以废话少说,让我们开始吧!

整套框架分为三大部分

在游戏运行时,我们会把界面配置关系配置在UIManager中,当我们创建好了一个界面,比如用户列表界面,那么我们就给这个界面取一个名,并且把它放在prefab下的一个文件夹,并且配置在这个配置关系表中,后面我们在新创建一些界面,比如菜单界面,也可以把它配置在这个界面关系配置表中

我总共使用了三个界面来举例,最后一个界面是创建新用户的一个界面,总共配了三个配置关系,包含名称、路径和预制体

配置完成后,我们给UIManager提供两个最重要的方法,一个是OpenPanel和ClosePanel,用于打开界面和关闭界面,介绍完框架的设计

我们就可以开始书写代码了

打开游戏编辑器,我们首先在一个空场景中创建canvas画布,并且将其参数设置为合适的范围和类型,这个主要根据自己项目来设置

接着我们使用工程中的ui素材拼接界面,我这里分了如下几个界面,主菜单界面,用户列表界面和新用户界面等等,最后把这些界面拖拽到resources下的某个目录,将其制作成预制件执行

加入代码后,最终的运行效果就是这样的

框架代码

UIManager.cs

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
// 用户界面管理器
public class UIManager
{
    // 单例模式的实例
    private static UIManager _instance;
    // 用户界面的根节点
    private Transform _uiRoot;
    // 路径配置字典
    private Dictionary<string, string> pathDict;
    // 预制件缓存字典
    private Dictionary<string, GameObject> prefabDict;
    // 已打开界面的缓存字典
    public Dictionary<string, BasePanel> panelDict;

    // 获取单例模式的实例
    public static UIManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new UIManager();
            }
            return _instance;
        }
    }

    // 获取用户界面的根节点
    public Transform UIRoot
    {
        get
        {
            if (_uiRoot == null)
            {
                if (GameObject.Find("Canvas"))
                {
                    _uiRoot = GameObject.Find("Canvas").transform;
                }
                else
                {
                    _uiRoot = new GameObject("Canvas").transform;
                }
            };
            return _uiRoot;
        }
    }

    // 用户界面管理器的构造函数
    private UIManager()
    {
        InitDicts();
    }

    // 初始化字典
    private void InitDicts()
    {
        prefabDict = new Dictionary<string, GameObject>();
        panelDict = new Dictionary<string, BasePanel>();

        pathDict = new Dictionary<string, string>()
        {
            {UIConst.AllCardPanel, "Menu/AllCardPanel"},
            {UIConst.MenuPanel, "Menu/MenuPanel"},
            {UIConst.MainMenuPanel, "Menu/MainMenuPanel"},
            {UIConst.MenuTipPanel, "Menu/MenuTipPanel"},
            {UIConst.NewUserPanel, "Menu/NewUserPanel"},
            {UIConst.UserPanel, "Menu/UserPanel"},
            {UIConst.ReNameUserPanel, "Menu/ReNameUserPanel"},
        };
    }

    // 打开面板
    public BasePanel OpenPanel(string name)
    {
        BasePanel panel = null;
        // 检查是否已打开
        if (panelDict.TryGetValue(name, out panel))
        {
            Debug.Log("界面已打开: " + name);
            return null;
        }

        // 检查路径是否配置
        string path = "";
        if (!pathDict.TryGetValue(name, out path))
        {
            Debug.Log("界面名称错误,或未配置路径: " + name);
            return null;
        }

        // 使用缓存预制件
        GameObject panelPrefab = null;
        if (!prefabDict.TryGetValue(name, out panelPrefab))
        {
            string realPath = "Prefab/Panel/" + path;
            panelPrefab = Resources.Load<GameObject>(realPath) as GameObject;
            prefabDict.Add(name, panelPrefab);
        }

        // 打开界面
        GameObject panelObject = GameObject.Instantiate(panelPrefab, UIRoot, false);
        panel = panelObject.GetComponent<BasePanel>();
        panelDict.Add(name, panel);
        panel.OpenPanel(name);
        return panel;
    }

    // 关闭面板
    public bool ClosePanel(string name)
    {
        BasePanel panel = null;
        if (!panelDict.TryGetValue(name, out panel))
        {
            Debug.Log("界面未打开: " + name);
            return false;
        }

        panel.ClosePanel();
        // panelDict.Remove(name);
        return true;
    }

    // 显示提示
    public void ShowTip(string tip)
    {
        MenuTipPanel menuTipPanel = OpenPanel(UIConst.MenuTipPanel) as MenuTipPanel;
        menuTipPanel.InitTip(Globals.TipCreateOne);
    }
}

// 用户界面常量
public class UIConst
{
    // menu panels
    public const string AllCardPanel = "AllCardPanel";
    public const string MenuPanel = "MenuPanel";
    public const string MainMenuPanel = "MainMenuPanel";
    public const string MenuTipPanel = "MenuTipPanel";
    public const string NewUserPanel = "NewUserPanel";
    public const string UserPanel = "UserPanel";
    public const string ReNameUserPanel = "ReNameUserPanel";
}

BasePanel.cs

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BasePanel : MonoBehaviour  // 基础面板类,继承自MonoBehaviour
{
    protected bool isRemove = false;  // 是否移除,默认为否
    protected new string name;  // 面板名称

    public virtual void SetActive(bool active)  // 设置面板活动状态
    {
        gameObject.SetActive(active);  // 设置游戏对象活动状态
    }

    public virtual void OpenPanel(string name)  // 打开面板方法
    {
        this.name = name;  // 设置面板名称
        SetActive(true);  // 设置面板为活动状态
    }

    public virtual void ClosePanel()  // 关闭面板方法
    {
        isRemove = true;  // 设置移除状态为是
        SetActive(false);  // 设置面板为非活动状态
        Destroy(gameObject);  // 销毁游戏对象
        if (UIManager.Instance.panelDict.ContainsKey(name))  // 如果UI管理器的面板字典包含该面板
        {
            UIManager.Instance.panelDict.Remove(name);  // 从字典中移除该面板
        }
    }
}

调用

以我视频中所示的这些界面为例,每一个界面我都会给他们挂接上一个脚本,这些脚本继承自base panel,比如在主菜单界面,当我点击修改用户时,则会弹出用户列表的这个界面,这时候我只需要调用UIManager.Instance.OpenPanel,然后打开这个界面即可

csharp 复制代码
public void OnBtnChangeUser()
{
	print("OnBtnChangeUser");
	UIManager.Instance.OpenPanel(UIConst.UserPanel);
}

关闭界面,也是类似的道理,只要调用close panel即可

csharp 复制代码
public void OnBtnCancel(){
	print("OnBtnRename");
	ClosePanel();
}

源码

整理好我会放上来

参考

【视频】https://www.bilibili.com/video/BV1zT411b7L3/

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关推荐
虾球xz20 分钟前
游戏引擎学习第61天
java·学习·游戏引擎
Camllia371 小时前
《CS2》报错dxgi.dll缺失怎么办?《CS2》游戏提示dxgi.dll缺失要怎么解决?
游戏
葡萄架子2 小时前
用Python写炸金花游戏
windows·python·游戏
两水先木示4 小时前
【Unity3D】Jobs、Burst并行计算裁剪Texture3D物体
unity·jobs·burst
la_vie_est_belle10 小时前
《Cocos Creator游戏实战》非固定摇杆实现原理
游戏·cocos creator·游戏开发·cocos·非固定摇杆
Thomas_YXQ10 小时前
Unity3D Huatuo技术原理剖析详解
unity·unity3d·游戏开发·性能调优·热更新
小板凳-BGM11 小时前
C# 第二阶段 modbus
开发语言·ui·c#
火云洞红孩儿12 小时前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
虾球xz13 小时前
游戏引擎学习第59天
学习·游戏引擎
zh路西法13 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式