前言

        Pure MVC是在基于模型、视图和控制器MVC模式建立的一个轻量级的应用框架,这种开源框架是免费的,它最初是执行的ActionScript 3语言使用的Adobe Flex、Flash和AIR,已经移植到几乎所有主要的发展平台,支持两个版本框架:标准和多核,总之,标准版提供了一种简单的编码分离的方法,按照MVC设计概念。

        官方网址:http://puremvc.org/

        下载链接(c#):https://codeload.github.com/PureMVC/puremvc-csharp-standard-framework/zip/refs/heads/master

        官方文档:进入网站后一直下滑,找到China国旗,点击Read PDF既能看到官方的中文PDF文档。

其基本结构为MVC加上常用的设计模式

  • Model(数据模型):关联Proxy(代理)对象,负责处理数据。
  • View(界面):关联Mediator(中介)对象,负责处理界面。
  • Controller(业务控制):管理Command(命令)对象,负责处理业务逻辑。
  • Facade(外观):是MVC三者的经纪人,统管全局,可以获取代理、中介、命令。
  • Notification :通知,负责传递信息。

 导入Unity

Pure MVC导入到Unity中有两种方式:

第一种方法

先打开工程文件

在解决方案处点击生成 

在puremvc-csharp-standard-framework-master\PureMVC\bin\Debug\net5.0这个路径 复制dll文件到Unity的Plugins文件里

第二种方法

在puremvc-csharp-standard-framework-master\PureMVC路径直接复制这三个文件到Unity里

PureMVC思想

Model

        Model在PureMVC中主要负责保存对Proxy对象的引用,并通过这些Proxy对象来操作数据模型,以及与远程服务进行通信以存取数据。Model是MVC架构中的核心部分之一,它负责管理和维护应用程序的数据状态。

Proxy的作用

        Proxy在PureMVC中充当数据模型和应用程序其他部分之间的中介。它提供了一个包装器或中介,使客户端能够方便地访问和操作场景背后的真实对象。Proxy可以管理数据对象(Data Object)以及对数据对象的访问,包括数据的查询、插入、更新和删除等操作。

Obj(Data Objects)类

/// <summary>
/// 玩家数据结构
/// </summary>
public class PlayerDataObj
{
    //申明一堆玩家属性相关的变量
    public string playerName;
    public int lev;
    public int money;
    public int gem;
    public int power;

    public int hp;
    public int atk;
    public int def;
    public int crit;
    public int miss;
    public int luck;
}

Proxy(代理)类

using PureMVC.Patterns.Proxy;
using UnityEngine;

/// <summary>
/// 玩家数据代理对象
/// 主要处理 玩家数据更新相关的逻辑
/// </summary>
public class PlayerProxy : Proxy
{
    //1.继承Proxy父类
    //2.写构造函数

    public new const string NAME = "PlayerProxy";

    public PlayerProxy() : base(PlayerProxy.NAME)
    {
        //在构造函数中 初始化一个数据 进行关联
        PlayerDataObj data = new PlayerDataObj();

        //初始化
        data.playerName = PlayerPrefs.GetString("PlayerName","玩家01");
        data.lev = PlayerPrefs.GetInt("PlayerLev", 1);
        data.money = PlayerPrefs.GetInt("PlayerMoney",9999);
        data.gem = PlayerPrefs.GetInt("PlayerGem",8888);
        data.power = PlayerPrefs.GetInt("PlayerPower",99);

        data.hp = PlayerPrefs.GetInt("PlayerHp", 100);
        data.atk = PlayerPrefs.GetInt("PlayerAtk", 20);
        data.def = PlayerPrefs.GetInt("PlayerDef", 10);
        data.crit = PlayerPrefs.GetInt("PlayerCrit", 20);
        data.miss = PlayerPrefs.GetInt("PlayerMiss", 10);
        data.luck = PlayerPrefs.GetInt("PlayerLuck", 40);

        //赋值给自己的Data进行关联
        Data = data;
    }

    public void LevUp()
    {
        PlayerDataObj data = Data as PlayerDataObj;

        //升级 改变内容
        data.lev += 1;

        data.hp += data.lev;
        data.atk += data.lev;
        data.def += data.lev;
        data.crit += data.lev;
        data.miss += data.lev;
        data.luck += data.lev;
    }

    public void SaveDate()
    {
        PlayerDataObj data = Data as PlayerDataObj;

        //将数据存储到本地
        PlayerPrefs.SetString("PlayerName",data.playerName);
        PlayerPrefs.SetInt("PlayerLev",data.lev);
        PlayerPrefs.SetInt("PlayerMoney",data.money);
        PlayerPrefs.SetInt("PlayerGem",data.gem);
        PlayerPrefs.SetInt("PlayerPower",data.power);

        PlayerPrefs.SetInt("PlayerHp",data.hp);
        PlayerPrefs.SetInt("PlayerAtk",data.atk);
        PlayerPrefs.SetInt("PlayerDef",data.def);
        PlayerPrefs.SetInt("PlayerCrit",data.crit);
        PlayerPrefs.SetInt("PlayerMiss",data.miss);
        PlayerPrefs.SetInt("PlayerLuck",data.luck);

    }


}

View

Mediator的作用

        一个Mediator通常关联着多个UI组件,负责它们的创建、更新和销毁等生命周期管理。Mediator可以监听来自UI组件的事件,并将这些事件转发给PureMVC框架中的其他部分(如Command或Proxy),同时也可以接收来自其他部分的通知,并据此更新UI组件。

UI(View Components类)

主面板

using UnityEngine;
using UnityEngine.UI;

public class MainView : MonoBehaviour
{
    //1.寻找控件
    public Button btnRole;
    public Button btnSill;

    public Text txtName;
    public Text txtLev;
    public Text txtMoney;
    public Text txtGem;
    public Text txtPower;

    //2.提供面板更新的相关方法给外部
    public void UpdateInfo(PlayerDataObj data)
    {
        txtName.text =data.playerName;
        txtLev.text = "LV." + data.lev;
        txtMoney.text = data.money.ToString();
        txtGem.text = data.gem.ToString();
        txtPower.text = data.power.ToString();
    }
}

角色面板

using UnityEngine;
using UnityEngine.UI;

public class RoleView : MonoBehaviour
{
    //1.寻找控件
    public Button btnClose;
    public Button btnLevUp;

    public Text txtLev;
    public Text txtHp;
    public Text txtAtk;
    public Text txtDef;
    public Text txtCrit;
    public Text txtMiss;
    public Text txtLuck;

    //2.提供面板更新的相关方法给外部
    public void UpdateInfo(PlayerDataObj data)
    {
        txtLev.text = "Lv."+ data.lev;
        txtHp.text = data.hp.ToString();
        txtAtk.text = data.atk.ToString();
        txtDef.text = data.def.ToString();
        txtCrit.text = data.crit.ToString();
        txtMiss.text = data.miss.ToString();
        txtLuck.text = data.luck.ToString();
    }
}

Mediator(中介者)类

主面板管理

using PureMVC.Interfaces;
using PureMVC.Patterns.Mediator;

public class MainViewMediator : Mediator
{
    public static new string NAME = "MainViewMediator";
    //套路写法

    //1.继承PureMVC中的Meditor脚本

    //2.写构造函数
    public MainViewMediator() : base(NAME)
    {
        //这里写创建界面预设体等等的逻辑
        //但是界面显示应该是触发的控制的
        //而且创建界面的代码 重复性比较高
    }
    //3.重写监听通知方法
    public override string[] ListNotificationInterests()
    {
        //PureMVC的规则
        //需要监听哪些通知 就在这里将通知们通过字符串数组的形式返回出去
        //PureMVC就会帮助我们监听这些通知(类似于通过事件名 注册事件监听)
        return new string[] {
            PureNotification.UPDATE_PLAYE_INFO,
        };
    }
    //4.重写处理通知方法
    public override void HandleNotification(INotification notification)
    {
        //INotification 对象 里面包含两个对我们来说非常重要的参数
        //(1).通知名 我们根据这个名字 来做对应的处理
        //(2).通知包含的信息
        switch(notification.Name)
        {
            case PureNotification.UPDATE_PLAYE_INFO:
                //收到 更新通知的时候 做处理
                if(ViewComponent !=null)
                {
                    (ViewComponent as MainView).UpdateInfo(notification.Body as PlayerDataObj);
                }
                break;
        }
    }


    //5.可选:重写注册时的方法
    public override void OnRegister()
    {
        base.OnRegister();
        //初始化一些内容
    }

    public void SetView(MainView view)
    {
        ViewComponent = view;
        view.btnRole.onClick.AddListener(() =>
        {
            //继承了Mediator,Command,Proxy的类可以直接使用发送通知的方法
            SendNotification(PureNotification.SHOW_PANEL, "RolePanel");
        });
    }
}

角色面板管理

using PureMVC.Interfaces;
using PureMVC.Patterns.Mediator;

public class RoleViewMediator : Mediator
{
    public static new string NAME = "MainViewMediator";

    //1.继承PureMVC中的Mediator脚本
    //2.写构造函数
    public RoleViewMediator() : base(NAME)
    {

    }

    //3.重写监听通知的方法
    public override string[] ListNotificationInterests()
    {
        //PureMVC的规则
        //需要监听哪些通知 就在这里将通知们通过字符串数组的形式返回出去
        //PureMVC就会帮助我们监听这些通知(类似于通过事件名 注册事件监听)
        return new string[] {
            PureNotification.UPDATE_PLAYE_INFO,
            //关心别的通知 就在这后面通过逗号加起来连接
        };
    }

    //4.重写处理通知的方法
    public override void HandleNotification(INotification notification)
    {
        //INotification 对象 里面包含两个对我们来说 重要的参数
        //1:通知名 根据这个名字来做对应的处理
        //2.通知包含的信息
        switch (notification.Name)
        {
            case PureNotification.UPDATE_PLAYE_INFO:
                //玩家数据更新 逻辑处理
                if(ViewComponent != null )
                {
                    (ViewComponent as RoleView).UpdateInfo(notification.Body as PlayerDataObj);
                }
                break;
        }
    }

    public void SetView(RoleView view)
    {
        ViewComponent = view;
        //关闭按钮事件监听
        view.btnClose.onClick.AddListener(() =>
        {
            //继承了Mediator,Command,Proxy的类可以直接使用发送通知的方法
            SendNotification(PureNotification.HIDE_PANEL, this);
        });

        //升级按钮监听
        view.btnLevUp.onClick.AddListener(() =>
        {
            //升级
            //通知升级
            SendNotification(PureNotification.LEV_UP);
        });
    }
}

Facade

        Facade为PureMVC中的Model、View和Controller提供了一个统一的访问接口。开发者不需要直接访问这些子系统的具体实现,只需要通过Facade即可。Facade通常被实现为单例模式,以确保整个系统中只有一个Facade实例。

using PureMVC.Patterns.Facade;

public class GameFacade : Facade
{
    //1.继承PureMVC中Facade脚本

    //2.为了方便我们使用Facade 学要自己写一个单例模式的属性
    public static GameFacade Instance
    {
        get
        {
            if(instance==null)
            {
                instance = new GameFacade();
            }
            return instance as GameFacade;
        }
    }

    //3.初始化控制层相关的内容
    protected override void InitializeController()
    {
        base.InitializeController();
        //这里写一些 关于命令和通知绑定的逻辑

        //注册初始化命令
        RegisterCommand(PureNotification.START_UP,()=>
        {
            return new StartUpCommand();
            //当发送START_UP命令时返回一个StartUpCommand()命令并自动执行Excute方法
        });

        //注册显示面板命令
        RegisterCommand(PureNotification.SHOW_PANEL, () =>
        {
            return new ShowPanelCommand();
        });

        //注册隐藏面板命令
        RegisterCommand(PureNotification.HIDE_PANEL, () =>
        {
            return new HidePanelCommand();
        });

        //注册玩家升级命令
        RegisterCommand(PureNotification.LEV_UP, () =>
        {
            return new LevUpCommand();
        });

    }

    //4.启动函数
    public void StartUp()
    {
        //发送通知
        //(1)第一个参数为想要执行的命令
        //(2)第二个参数为传入的参数string类型的notification.Body


        SendNotification(PureNotification.START_UP);//这里是真正开始执行命令的逻辑

        //SendNotification(PureNotification.SHOW_PANEL,"MainPanel");
    }
}

Controller

        Controller是PureMVC框架中的消息处理中心,它接收来自View的通知(Notification),并根据通知的类型调用相应的Command来执行特定的业务逻辑。

        在PureMVC中,Command通过注册到Facade上,可以接收来自View或Model的通知。这些通知通常包含了触发事件的相关信息。当Command接收到通知后,它会根据通知的类型和携带的信息,执行相应的业务逻辑。

PureMVC通知类

/// <summary>
/// 这个是pureMVC中的 通知类
/// 主要是用来申明各个通知的 名字
/// 方便使用和管理
/// </summary>
public class PureNotification
{
    //启动通知
    public const string START_UP = "startUp";

    //显示面板通知
    public const string SHOW_PANEL = "showPanel";

    //隐藏面板通知
    public const string HIDE_PANEL = "hidePanel";

    //代表玩家数据更新的通知名
    public const string UPDATE_PLAYE_INFO = "updatePlayerInfo";

    //玩家升级通知
    public const string LEV_UP = "levUp";
}

初始化命令

using PureMVC.Interfaces;
using PureMVC.Patterns.Command;
public class StartUpCommand : SimpleCommand
{
    //1.继承Command相关的脚本
    //2.重写里面的执行函数
    public override void Execute(INotification notification)
    {
        base.Execute(notification);
        //当命令被执行时 就会调用该方法

        //启动命令中往往是做一些初始化操作

        //没有这个数据代理 才注册 有了就别注册
        if(!Facade.HasProxy(PlayerProxy.NAME))
        {
            Facade.RegisterProxy(new PlayerProxy());
        }
    }
}

显示面板命令

using PureMVC.Interfaces;
using PureMVC.Patterns.Command;
using UnityEngine;
public class ShowPanelCommand : SimpleCommand
{
    public override void Execute(INotification notification)
    {
        base.Execute(notification);
        //写面板创建的逻辑
        string panelName = notification.Body.ToString();

        switch(panelName)
        {
            case "MainPanel":
                //显示主面板相关内容

                //如果要使用Mediator 一定也要在Facade中去注册
                //commend、proxy都是一样的 想要使用 就要注册

                //可以在命令中直接使用Facade代表的就是唯一的Facade
                if(!Facade.HasMediator(MainViewMediator.NAME))//判断Facade里面有没有Mediator,没有就实例化一个,防止Mediator重复
                {
                    Facade.RegisterMediator(new MainViewMediator());//新建一个Mediator对象,将它传入Facade里面注册
                }
                //Facade 得到Mediator的方法
                MainViewMediator mainMe = Facade.RetrieveMediator(MainViewMediator.NAME) as MainViewMediator;

                //有了Mediator之后创建界面 预设体

                if(mainMe.ViewComponent == null)
                {
                    //实例化面板
                    GameObject res = Resources.Load<GameObject>("UI/MainPanel");
                    GameObject obj = GameObject.Instantiate(res);

                    //设置它的父对象 为Canvans
                    obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
                    //得到预设体上的 view脚本 关联到mediator上
                    mainMe.SetView(obj.GetComponent<MainView>());
                }
                //实现面板后在这里进行更新
                //需要把数据通过参数传出去
                SendNotification(PureNotification.UPDATE_PLAYE_INFO,Facade.RetrieveProxy(PlayerProxy.NAME).Data);

                break;
            case "RolePanel":

                //显示角色面板相关内容
                if (!Facade.HasMediator(MainViewMediator.NAME))
                {
                    Facade.RegisterMediator(new MainViewMediator());
                }

                RoleViewMediator roleMe = Facade.RetrieveMediator(MainViewMediator.NAME) as RoleViewMediator;
                
                if (roleMe.ViewComponent == null)
                {
                    GameObject res = Resources.Load<GameObject>("UI/RolePanel");
                    GameObject obj = GameObject.Instantiate(res);

                    obj.transform.SetParent(GameObject.Find("Canvas").transform, false);

                    roleMe.SetView(obj.GetComponent<RoleView>());
                }

                SendNotification(PureNotification.UPDATE_PLAYE_INFO, Facade.RetrieveProxy(PlayerProxy.NAME).Data);

                break;
        }
    }
}

隐藏面板命令

using PureMVC.Interfaces;
using PureMVC.Patterns.Command;
using PureMVC.Patterns.Mediator;
using UnityEngine;

public class HidePanelCommand : SimpleCommand
{
    public override void Execute(INotification notification)
    {
        base.Execute(notification);
        //隐藏的目的
        //得到mediator 在得到mediator 中的 view 然后去要不删除 要不 设置显隐
        //得到传入的 mediator
        Mediator m =notification.Body as Mediator;

        if (m!=null && m.ViewComponent != null)
        {
            //直接删除场景上的面板对象
            GameObject.Destroy((m.ViewComponent as MonoBehaviour).gameObject);
            //删除后置空
            m.ViewComponent = null;
        }
    }
}

升级数据命令

using PureMVC.Interfaces;
using PureMVC.Patterns.Command;

public class LevUpCommand : SimpleCommand
{
    public override void Execute(INotification notification)
    {
        base.Execute(notification);

        //得到数据代理 调用升级 升级完成后通知别人 更新数据
        PlayerProxy playerProxy =Facade.RetrieveProxy(PlayerProxy.NAME) as PlayerProxy;

        if(playerProxy != null)
        {
            //升级
            playerProxy.LevUp();

            //保存数据
            //playerProxy.SaveDate();

            //通知更新
            SendNotification(PureNotification.UPDATE_PLAYE_INFO,playerProxy.Data);
        }
    }
}

总结

        在PureMVC中,通常遵循一种特定的开发流程或“套路”来构建应用程序。这个流程大致可以概括为:先定义Model数据,再设计View界面,然后使用Command命令进行模块间的串联,最后通过Facade类进行模块的判断、注册和获取。

更多推荐