单例设计模式的UI框架

来源:互联网 发布:ecshop sql 编辑:程序博客网 时间:2024/06/04 19:21

MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式:
  • Model(模型)表示应用程序核心(比如数据库记录列表)。
  • View(视图)显示数据(数据库记录)。
  • Controller(控制器)处理输入(写入数据库记录)。 (引自百度)
编写UI框架的意义:

  • 打开,关闭,层级,页面跳转等管理问题集中化,将外部切换等逻辑交给UIManager处理
  • 功能逻辑分散化,每个页面维护自身逻辑,依托于框架便于多人协同开发,不用关心跳转和显示关闭细节
  • 通用性框架能够做到简单的代码复用和"项目经验"沉淀

优点:

耦合性低  重用性高  可维护性高  生命周期成本低  有利软件工程化管理  部署快

缺点:

不适合小型,中等规模的应用程序  增加系统结构和实现的复杂性  视图与控制器间的过于紧密的连接   视图对模型数据的低效率访问


(以奇怪大冒险手游为例)

抽象Model层:

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

// UI派生类的基类,我们可以写一些通用方法
public class UIBase : MonoBehaviour
{
    // 用户界面第一次进入屏幕调用
    public virtual void UserInterfaceEnter()
    {


    }
    // 用户界面在当前屏幕停止时调用
    public virtual void UserInterfacePause()
    {


    }
    // 用户界面重新回到屏幕时调用
    public virtual void UserInterfaceResume()
    {


    }
    // 用户界面销毁时调用
    public virtual void UserInterfaceDestroy()
    {


    }
    public virtual void UserInterfaceExit()
    {


    }


}



UI的管理类,控制UI的出栈入栈和暂停恢复:


public class UIManager : MonoBehaviour {


    //  因为我们需要经常调用UIManager类中的方法,并且UIManager不允许存在多个对象,只能有有一个对象,所以使用单例


    private static UIManager _instance;
    public static UIManager Instance
    {
        get
        {
            return _instance;
        }
    }


    // 创建一个泛型栈,用来管理UI入栈出栈
    private static Stack<UIBase> UIStack = new Stack<UIBase>();
    // 字典来缓存已经实例化的UI面板对象
    private static Dictionary<string, UIBase> CacheDict = new Dictionary<string, UIBase>();
    // 创建另一个字典来管理我们所有的UI预设体
    private static Dictionary<string, GameObject> AllObjectDict = new Dictionary<string, GameObject>();
    // 在Awake方法中需要指定单例
    private void Awake()
    {
        // 执行单例对象
        _instance = this;   // 实例化
        // 将我们的预设体都添加入字典
        AddUIPanel("UIStart");
        AddUIPanel("UIOption");
        AddUIPanel("PlayScene");
        
    }


    // 添加UI面板中的预设体
    private void AddUIPanel(string name)   // 根据名字添加预设体
    {
        // 要先拿到预设体
        // 拿到路径,也就是我们存储预设体的路径
        string uiPath = "UI/" + name;
        // 通过Resources 类加载Resources/UI下的预设体
        GameObject uiObj = Resources.Load<GameObject>(uiPath);
        // 判断是否为空
        if (uiObj)
        {
            // 将预设体添加到字典,名字作为键,预设体作为值
            AllObjectDict.Add(name, uiObj);
        }
    }
    // 入栈
    public void PushUIPanel(string uiName)
    {
        // 判断栈中是否为空
        if (UIStack.Count > 0)
        {
            // 返回栈顶元素 
            UIBase currentTop = UIStack.Peek();
            // 暂停UI
            currentTop.OnPause();
        }
        // 添加新的元素到栈顶
        UIBase newTop = GetNewElement(uiName);
        // 入栈的时候调用 UIBase 入栈方法
        newTop.OnEnter();
        UIStack.Push(newTop);
    }


    // 获取UIBase的方法
    private UIBase GetNewElement(string uiname)
    {
        // 首先需要判断缓存区是否已经有了缓存,如果有那么直接将缓存区的返回,没有的话添加入缓存区并返回
        foreach (var item in CacheDict.Keys)
        {
            // 如果我们需要添加的栈顶名字缓存区有的话那么直接取出
            if (item == uiname)
            {
                return CacheDict[uiname];
            }
        }
        GameObject go = AllObjectDict[uiname];
        // 其次创建 go
        GameObject go1 = Instantiate(go);
        // 让我们创建出的名字和设定的一样
        go1.name = uiname;
        // 拿到他身上的组件
        UIBase uibase = go1.GetComponent<UIBase>();
        // 添加入缓存区
        CacheDict.Add(uiname, uibase);
        return uibase;
    }
    // 出栈
    public void PopUIStack()
    {
        // 判断栈中是否有元素
        if (UIStack.Count == 0)
        {
            return;
        }
        // 取出栈顶并且移除
        UIBase currentTop = UIStack.Pop();
        // 执行退出方法
        currentTop.OnExit();
        if (UIStack.Count > 0)
        {
            // 取出当前栈顶元素,并执行恢复UI方法
            UIBase newTop = UIStack.Peek();
            newTop.OnResume();
        }
    }
}

控制选项界面:

public class UIOptionControl : UIBase {




    public override void OnEnter()
    {
        // 使当前的UI摄像机为我们的主摄像机
        GetComponent<Canvas>().worldCamera = Camera.main;
        gameObject.SetActive(true);
        canvasG.interactable = true;
        canvasG.DOFade(1f, 0.3f);
    }
    public override void OnPause()
    {
        canvasG.interactable = false;
    }
    public override void OnResume()
    {
        canvasG.interactable = true;
    }
    public override void OnExit()
    {
        canvasG.DOFade(0, 0.3f);
        canvasG.interactable = false;
        gameObject.SetActive(false);
    }
    public void PushStart()
    {
        // 移除栈顶
        UIManager.Instance.PopUIStack();
    }
}

控制开始界面:

public class UIStartControl : UIBase
{
    public override void OnEnter()
    {
        // 使当前的UI摄像机为我们的主摄像机
        GetComponent<Canvas>().worldCamera = Camera.main;
        AudioManager.Instance.PlayAudio("Audio_bgm_1");
    }
    public override void OnPause()
    {
        // 禁用UI的交互功能
        canvasG.interactable = false;
    }
    public override void OnResume()
    {
        canvasG.interactable = true;
    }
    public override void OnExit()
    {
        base.OnExit();
    }
    public void PushOption()
    {
        // 添加栈顶
        UIManager.Instance.PushUIPanel("UIOption");
    }
    public void IsMute(bool mute)
    {
        AudioManager.Instance.Mute = mute;
    }
    public void PushPlayScene() {
        // 添加游戏界面为栈顶
        UIManager.Instance.PushUIPanel("PlayScene");
    }
    
}

管理音乐的类:

public class AudioManager : MonoBehaviour
{


private AudioManager()
    {


    }
    private static AudioManager _instance;
    public static AudioManager Instance
    {
        get
        {
            return _instance;
        }
    }
    private void Awake()
    {
        _instance = this;
        audioSource = GetComponent<AudioSource>();
        // 设置
        audioSource.playOnAwake = true;
        audioSource.loop = true;
    }
    AudioSource audioSource;
    string audioDir = "Audios";
    AudioClip currentClip;
    Dictionary<string, AudioClip> AllClipDict = new Dictionary<string, AudioClip>();
    // 播放一个北京音乐
    public void PlayBGM(string ACname)
    {
        AudioClip ac;
        // 判断这首音乐已经加载到内存,是的话就获取出来,不是就从资源文件加当中加载
        if (AllClipDict.ContainsKey(ACname))
        {
            ac = AllClipDict[ACname];
        }
        else
        {
            // 构建路径
            string path = audioDir + "/" + ACname;
            ac = Resources.Load<AudioClip>(path);
            AllClipDict.Add(ACname, ac);
        }
        currentClip = ac;
        audioSource.clip = ac;
        audioSource.Play();
    }
    public void StopBGM()
    {
        audioSource.Stop();
    }
    public void ResumeBGM()
    {
        audioSource.Play();
    }
    // 调节音量大小
    public float Volumn
    {
        set
        {
            audioSource.volume = value;
        }
        get
        {
            return audioSource.volume;
        }
    }
    Dictionary<string, AudioClip> AllBGmDict = new Dictionary<string, AudioClip>();
    // 音频(一小段)
    public void PlayClip(string name)
    {
        AudioClip ac;
        
        if (AllBGmDict.ContainsKey(name))
        {
            ac = AllBGmDict[name];
        }
        else
        {
            // 构建路径
            string path = audioDir + "/" + name;
            ac = Resources.Load<AudioClip>(path);
            AllBGmDict.Add(name, ac);
        }
        AudioSource.PlayClipAtPoint(ac, Vector3.zero);
    }
}

启动UI的类:




public class GameController : MonoBehaviour {



void Start () {
        UIManager.Instance.PushUIInterface("UIStart");
        AudioManager.Instance.PlayBGM("Audio_bgm_1");
}
}