欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity游戏开发UI框架(3)

程序员文章站 2022-04-03 16:53:41
...
这应该是最后一篇文章了。
前面的内容都是通过调用MySceneManager,进行状态(场景)切换,也就是核心就在他,一切的所有的内容都是围绕他进行的。控制了中枢MySceneManager,就控制了整个场景的切换。
同样的UI方面的处理也是一样的。唯一特别的就是LoadingPanel的处理,有那么一点点不同。之后详细说。
其他界面的处理思路和状态的处理一模一样的。但是考虑到UI有很多种,其中有全屏的(还分为透明和不透明的),有半屏的。这两个区别就是显示全屏的UI,后面的其他UI可以不用显示了,但是那种透明的界面,或者Tips的界面,还是能看到后面的UI,也就是层级的问题。![例如注册界面后面的登录界面还是能看到的,就不能把Login关闭或者隐藏](https://img-blog.csdnimg.cn/20210509151523706.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E3Mjc2NzQ2NTA=,size_16,color_FFFFFF,t_70#pic_center)

至于具体怎么处理要看个人吧,我使用的枚举作为标记区分;
先上管理类:
一、UI管理类

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

public class UIManager : MonoBehaviour
{
    private static Transform canvas;
    public static Transform Canvas
    {
        get
        {
            if (canvas == null)
            {
               canvas= GameObject.FindWithTag("Canvas").transform;             
            }
            return canvas;
        }
    }
    /// <summary>
    /// 已经加载过的Panel;
    /// </summary>
    private static Dictionary<PanelNameEnum, BasePanel> m_panelDic = new Dictionary<PanelNameEnum, BasePanel>();

    public static BasePanel LoadPanel(PanelNameEnum panelEnum, PanelNameEnum prePanel)//传入显示的界面,参数自己;
    {      
        BasePanel target = null;
        string pName = Enum.GetName(typeof(PanelNameEnum), panelEnum);
        if (!m_panelDic.TryGetValue(panelEnum,out target))
        {
        	//通过名字uiprefabs(自己定义的)加载AB包,以及AB包的资源(想要加载的Panel);
            GameObject t= ABManager.Instance.LoadAssets<GameObject>("uiprefabs", pName);
            GameObject temp = Instantiate(t, Canvas, false);
            temp.name = pName;
            target= temp.GetComponent<BasePanel>();
            m_panelDic.Add(panelEnum, target);            
        };
        //根据他的枚举类型,决定当前的这个之前的panel是否需要隐藏;
        if(prePanel!=PanelNameEnum.None)
             SwitchEnumPanel(target.ScreenEnu, m_panelDic[prePanel]);
        target.gameObject.SetActive(true);
        target.transform.SetParent(Canvas,false);
        target.transform.SetAsLastSibling();
        return target;
    }
    //Loading 专用加载;传数据或者传递方法以后在补充;
    public static BasePanel LoadPanel(string title=null,string content=null,string imageName=null)
    {
        BasePanel target=null;
        string pName = Enum.GetName(typeof(PanelNameEnum), PanelNameEnum.LoadingPanel);
        if (!m_panelDic.TryGetValue(PanelNameEnum.LoadingPanel, out target))
        {
            GameObject t = ABManager.Instance.LoadAssets<GameObject>("uiprefabs", pName);
            GameObject temp = Instantiate(t, Canvas, false);
            temp.name = pName;
            target = temp.GetComponent<BasePanel>();
            m_panelDic.Add(PanelNameEnum.LoadingPanel, target);
        };
        target.gameObject.SetActive(true);
        target.transform.SetParent(Canvas, false);
        target.transform.SetAsLastSibling();
        if(target is LoadingPanel)
        {
            (target as LoadingPanel).ShowContent(title, content, imageName);
        }
        return target;
    }

    //参数panel是即将要打开的。
    private static void SwitchEnumPanel(PanelScreenEnum panel,BasePanel panelName)
    {      
        switch (panel)
        {
            case PanelScreenEnum.FullScreen:
                panelName.ISShow(false);
                break;
            case PanelScreenEnum.HalfScreen:
                panelName.ISShow(true);
                break;
            case PanelScreenEnum.Tips:
                panelName.ISShow(true);
                break;
           
            default:
                break;
        }

    }
    public static void DestoryPanel(PanelNameEnum panelEnum)
    {
        if (m_panelDic.ContainsKey(panelEnum))
        {
            BasePanel temp = m_panelDic[panelEnum];
            m_panelDic.Remove(panelEnum);
            Destroy(temp.gameObject);
        }
        else
        {
            MDebug.LogError($"Please Check {panelEnum},Don't Destory!!!");
        }
    }
    public static void HideTargetPanel(PanelNameEnum panelEnum)
    {
        BasePanel target = null;
        if (m_panelDic.TryGetValue(panelEnum, out target))
        {
            target.ISShow(false);
        }
    }
    public static void HideAllPanel()
    {
        if (m_panelDic == null)
        {
            MDebug.LogError("panelDic is null, please Check!!!");
        }
        foreach (var item in m_panelDic.Values)
        {
            item.ISShow(false);
        }
    }

    /// <summary>
    /// 谨慎使用;
    /// </summary>
    public static void ClearPanelDic()
    {
        foreach (var item in m_panelDic.Values)
        {
            Destroy(item.obj);
        }
        m_panelDic.Clear();
        GC.Collect();
    }
}

二、UI的基类

public abstract class BasePanel : MonoBehaviour
{
	//表示该UIPanel是全屏的还是其他的;
    protected PanelScreenEnum screenEnu = PanelScreenEnum.HalfScreen;
    //名字的枚举实际上就是要么通过名字字符串直接加载,要么就是通过枚举加载,这个加载的方式是和UIManager自己定义的有关的,怎么写都行;只是我用的枚举而已。
    protected PanelNameEnum nameEnum;
    public void Awake()
    {
   		//数据初始化,不一定用得到;       
        InitData();
    }
    public void OnUpdate()
    {
    	//刷新UI的逻辑;
        UpdateUI();
    }
    public GameObject obj
    {
        get
        {
            return gameObject;
        }       
    }

    public PanelScreenEnum ScreenEnu
    {
        get => screenEnu;
    }
    public PanelNameEnum NameEnum { get => nameEnum; }

    public void ISShow(bool isShow)
    {

        obj.SetActive(isShow);
    }
    protected virtual void UpdateUI() { }
    protected abstract void InitData();
}

三、单独把Loading界面拿出来

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

public class LoadingPanel : BasePanel
{
    [SerializeField]
    private Image m_Bg;
    public Slider m_Slider;
    public Text  m_title_text;
    public Text m_Content;
    public Text showSliderValue_text;
    protected override void InitData()
    {
        this.screenEnu = PanelScreenEnum.FullScreen;
        this.nameEnum = PanelNameEnum.LoadingPanel;
        MessageManager.AddListener("loading", DoValueSlider);
    }
    public void OnDestroy()
    {
        MessageManager.RemoveFunc("loaidng");
    }
    public void DoValueSlider(float num)
    {
        m_Slider.value = num;
        MDebug.Log("Loading界面的进度值 m_Slider.value:" + m_Slider.value);
        if (num >= 0.8999)
        {
            m_Slider.value = 1;
        }
        showSliderValue_text.text = (m_Slider.value * 100).ToString() + "%";
    }
    public void ShowContent(string title = null, string content = null, string imageName = null)
    {
        if (title != null && content != null)
            m_title_text.text = title;
        m_Content.text = content;
        if (imageName != null && imageName!=string.Empty)
        {
            MDebug.Log(imageName != string.Empty);
            MDebug.Log(imageName!=null);
            m_Bg = ABManager.Instance.LoadAssets<Image>("iamgebg", imageName);
        }
    }
}

注意: MessageManager.AddListener(“loading”, DoValueSlider);在界面一打开的时候我就把这个消息注册到消息盒子里面去了,在那执行的呢?还记得场景管理类中吗?进入每个场景,就首先通过UIManager加载了loading界面,因此也就把这个委托注册到MessageManager这个雷中秋u了。然后,通过MySceneManager执行了LoadSceneMsg管理类中的异步加载场景的方法,在这个方法里面, 执行了MessageManager.DoFunc(“loading”, async.progress);通过传入两个参数,执行了之前注册的委托DoValueSlider。当加载完成以后,我又重新移除了这个方法。如果销毁这个界面,那么就自动移除了这个方法。
while (async.progress < 0.9f)
{
MDebug.Log(“async.progress” + async.progress);
MessageManager.DoFunc(“loading”, async.progress);
yield return null;
}

到此为止,框架算是完成了。因为有部分东西其实还是不合理,但是我实在是懒得改了……自行解决就行了。

下面这个是关于音乐相关处理:
四、其他播放音乐的管理。同样的特效也是这个道理,没什么好说的。

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

public class MusicManager :AutoSingleton<MusicManager>
{
    private AudioClip buttonClip;
    private AudioClip bgClip;
    private AudioClip otherClip;
    private GameObject audioSource;
    private AudioSource buttonAudioSource;
    private AudioSource bgAudioSource;
    private AudioSource otherAudioSource;

    private void Awake()
    {
        CreatAudioSource();
        PlayBGAudio(true);
    }
    private void CreatAudioSource()
    {
        if (audioSource == null)
        {
            audioSource = new GameObject("AudioSourceComponent");
            DontDestroyOnLoad(audioSource);
            MDebug.Log("创建了物体AudioSource");
        }
        if (buttonAudioSource == null)
        {
            GameObject go = new GameObject("buttonAudioSource");
            go.transform.SetParent(audioSource.transform);
            buttonAudioSource = go.AddComponent<AudioSource>();
        }
        if (bgAudioSource == null)
        {
            GameObject go = new GameObject("bgAudioSource");
            bgAudioSource = go.AddComponent<AudioSource>();
            go.transform.SetParent(audioSource.transform);
        }
        if (otherAudioSource == null)
        {
            GameObject go = new GameObject("otherAudioSource");
            otherAudioSource = go.AddComponent<AudioSource>();
            go.transform.SetParent(audioSource.transform);
        }
    }
    public void OnMyClickClip()
    {
        if (buttonClip == null)
            buttonClip = ABManager.Instance.LoadAssets<AudioClip>("audio", "buttonClip");
        buttonAudioSource.clip = buttonClip;
        buttonAudioSource.mute = false;
        buttonAudioSource.loop = false;
        buttonAudioSource.Play();
    }
    public void PlayBGAudio(bool isPlay)
    {
        if (bgClip == null)
        {
            bgClip = ABManager.Instance.LoadAssets<AudioClip>("audio", "zanyong_bg");
        }
        bgAudioSource.clip = bgClip;
        buttonAudioSource.mute = false;
        buttonAudioSource.loop = true;
        bgAudioSource.Play();
    }
}

搞定!!!以后有空可能会写一点关于Shader的东西吧,也可能会写一些关于Ai相关的FSM啊,GOAP之类的,自动寻路之类的。我要说但是了,实在时间不太多(懒的另一种说法),基本除了在学习就是学习(这个是真的),其余时间睡觉了。我属于死宅那种……
我到现在游戏开发工作满打满算才两年,实际上也是个新手,说是啥也不会也不为过。最开始走了不少弯路,最近半年对shader比较感兴趣,所以不务正业玩了很久的shader,我对写代码感兴趣,所以不爱玩连连看,连连看也就了解的比较少吧。这个工程呢,下面我贴出来就行了,本来想自己写个demo玩的,但是美术资源不好找,最近又有新的学习目标,就搁浅了。过段时间和约好的三个大佬(一个地编,一个特效,一个前端主程),正在忽悠一个原画小哥,打算抽空忽悠一个模型过来,人凑齐了可能会一起做个小游戏玩吧。

工程里里面超了几个sahder代码,挺简单但是蛮好玩的。我就不删除了。
链接: https://pan.baidu.com/s/1qS0neCerO0FJ2sVK_tGYWQ 密码: vpdu