承接前面的内容,继续来学习Unity中的程序基础框架
顾名思义,作用:统一管理音乐音效相关
ProjectBase下创建一个目录Music,再在下面创建一个MusicMgr,它也是继承自单例模式模块
以前我们处理播放声音是怎么搞?每一个需要播放声音的地方分开弄代码,这样很麻烦,改起来也不好改。
Resource目录下面新建一个Music目录存放我们的音乐,Sounds存放音效,bk存放背景音乐
代码
public class MusicMgr : BaseManager
{
private AudioSource bkMusic = null;
private float bkVaule = 1;
private float soundVaule = 1;
private GameObject soundObj = null;
private List soundList = new List();
public MusicMgr() {
MonoMgr.GetInstance().AddUpdateListener(update);
}
private void update() {
for (int i = soundList.Count-1; i >= 0; i--) {
if (!soundList[i].isPlaying) {
GameObject.Destroy(soundList[i]);
soundList.RemoveAt(i);
}
}
}
//播放背景音乐
public void PlayBKMusic(string name) {
if (bkMusic == null) {
GameObject obj = new GameObject("BKMusic");
bkMusic = obj.AddComponent();
}
//异步加载背景音乐并且加载完成后播放
ResMgr.GetInstance().LoadAsync("Music/bk/"+name,(clip) => {
bkMusic.clip = clip;
bkMusic.loop = true;
//调整大小
bkMusic.volume = bkVaule;
bkMusic.Play();
});
}
//改变音量大小
public void ChangeBKValue(float v) {
bkVaule = v;
if (bkMusic == null) {
return;
}
bkMusic.volume = bkVaule;
}
//暂停背景音乐
public void PauseBKMusic() {
if (bkMusic == null)
{
return;
}
bkMusic.Pause();
}
//停止背景音乐
public void StopBKMusic() {
if (bkMusic == null) {
return;
}
bkMusic.Stop();
}
//播放音效
public void PlaySound(string name,bool isLoop,UnityAction callback=null ) {
if (soundObj == null) {
soundObj = new GameObject();
soundObj.name = "Sounds";
}
AudioSource source=soundObj.AddComponent();
ResMgr.GetInstance().LoadAsync("Music/Sounds/" + name, (clip) => {
source.clip = clip;
source.loop = isLoop;
//调整大小
source.volume = soundVaule;
source.Play();
//音效资源异步加载结束后,将这个音效组件加入集合中
soundList.Add(source);
if (callback != null) {
callback(source);
}
});
}
//改变所有音效大小
public void ChangeSoundValue(float value) {
soundVaule = value;
for (int i = 0; i < soundList.Count; ++i) {
soundList[i].volume = value;
}
}
//停止音效
public void StopSound(AudioSource source) {
if (soundList.Contains(source)) {
soundList.Remove(source);
source.Stop();
GameObject.Destroy(source);
}
}
}
没什么好说的,很好理解。
使用的时候,注意好路径
统一管理UI以及相关(UGUI),提高代码复用率,降低文件耦合性。
在ProjectBase下面新建一个目录——UI,再在下面新建两个文件——UIManager.cs、BasePanel.cs
//面板基类
//找到所有自己面板下的控件对象
//提供显式/隐藏的行为
public class BasePanel : MonoBehaviour
{
//通过里式转换原则,来存储所有的UI控件
private Dictionary> controlDic
= new Dictionary>();
private void Awake()
{
FindChildControl
使用方法很简单,在UI的Canvas上挂载一个脚本继承自这个BasePanel,这样这个Canvas下面所有的UI都可以被GetControl直接找到,很方便
示例
public class UItest : BasePanel
{
private void Start()
{
GetControl
我们现在来编写UI管理器,首先修改场景Canvas的属性来保证分辨率问题
主要是修改Canvas Scaler (画布定标器)
关于定标器的设置,可以参考这个文章和这个文章
看代码之前请先来理解一下思路:比如某UI结构是这样的
纠错:这里顺序错了,top层应该在最下面,bot在最上面
Canvas就不用说了,top、mid、bot分别是我们的三个层,为了应对一些层级UI情况,再往下的各个Panel才是放置UI的重点面板。
我们将我们准备的几个Panel做成预制体,然后删掉场景中的它们。
我们再将Canvas(子物体有top、mid、bot三个层)以及EventSystem再做成两个预制体,这两个物体生成到场景中,我们会设置成DontDestroyOnLoad。
后面如果面板中有按钮等组件,一定要放在顶层
我们的某个面板,比如LoginPanel,需要挂载一个脚本,挂载的脚本需要继承 BasePanel
像是这样
public class LoginPanel : BasePanel
{
private void Start()
{
GetControl("Button").onClick.AddListener(() =>
{
print("我是顶层");
});
}
public void InitInfo() {
print("初始化了数据");
}
}
然后,我们的UIManager.cs文件内容如下
//UI层级枚举
public enum E_UI_Layer {
Bot,
Mit,
Top
}
//UI管理器(管理面板)
//管理所有显示的面板
//提供给外部 显示和隐藏
public class UIManager : BaseManager
{
public Dictionary panelDic
= new Dictionary();
//这是几个UI面板
private Transform bot;
private Transform mid;
private Transform top;
public UIManager() {
//去找Canvas(做成了预设体在Resources/UI下面)
GameObject obj=ResMgr.GetInstance().Load("UI/Canvas");
Transform canvas = obj.transform;
//创建Canvas,让其过场景的时候不被移除
GameObject.DontDestroyOnLoad(obj);
//找到各层
bot = canvas.Find("bot");
mid = canvas.Find("mid");
top = canvas.Find("top");
//加载EventSystem,有了它,按钮等组件才能响应
obj =ResMgr.GetInstance().Load("UI/EventSystem");
//创建Canvas,让其过场景的时候不被移除
GameObject.DontDestroyOnLoad(obj);
}
public void ShowPanel(string panelName,
E_UI_Layer layer=E_UI_Layer.Top,
UnityAction callback=null) where T:BasePanel {
//已经显示了此面板
if (panelDic.ContainsKey(panelName))
{
//调用重写方法,具体内容自己添加
panelDic[panelName].ShowMe();
if (callback!=null)
callback(panelDic[panelName] as T);
return;
}
ResMgr.GetInstance().LoadAsync("UI/"+panelName,(obj)=> {
//把它作为Canvas的子对象
//并且设置它的相对位置
//找到父对象
Transform father = bot;
switch (layer) {
case E_UI_Layer.Mit:
father = mid;
break;
case E_UI_Layer.Top:
father = top;
break;
}
//设置父对象
obj.transform.SetParent(father);
//设置相对位置和大小
obj.transform.localPosition = Vector3.zero;
obj.transform.localScale = Vector3.one;
(obj.transform as RectTransform).offsetMax = Vector2.zero;
(obj.transform as RectTransform).offsetMin = Vector2.zero;
//得到预设体身上的脚本(继承自BasePanel)
T panel = obj.GetComponent();
//执行外面想要做的事情
if (callback != null) {
callback(panel);
}
//在字典中添加此面板
panelDic.Add(panelName, panel);
});
}
//隐藏面板
public void HidePanel(string panelName) {
if (panelDic.ContainsKey(panelName)) {
//调用重写方法,具体内容自己添加
panelDic[panelName].HideMe();
GameObject.Destroy(panelDic[panelName].gameObject);
panelDic.Remove(panelName);
}
}
}
在外部调用的示例
public class UItest :MonoBehaviour
{
private void Start()
{
//新建一个挂载着LoginPanel的叫做LoginPanel的面板,
//新建到top层下面,异步加载完成后调用ShowPanelOver
UIManager.GetInstance().ShowPanel("LoginPanel",
E_UI_Layer.Top,
ShowPanelOver);
//如果是同步加载,则可以直接如下调用面板中的方法
//不过咱们是异步加载,所以必须在回调中调用方法(因为此刻面板尚未创建)
//LoginPanel p=this.gameobject.GetComponent();
//p.IninInfo();
}
private void ShowPanelOver(LoginPanel panel) {
panel.InitInfo();
Invoke("DelayHide", 6);
}
private void DelayHide() {
UIManager.GetInstance().HidePanel("LoginPanel");
}
}
基本需要的框架到这里为止就全部结束了,我现在将做的基础框架集合导出成了一个Unity包
我保存在了自己的网盘中,如果你需要,请及时保存
链接:https://pan.baidu.com/s/1dCNcxg8Xk9Cb8DMBWxVdDQ
提取码:hkj3
或者可以访问我的 Github,获得Unity项目包
再次感谢b站的唐老湿
商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢