Unity游戏开发UI框架(3)
这应该是最后一篇文章了。
前面的内容都是通过调用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