MVC

  • 一、什么是MVC
  • 二、MVC小案例
  • Model
  • View
  • Controller
  • 启动脚本
  • 三、小小小总结


一、什么是MVC

  说起MVC,可能都知道是Model--View--Controller,但是真要通透理解它的思想,还真是不容易。

  何谓MVC呢?MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。MVC百度百科   通过以上,我们大体可以知道MVC目的是将M和V分离,并且C确保M和V的同步,一旦M改变V应该同步更新。,带着这个基本思想我们看下图:

unity的mvc三层架构 unity mvc ui_unity3d


  MVC目的将Model(模型)层跟View(视图)层分离,而Controller(控制层)就是将Model层跟View层联系起来。

  用户在操作界面(View)时,将行为传递到控制层,控制层更新Model层,而Model层更新完成后需要通知View层完成界面更新,所以Model通知控制层需要更新View层了。View界面更新,最后反馈给用户。

  这样Model跟View分离,逻辑基本都是在Controller进行操作,Model中没有任何View的数据,二者这样大大减少了代码的耦合度。

说的太多不一定能理解其好处,直接看二中的小案例

二、MVC小案例

1.使用UGUI搭建的如下图的面板,实现一个简单的UI面板。

  功能是:点击主面板(MainPanel)中的面板按钮,显示玩家信息(PlayerPanel)面板,PlayerPanel面板上点击升级按钮,当前面板和主面板上对应的值改变。

unity的mvc三层架构 unity mvc ui_c#_02


unity的mvc三层架构 unity mvc ui_mvc_03

Model

Model相当于数据模型,不存在任何View的数据和状态。只能被Controller访问和其它的Modelc层,通过事件的订阅来响应Model具体的操作。

using UnityEngine;
using UnityEngine.Events;

//数据模型
public class Model
{
    #region 单例模式

    private static Model data = null;

    public static Model Data
    {
        get
        {
            if (data == null)
            {
                data = new Model();
                //首次创建数据初始化
                data.Init();
            }
            return data;
        }
    }

    #endregion 单例模式

    private string playerName;

    public string PlayerName
    {
        get { return playerName; }
    }

    private int playerLev;

    public int PlayerLev
    {
        get { return playerLev; }
    }

    private int playerGold;

    public int PlayerGold
    {
        get { return playerGold; }
    }

    private int playerHp;

    public int PlayerHp
    {
        get { return playerHp; }
    }

    private int playerMp;

    public int PlayerMp
    {
        get
        {
            return playerMp;
        }
    }

    private int playerAtk;

    public int PlayerAtk
    {
        get { return playerAtk; }
    }

    //数据初始化
    private void Init()
    {
        playerName = PlayerPrefs.GetString("playerName", "11");
        playerLev = PlayerPrefs.GetInt("playerLev", 1);
        playerGold = PlayerPrefs.GetInt("playerGold", 50);

        playerHp = PlayerPrefs.GetInt("playerHp", 100);
        playerMp = PlayerPrefs.GetInt("playerMp", 200);

        playerAtk = PlayerPrefs.GetInt("playerAtk", 100);
    }

    //升级
    public void LevUp()
    {
        playerLev += 1;

        playerGold += playerLev;
        playerHp += playerLev;
        playerMp += playerLev;
        playerAtk += playerLev;

        SaveData();
    }

    //保存数据
    private void SaveData()
    {
        PlayerPrefs.SetString("playerName", "11");
        PlayerPrefs.SetInt("playerLev", playerLev);
        PlayerPrefs.SetInt("playerGold", playerGold);

        PlayerPrefs.SetInt("playerHp", playerHp);
        PlayerPrefs.SetInt("playerMp", playerMp);
        PlayerPrefs.SetInt("playerAtk", playerAtk);

        //更新数据
        UpdateInfo();
    }

    //更新事件的委托
    private event UnityAction<Model> UpdateEvent;

    //添加监听事件
    public void AddEventListener(UnityAction<Model> unityAction)
    {
        UpdateEvent += unityAction;
    }

    //移除监听事件
    public void RemoveEventListener(UnityAction<Model> unityAction)
    {
        UpdateEvent -= unityAction;
    }

    //执行更新
    public void UpdateInfo()
    {
        if (UpdateEvent != null)
        {
            UpdateEvent(this);
        }
    }
}

View

视图层用于显示UI
MainView脚本

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 主视图
/// </summary>
public class MainView : MonoBehaviour
{
    public Text txtName;   //姓名
    public Text txtLev;    //等级
    public Text txtGold;   //金币
    public Button btnPanel;

    //更新的方法
    public void UpdateData(Model model)
    {
        txtName.text = model.PlayerName;
        txtLev.text = model.PlayerLev.ToString();
        txtGold.text = model.PlayerGold.ToString();
    }
}

PlayerView脚本

using UnityEngine;
using UnityEngine.UI;

//视图层
public class PlayerView : MonoBehaviour
{
    public Text txtLev;
    public Text txtHp;
    public Text txtMp;
    public Text txtAtk;

    public Button btnUp;
    public Button btnClose;

    public void UpdateData(Model model)
    {
        txtLev.text = model.PlayerLev.ToString();
        txtHp.text = model.PlayerHp.ToString();
        txtMp.text = model.PlayerMp.ToString();
        txtAtk.text = model.PlayerAtk.ToString();
    }
}

Controller

控制层包含了Model和View之间的联系

MainControler 脚本

using UnityEngine;

public class MainControler : MonoBehaviour
{
    private MainView mainView;

    private static MainControler controler;

    public static MainControler Controler
    {
        get
        {
            return controler;
        }
    }

    //显示
    public static void Show()
    {
        if (controler == null)
        {
            GameObject res = Resources.Load<GameObject>("UI/MainPanel");
            GameObject obj = Instantiate(res);
            obj.transform.SetParent(GameObject.Find("Canvas").transform, false);

            controler = obj.GetComponent<MainControler>();
        }
        controler.gameObject.SetActive(true);
    }

    //隐藏
    public static void Hide()
    {
        if (controler != null)
        {
            controler.gameObject.SetActive(false);
        }
    }

    private void Start()
    {
        mainView = this.GetComponent<MainView>();
        //首次更新数据
        mainView.UpdateData(Model.Data);

        //按钮的点击事件
        mainView.btnPanel.onClick.AddListener(() =>
        {
            print("点击了主场景中的按钮");
            PlayerControler.Show();
        });

        //添加更新视图层中的事件
        Model.Data.AddEventListener(UpdateData);
    }

    private void UpdateData(Model model)
    {
        if (mainView != null)
        {
            mainView.UpdateData(model);
        }
    }

    private void OnDestroy()
    {
        Model.Data.RemoveEventListener(UpdateData);
    }
}

PlayerControler脚本

using UnityEngine;

public class PlayerControler : MonoBehaviour
{
    private PlayerView playerView;

    private static PlayerControler controler = null;

    public static PlayerControler Controler
    {
        get
        {
            return controler;
        }
    }

    public static void Show()
    {
        if (controler == null)
        {
            GameObject res = Resources.Load<GameObject>("UI/PlayerPanel");
            GameObject obj = Instantiate(res);
            obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
            controler = obj.GetComponent<PlayerControler>();
        }

        controler.gameObject.SetActive(true);
    }

    public static void Hide()
    {
        if (controler != null)
        {
            controler.gameObject.SetActive(false);
        }
    }

    private void Start()
    {
        playerView = this.GetComponent<PlayerView>();
        playerView.UpdateData(Model.Data);

        playerView.btnUp.onClick.AddListener(() =>
        {
            //升级按钮
            Model.Data.LevUp();
        });

        playerView.btnClose.onClick.AddListener(() =>
        {
            Hide();
        });

        Model.Data.AddEventListener(UpdateData);
    }

    private void UpdateData(Model model)
    {
        if (playerView != null)
        {
            playerView.UpdateData(model);
        }
    }

    private void OnDestroy()
    {
        Model.Data.RemoveEventListener(UpdateData);
    }
}

脚本挂载界面

1.MainPanel界面

unity的mvc三层架构 unity mvc ui_unity的mvc三层架构_04


2.PlayerPanel界面

unity的mvc三层架构 unity mvc ui_MVC_05

启动脚本

using UnityEngine;

//启动入口
public class GameMgr : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.M))
        {
            MainControler.Show();
        }
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            MainControler.Hide();
        }
    }
}

运行结果:

  点击PlayerPanel数据对应更新。

unity的mvc三层架构 unity mvc ui_c#_06

三、小小小总结

  • 同一个模型数据可以被不同的视图反复调用,很大程度上提高了代码的重用
  • Model、View、Controller三个模块相互独立,降低了代码的耦合度


   当然在Unity中开发中MVC也不能适用,大多数在UI系统中才可以,比如复杂的UI逻辑中,且一般的小项目也没有必要使用。