Unity3D程序基础框架(下)

Unity3D程序基础框架(下)

前言:

承接前面的内容,继续来学习Unity中的程序基础框架

 

 

 

 

 

 

 


音效管理模块

顾名思义,作用:统一管理音乐音效相关

ProjectBase下创建一个目录Music,再在下面创建一个MusicMgr,它也是继承自单例模式模块

以前我们处理播放声音是怎么搞?每一个需要播放声音的地方分开弄代码,这样很麻烦,改起来也不好改。

Resource目录下面新建一个Music目录存放我们的音乐,Sounds存放音效,bk存放背景音乐

代码

public class MusicMgr : BaseManager<MusicMgr>
{
    private AudioSource bkMusic = null;

    private  float bkVaule = 1;

    private float soundVaule = 1;

    private GameObject soundObj = null;
    private List<AudioSource> soundList = new List<AudioSource>();


    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<AudioSource>();
        }
        //异步加载背景音乐并且加载完成后播放
        ResMgr.GetInstance().LoadAsync<AudioClip>("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<AudioSource> callback=null ) {
        if (soundObj == null) {
            soundObj = new GameObject();
            soundObj.name = "Sounds";
        }
        AudioSource source=soundObj.AddComponent<AudioSource>();
        ResMgr.GetInstance().LoadAsync<AudioClip>("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管理模块

UI基类

统一管理UI以及相关(UGUI),提高代码复用率,降低文件耦合性。

在ProjectBase下面新建一个目录——UI,再在下面新建两个文件——UIManager.cs、BasePanel.cs

//面板基类
//找到所有自己面板下的控件对象
//提供显式/隐藏的行为
public class BasePanel : MonoBehaviour
{
    //通过里式转换原则,来存储所有的UI控件
    private Dictionary<string, List<UIBehaviour>> controlDic 
        = new Dictionary<string, List<UIBehaviour>>();
    private void Awake()
    {
        FindChildControl<Button>();
        FindChildControl<Image>();
        FindChildControl<Text>();
        FindChildControl<Toggle>();
        FindChildControl<ScrollRect>();
        FindChildControl<Slider>();
    }
    //得到对应名字的对应控件脚本
    protected T GetControl<T>(string controlName) where T:UIBehaviour {
        if (controlDic.ContainsKey(controlName)) {
            for (int i = 0; i < controlDic[controlName].Count; i++) {
                //对应字典的值(是个集合)中,符合要求的类型的
                //则返回出去,这样外部就
                if (controlDic[controlName][i] is T) {
                    return controlDic[controlName][i] as T;
                }
            }
        }
        return null;
    }
    //找到相对应的UI控件并记录到字典中
    private void FindChildControl<T>() where T:UIBehaviour {
        T[] controls = this.GetComponentsInChildren<T>();
        string objname;
        for (int i = 0; i < controls.Length; i++)
        {
            objname = controls[i].gameObject.name;
            if (controlDic.ContainsKey(objname))
            {
                controlDic[objname].Add(controls[i]);
            }
            else
            {
                controlDic.Add(objname, new List<UIBehaviour>() { controls[i] });
            }            
        }
    }
    //让子类重写(覆盖)此方法,来实现UI的隐藏与出现

    public virtual void ShowMe() {

    }
    public virtual void HideMe() {

    }
}

使用方法很简单,在UI的Canvas上挂载一个脚本继承自这个BasePanel,这样这个Canvas下面所有的UI都可以被GetControl直接找到,很方便

示例

public class UItest : BasePanel
{
    private void Start()
    {
        GetControl<Button>("Button").onClick.AddListener(() =>
        {
            print("开始游戏");
        });
        GetControl<Button>("Button2").onClick.AddListener(() =>
        {
            print("设置");
        });
    }
}

UI管理器

我们现在来编写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>("Button").onClick.AddListener(() =>
        {
            print("我是顶层");
        });
    }
    public void InitInfo() {
        print("初始化了数据");
    }
}

然后,我们的UIManager.cs文件内容如下

//UI层级枚举
public enum E_UI_Layer {
    Bot,
    Mit,
    Top
}


//UI管理器(管理面板)
//管理所有显示的面板
//提供给外部 显示和隐藏
public class UIManager : BaseManager<UIManager>
{
    public Dictionary<string, BasePanel> panelDic 
        = new Dictionary<string, BasePanel>();

    //这是几个UI面板
    private Transform bot;
    private Transform mid;
    private Transform top;


    public UIManager() {
        //去找Canvas(做成了预设体在Resources/UI下面)
        GameObject obj=ResMgr.GetInstance().Load<GameObject>("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<GameObject>("UI/EventSystem");

        //创建Canvas,让其过场景的时候不被移除
        GameObject.DontDestroyOnLoad(obj);
    }
       
    public void ShowPanel<T>(string panelName,
                        E_UI_Layer layer=E_UI_Layer.Top,
                        UnityAction<T> callback=null) where T:BasePanel {
        //已经显示了此面板
        if (panelDic.ContainsKey(panelName))
        {
            //调用重写方法,具体内容自己添加
            panelDic[panelName].ShowMe();
            if (callback!=null)
                callback(panelDic[panelName] as T);
            return;
        }
        ResMgr.GetInstance().LoadAsync<GameObject>("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<T>();

            //执行外面想要做的事情
            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>("LoginPanel", 
            E_UI_Layer.Top,
            ShowPanelOver);
        //如果是同步加载,则可以直接如下调用面板中的方法
        //不过咱们是异步加载,所以必须在回调中调用方法(因为此刻面板尚未创建)
        //LoginPanel p=this.gameobject.GetComponent<LoginPanel>();
        //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站的唐老湿

 


商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

 

 

发表评论

是的,我就是计算机界的枭雄!