UMVCSL7——PureMVC框架UI实例

注意!需要先将前面6节课的内容看完再来接着看这里的内容

本章代码关键字

1
2
3
4
5
6
7
8
Notifier.Facade                //继承了Notifier的类(SimpleCommand、Mediator、Proxy都继承了该类)可以直接调用场景上唯一的Facade
Notifier.SendNotification() //继承了Notifier的类可以直接发送通知
Facade.HasMediator() //传入Mediator的名字,检测是否有该Mediator
Facade.RegisterMediator() //传入Mediator对象,注册Mediator
Facade.RetrieveMediator() //传入Mediator的名字,获取特定的Mediator
Facade.HasProxy() //传入Proxy的名字,检测是否有该Proxy
Facade.RegisterProxy() //传入Proxy对象,注册Proxy
Facade.RetrieveProxy() //传入Proxy的名字,检测是否有该Proxy

显示面板

  • 将一个Command​命令与显示面板的通知绑定
  • 在该Command​命令的Execute()​方法内实现显示面板
  • 首先检测是否注册过Mediator​,没有就注册Mediator​,并将该Mediator​关联到View​控件
  • 通过Mediator​来执行显示面板方法

首先我们需要声明一个ShowPanelCommand​的命令,在该命令里实现显示面板的方法

1
2
3
4
5
6
7
8
9
10
11
12
using PureMVC.Interfaces;
using PureMVC.Patterns.Command;
using UnityEngine;

public class ShowPanelCommand : SimpleCommand
{
public override void Execute(INotification notification)
{
base.Execute(notification);
//写面板创建的逻辑
}
}

然后将该命令在Facade​类里注册,与SHOW_PANEL​这个通知绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using PureMVC.Patterns.Facade;

public class GameFacade : Facade
{
//套路写法
//1.继承PureMVC中的Facade脚本
//2.为了方便使用Facade 我们需要自己写一个单例模式的属性
//3.初始化 控制层相关的内容
//4.必须定义一个启动函数

//2.为了方便使用Facade 我们需要自己写一个单例模式的属性 ??= 是空赋值运算符,当instance为null时,执行运算符后面的表达式
public static GameFacade Instance => (instance ??= new GameFacade()) as GameFacade;

//3.初始化 控制层相关的内容
protected override void InitializeController()
{
base.InitializeController();
//这里面要写一些 关于 命令和通知 绑定的逻辑
RegisterCommand(PureNotification.START_UP, () => { return new StartUpCommand(); });
RegisterCommand(PureNotification.SHOW_PANEL, () => { return new ShowPanelCommand(); });
}

//4.必须定义一个启动函数
public void StartUp()
{
//发送通知,发送通知时可以传入object参数,供监听者调用这个object,就像事件中心那样
SendNotification(PureNotification.START_UP);
}
}

接下来即可在ShowPanelCommand​内实现显示面板的方法
我们可以通过Execute()​传入的notification.body​(通知者传入的对象参数)来判断显示的是哪个面板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;

public class Main : MonoBehaviour
{
void Start()
{
GameFacade.Instance.StartUp();
}

void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
//显示主面板
GameFacade.Instance.SendNotification(PureNotification.SHOW_PANEL, "MainPanel");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ShowPanelCommand : SimpleCommand
{
public override void Execute(INotification notification)
{
base.Execute(notification);
//写面板创建的逻辑
string panelName = notification.Body.ToString();
switch (panelName)
{
case "MainPanel":
break;
case "RolePanel":
break;
}
}
}

继承了Notifier​的类(SimpleCommand​、Mediator​、Proxy​都继承了该类)可以使用Facade​,以直接调用场景上唯一的Facade
因此可以在命令中直接使用Facade​代表的就是唯一的Facade

调用Facade​可以注册Mediator​,以及判断一个Mediator​是否被注册
注意!如果要使用Mediator,一定也要在Facade中去注册,commandproxy也一样,如果要通过Facade调用就要去注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ShowPanelCommand : SimpleCommand
{
public override void Execute(INotification notification)
{
base.Execute(notification);
//写面板创建的逻辑
string panelName = notification.Body.ToString();
switch (panelName)
{
case "MainPanel":
//显示面板相关内容
//如果要使用Mediator,一定也要在Facade中去注册
//command、proxy都是一样的,要用 就要去注册
//可以在命令中总结使用Facade代表的就是唯一的Facade

//注册面板的方式
//判断如果没有该Mediator,就注册一个这个Mediator
if (!Facade.HasMediator(NewMainViewMediator.NAME))
Facade.RegisterMediator(new NewMainViewMediator());
break;
case "RolePanel":
break;
}
}
}

mediator​之后,就是可以创建界面,创建预设体,并可以将界面的View​控件,关联到mediator​上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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中去注册
//command、proxy都是一样的,要用 就要去注册
//可以在命令中总结使用Facade代表的就是唯一的Facade

//注册面板的方式
//判断如果没有该Mediator,就注册一个这个Mediator
if (!Facade.HasMediator(NewMainViewMediator.NAME))
Facade.RegisterMediator(new NewMainViewMediator());
//有mediator之后,就是去创建界面,创建预设体
//先获取Mediator,Facade有得到Mediator的方法
NewMainViewMediator mainViewMediator = Facade.RetrieveMediator(NewMainViewMediator.NAME) as NewMainViewMediator;
//如果没有关联面板
if (mainViewMediator.ViewComponent == null)
{
GameObject res = Resources.Load<GameObject>("UI/MainPanel");
GameObject obj = GameObject.Instantiate(res);
//设置它的父对象为Canvas
obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
//将MainPanel的View控件关联给Mediator
mainViewMediator.ViewComponent = obj.GetComponent<NewMainView>();
}

break;
case "RolePanel":
//注册面板的方式
//判断如果没有该Mediator,就注册一个这个Mediator
if (!Facade.HasMediator(NewRoleViewMediator.NAME))
Facade.RegisterMediator(new NewRoleViewMediator());
//有mediator之后,就是去创建界面,创建预设体
//先获取Mediator,Facade有得到Mediator的方法
NewRoleViewMediator roleViewMediator = Facade.RetrieveMediator(NewRoleViewMediator.NAME) as NewRoleViewMediator;
//如果没有关联面板
if (roleViewMediator.ViewComponent == null)
{
GameObject res = Resources.Load<GameObject>("UI/RolePanel");
GameObject obj = GameObject.Instantiate(res);
//设置它的父对象为Canvas
obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
//将RolePanel的View控件关联给Mediator
roleViewMediator.ViewComponent = obj.GetComponent<NewRoleView>();
}
break;
}
}
}

我们还没有监听View​的用户输入行为,在可以在Mediator​里设置监听(甚至可以不用在Command​内执行关联)
继承了Notifier​的类(SimpleCommand​、Mediator​、Proxy​都继承了该类)可以直接使用SendNotification()

1
2
3
4
5
6
7
8
public void SetView(NewMainView view)
{
ViewComponent = view;
view.btnRole.onClick.AddListener(() =>
{
SendNotification(PureNotification.SHOW_PANEL, "RolePanel");
});
}
1
2
3
4
5
6
7
8
9
if (mainViewMediator.ViewComponent == null)
{
GameObject res = Resources.Load<GameObject>("UI/MainPanel");
GameObject obj = GameObject.Instantiate(res);
//设置它的父对象为Canvas
obj.transform.SetParent(GameObject.Find("Canvas").transform, false);
//将MainPanel的View控件关联给Mediator
mainViewMediator.SetView(obj.GetComponent<NewMainView>());
}

隐藏面板

  • 将一个Command​命令与隐藏面板的通知绑定
  • 在该Command​命令的Execute()​方法内实现隐藏面板
  • 得到Mediator​再得到Mediator​中的View
  • 通过View​来删除或隐藏UI对象

隐藏的目的,得到Mediator​再得到Mediator​中的View​,通过View​来删除或隐藏UI对象
这里通过外部通知者传入要隐藏面板的Mediator

注:这里notification.Body is Mediator mediator​使用了模式匹配的类型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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,通过View来删除或隐藏UI对象
//通过Body来获取Mediator对象
if (notification.Body is Mediator mediator && mediator.ViewComponent != null)
{
//直接删除场景上的对象
GameObject.Destroy((mediator.ViewComponent as MonoBehaviour).gameObject);
//删除后必须要置空
mediator.ViewComponent = null;
}
}
}

之后再在外部使用通知隐藏面板即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using UnityEngine;

public class Main : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
GameFacade.Instance.StartUp();
}

// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
//显示主面板
GameFacade.Instance.SendNotification(PureNotification.SHOW_PANEL, "MainPanel");
}
else if (Input.GetKeyDown(KeyCode.N))
{
//隐藏主面板
GameFacade.Instance.SendNotification(PureNotification.HIDE_PANEL, GameFacade.Instance.RetrieveMediator(NewMainViewMediator.NAME));
}
}
}

我们还没有监听View​的用户输入行为,在可以在Mediator​里设置监听(甚至可以不用在Command​内执行关联)
继承了Notifier​的类(SimpleCommand​、Mediator​、Proxy​都继承了该类)可以直接使用SendNotification()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using PureMVC.Interfaces;
using PureMVC.Patterns.Mediator;

public class NewRoleViewMediator : Mediator
{
public void SetView(NewRoleView view)
{
ViewComponent = view;
view.btnClose.onClick.AddListener(() =>
{
SendNotification(PureNotification.HIDE_PANEL, this);
});
view.btnLevUp.onClick.AddListener(() =>
{
//升级...
});
}
}

更新通知

往往显示了面板后,需要在这里进行第一次UI控件更新,就需要把数据作为参数一起传出去
但是目前的Facade​是没有Proxy​的数据的,因此我们需要为Proxy​注册,我们一般在启动函数里注册​Proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using PureMVC.Patterns.Facade;

public class GameFacade : Facade
{
//4.必须定义一个启动函数
public void StartUp()
{
//发送通知,发送通知时可以传入object参数,供监听者调用这个object,就像事件中心那样
SendNotification(PureNotification.START_UP);
//在启动命令中,往往是做一些初始化操作
//如果没有数据代理,就先注册数据代理
if (!HasProxy(PlayerProxy.NAME))
RegisterProxy(new PlayerProxy());

}
}

之后就可以通过Facade​调用RetrieveProxy()​方法传入PlayerProxy.NAME​来获取Proxy​,进而通过Proxy.data​来获取数据
同时要注意,通知是不一定需要和command​命令绑定在一起的,在Mediator​内也可以设置监听哪些通知,执行对应的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class NewMainViewMediator : Mediator // 1.继承PureMVC中的Mediator脚本
{
// 3.重写监听通知的方法
public override string[] ListNotificationInterests()
{
//这是一个PureMVC的规则
//就是你需要监听哪些通知,那就在这里吧通知们通过字符串数组的形式返回出去
//PureMVC就会帮助我们监听这些通知
//类似于 通过事件名 注册事件监听
return new string[] { PureNotification.UPDATE_PLAYER_INFO };
}

// 4.重写处理通知的方法
public override void HandleNotification(INotification notification)
{
//INotification 对象 里面包含两个对我们来说 重要的参数
//1. 通知名 我们根据这个名字 来做对应的处理
//2. 通知包含的信息
switch (notification.Name)
{
//和上面的ListNotificationInterests返回的字符串对应,这里就是如果监听到对应字符串处理相应的逻辑
case PureNotification.UPDATE_PLAYER_INFO:
(ViewComponent as NewMainView).UpdateInfo(notification.Body as PlayerDataObject);
break;
}
}
}

public class NewRoleViewMediator : Mediator
{
public override string[] ListNotificationInterests()
{
return new string[] { PureNotification.UPDATE_PLAYER_INFO };
//如果要监听别的通知,就在字符串数组后面在添加通知名即可
}

public override void HandleNotification(INotification notification)
{
switch (notification.Name)
{
case PureNotification.UPDATE_PLAYER_INFO:
(ViewComponent as NewRoleView).UpdateInfo( notification.Body as PlayerDataObject );
break;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//ShowPanelCommand内
public override void Execute(INotification notification)
{
base.Execute(notification);
//写面板创建的逻辑
string panelName = notification.Body.ToString();
switch (panelName)
{
case "MainPanel":
//显示面板相关代码,这里略,与上面相同...
//往往显示了面板后,需要在这里进行第一次UI控件更新,就需要把数据作为参数一起传出去
SendNotification(PureNotification.UPDATE_PLAYER_INFO, Facade.RetrieveProxy(PlayerProxy.NAME).Data);
break;
case "RolePanel":
//显示面板相关代码,这里略,与上面相同...
SendNotification(PureNotification.UPDATE_PLAYER_INFO, Facade.RetrieveProxy(PlayerProxy.NAME).Data);
break;
}
}

接下来监听升级按钮的输入,实现升级的方法

这里我们再声明一个用于升级的LevUpCommand​命令,以及对应的通知,并在Facade​内注册它

1
2
3
4
public class PureNotification
{
public const string LEVEL_UP = "levelUp";
}

注:这里Facade.RetrieveProxy(PlayerProxy.NAME) is PlayerProxy playerProxy​使用了模式匹配的类型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using PureMVC.Interfaces;
using PureMVC.Patterns.Command;

public class LevUpCommand : SimpleCommand
{
public override void Execute(INotification notification)
{
base.Execute(notification);
if (Facade.RetrieveProxy(PlayerProxy.NAME) is PlayerProxy playerProxy)
{
//升级
playerProxy.LevelUp();
//通知更新
SendNotification(PureNotification.UPDATE_PLAYER_INFO, playerProxy.Data);
}
}
}
1
2
3
4
5
6
7
8
9
10
//GameFacade内部
protected override void InitializeController()
{
base.InitializeController();
//这里面要写一些 关于 命令和通知 绑定的逻辑
RegisterCommand(PureNotification.START_UP, () => { return new StartUpCommand(); });
RegisterCommand(PureNotification.SHOW_PANEL, () => { return new ShowPanelCommand(); });
RegisterCommand(PureNotification.HIDE_PANEL, () => { return new HidePanelCommand(); });
RegisterCommand(PureNotification.LEVEL_UP, () => { return new LevUpCommand(); });
}

之后在Mediator​内监听升级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//NewRoleViewMediator内部
public void SetView(NewRoleView view)
{
ViewComponent = view;
view.btnClose.onClick.AddListener(() =>
{
SendNotification(PureNotification.HIDE_PANEL, this);
});
view.btnLevUp.onClick.AddListener(() =>
{
//这里应该通知去升级
SendNotification(PureNotification.LEVEL_UP);
});
}

值得一提的是,Mediator​内每次对其关联的ViewComponent​进行操作时,都应当判空,避免出现问题

1
2
3
4
5
6
7
8
9
10
11
//NewRoleViewMediator内部
public override void HandleNotification(INotification notification)
{
switch (notification.Name)
{
case PureNotification.UPDATE_PLAYER_INFO:
if (ViewComponent != null)
(ViewComponent as NewRoleView).UpdateInfo( notification.Body as PlayerDataObject );
break;
}
}