1、项目简介:
创建一个Cube,Cube上有一个Text的UI显示一个随机的分数,Text上的值也是随机的,当我们鼠标点击中一次Cube分数就会增加一。
运行逻辑是:开始的时候View通过Mediator向Controller请求数据,然后Controller向服务端请求数据,服务端Service返回一个随机值给Controller里面创建的请求数据的Command,Controller逻辑层再将数据传递给Mediator,Mediator再负责显示到View上,然后为了将数据保存下来Controller再将数据传给Models模型层。然后我们再创建一个点击更新数据的Command,在这个Command里面我们将模型层的数据加一,然后再将数据返回给Services.
首先我们的代码有这些,我们分为Command层,Model层,Service层,View层,就是MVCS,Command负责传递命名,Model负责保存数据,Service负责与服务器交互,View负责视图的显示,所有的代码架构如下:
2、实现步骤:
(1)首先创建ContextView开启整个框架,
public class Demo1ContextView : ContextView
{
private void Awake()
{
this.context = new Demo1Cntext(this, true);
context.Start();
}
}
(2)然后创建MVCSContext负责绑定各个事件:
public class Demo1Cntext : MVCSContext
{
public Demo1Cntext(MonoBehaviour View, bool autoStartup) : base(View, autoStartup) { }
protected override void mapBindings()//进行绑定映射
{
//model M
injectionBinder.Bind<ScoreModel>().To<ScoreModel>().ToSingleton();
injectionBinder.Bind<AudioManager>().To<AudioManager>().ToSingleton();
//service S
injectionBinder.Bind<IScoreService>().To<ScoreService>().ToSingleton();//ToSingleton表示这个对象只会在整个工程中生成一个
//command C
commandBinder.Bind(Demo1CommandEvent.RequestScore).To<RequestscoreCommand>();
commandBinder.Bind(Demo1CommandEvent.UpdateScore).To<UpdateScoreCommand>();
//mediator V
mediationBinder.Bind<CubeView>().To<CubeMediator>();//完成View和mediator的绑定
//绑定开始事件 一个StartCommand 这个StartCommand会立即调用
commandBinder.Bind(ContextEvent.START).To<StartCommand>().Once();//把哪个事件与自己的StartCommand绑定上
}
}
(3)首先是开始命令:
using strange.extensions.command.impl;
using UnityEngine;
public class StartCommand : Command
{
[Inject]
public AudioManager audioManager { get; set; }
public override void Execute()
{
audioManager.Init();
Debug.Log("StartCommand!");
}
}
(4)接下来是视图层的脚本显示UI部分:
public class CubeView : View
{
[Inject]
public IEventDispatcher dispacher { get; set; }
private Text scoreText;
/// <summary>
/// 做初始化
/// </summary>
public void Init()
{
scoreText = transform.Find("Canvas/ScoreText").GetComponent<Text>();
}
[Inject]
public AudioManager audioManager { get; set; }
private void OnMouseDown()
{
//加分
Debug.Log("OnMouDown");
audioManager.playAudio("explosive");
dispacher.Dispatch(Demo1MediatorEvent.ClickDown);
}
public void Updatescore(int score)
{
scoreText.text = score.ToString();
}
}
(5)接下来是视图层通过View层Mediator向Command发起分数的请求:
public class CubeMediator : Mediator
{
[Inject]//可以访问到与自身绑定的 CubeView 完成注入的意思
public CubeView cubeView { get; set; }
[Inject(ContextKeys.CONTEXT_DISPATCHER)]//表示全局的派发器
public IEventDispatcher dispatcher { get; set; }
public override void OnRegister()//注册 当属性都调用完成后就会去调用这个方法 在OnRegister里面可以访问这些属性
{
cubeView.Init();
dispatcher.AddListener(Demo1MediatorEvent.ScoreChange, OnScoreChange);//监听注册方法返回分数
cubeView.dispacher.AddListener(Demo1MediatorEvent.ClickDown, OnClickDown);
//通过dispatcher发起请求分数的命令
dispatcher.Dispatch(Demo1CommandEvent.RequestScore);
base.OnRegister();
}
public override void OnRemove()//当取消运行的时候会调用这个 当Mediator对应的View的视图被销毁的时候会调用OnRemove
{
dispatcher.RemoveListener(Demo1MediatorEvent.ScoreChange, OnScoreChange);//移除监听
cubeView.dispacher.RemoveListener(Demo1MediatorEvent.ClickDown, OnClickDown);
}
//将返回的分数传递给View层
public void OnScoreChange(IEvent evt)
{
cubeView.Updatescore((int)evt.data);
}
//加分
public void OnClickDown()
{
dispatcher.Dispatch(Demo1CommandEvent.UpdateScore);
}
}
(6)下面是几个发起请求需要的枚举值,有CommandEvent、ServiceEvent、MediatorEvent:
public enum Demo1CommandEvent
{
RequestScore,
UpdateScore
}
public enum Demo1MediatorEvent
{
ScoreChange,
ClickDown
}
public enum Demo1ServiceEvent
{
RequestScore
}
(7)接下来就是Command层了,有请求分数和更新分数的命令,所以两个Command:
public class RequestscoreCommand : EventCommand
{
[Inject]
public IScoreService scoreService { get; set; }
[Inject]
public ScoreModel scoreModel { get; set; }
public override void Execute()//表示命名执行的时候
{
Retain();//表示让这个请求先不销毁,等接收到数据后进行释放
//添加监听器,监听OnComplete方法,第一个参数表示方法的事件枚举类型, 第二个参数表示一个方法
scoreService.dispatcher.AddListener(Demo1ServiceEvent.RequestScore, OnComplete);
scoreService.RequestScore("http://xx/xx/xxx");
}
//这个方法表示当scoreService请求分数完成后就会调用这个方法去取得数据
private void OnComplete(IEvent evt) {//IEvent存储的就是参数
Debug.Log("request score complete"+evt.data);
scoreService.dispatcher.RemoveListener(Demo1ServiceEvent.RequestScore, OnComplete);//移除对OnComplete的监听
scoreModel.score = (int)evt.data;
dispatcher.Dispatch(Demo1MediatorEvent.ScoreChange, evt.data);
Release();//释放请求,销毁当前对象
}
}
public class UpdateScoreCommand : EventCommand
{
[Inject]
public ScoreModel ScoreModel { get; set; }
[Inject]
public IScoreService scoreServer { get; set; }
public override void Execute()
{
ScoreModel.score++;
scoreServer.UpdateScore("http://xx/xx", ScoreModel.score);
dispatcher.Dispatch(Demo1MediatorEvent.ScoreChange, ScoreModel.score);
}
}
(8)然后就是Servicevice层了,一个是接口,一个是实现接口的类
public interface IScoreService
{
void RequestScore(string url);//请求分数
void OnReceiveScore();//收到服务器端发送的分数
void UpdateScore(string url, int Score);//更新分数
IEventDispatcher dispatcher { get; set; }
}
public class ScoreService : IScoreService
{
[Inject]
public IEventDispatcher dispatcher { get; set; }
public void RequestScore(string url)请求分数
{
Debug.Log("Request Score from url:" + url);
OnReceiveScore();
}
public void OnReceiveScore()收到服务器端发送的分数
{
int score = Random.Range(0, 100);
dispatcher.Dispatch(Demo1ServiceEvent.RequestScore, score);//通过Demo1CommandEvent.RequeestScore这个事件将数据发送出去
}
public void UpdateScore(string url, int Score)//更新分数
{
Debug.Log("Update score to url:"+url+"new score:"+Score);
}
}
(9)最后就是模型层,保存分分数,数据:
public class ScoreModel
{
public int score { get; set; }
}
(10)至于AudioManager在这里只用于音乐播放
(11)这样就完成了,开始请求获取一个初始分数,往后每点击Cube一次数据就会更新一次数据+1。
(12)运行结果为:
点击运行时:
鼠标点击方块上时:
3、总结:
(1)理清项目的流程:
当程序刚运行起来的时候,由于我们调用了ContextView的context.Start函数,而在Context中将ContextEvent.START事件与StartCommand绑定了起来,所以调用StartCommand的Execute函数执行如下代码:
audioManager.Init();//加载音效文件
Debug.Log("StartCommand!");
故控制台会打印出StartCommand!
随后,在将CubeView与CubeViewMediator进行绑定的时候,调用CubeViewMediator的Register函数,其中有这样几句代码:
dispatcher.AddListener(Demo1MediatorEvent.ScoreChange, OnScoreChange);//监听注册方法返回分数
cubeView.dispacher.AddListener(Demo1MediatorEvent.ClickDown, OnClickDown);
//通过dispatcher发起请求分数的命令
dispatcher.Dispatch(Demo1CommandEvent.RequestScore);
在执行到dispatcher.Dispatch(Demo1CommandEvent.RequestScore); 的时候会调用RequestscoreCommand的Execute方法:
scoreService.dispatcher.AddListener(Demo1ServiceEvent.RequestScore, OnComplete);
scoreService.RequestScore("http://xx/xx/xxx");
在执行上述的第一句代码时,意思是当有Demo1ServiceEvent.RequestScore发生时,调用OnComplete函数
在执行第二句代码时:
调用ScoreService类中的方法:
public void RequestScore(string url)请求分数
{
Debug.Log("Request Score from url:" + url);
OnReceiveScore();
}
public void OnReceiveScore()收到服务器端发送的分数
{
int score = Random.Range(0, 100);
dispatcher.Dispatch(Demo1ServiceEvent.RequestScore, score);//通过Demo1CommandEvent.RequeestScore这个事件将数据发送出去
}
于是控制台打印出Request Score from url:http://xx/xx/xxx
同时在执行OnReceiveScore时由于发送了Demo1ServiceEvent.RequestScore事件,承接上文于是调用RequestscoreCommand的OnComplete方法
Debug.Log("request score complete"+evt.data);
scoreService.dispatcher.RemoveListener(Demo1ServiceEvent.RequestScore, OnComplete);//移除对OnComplete的监听
scoreModel.score = (int)evt.data;
dispatcher.Dispatch(Demo1MediatorEvent.ScoreChange, evt.data);
Model层保存数据,发送一个Demo1MediatorEvent.ScoreChange事件,不要忘了在CubeMediator中对这个事件是有监听的,触发OnScoreChange方法,执行对CubeView的操作
cubeView.Updatescore((int)evt.data);
而当鼠标点击CubeView上时,调用CubeView中的OnMouseDown方法:
Debug.Log("OnMouDown");
audioManager.playAudio("explosive");
dispacher.Dispatch(Demo1MediatorEvent.ClickDown);
控制台打印OnMouDown,同时播放音效,然后发送Demo1MediatorEvent.ClickDown事件,谁来接收呢?
在CubeMediator中有过接收:
dispatcher.Dispatch(Demo1CommandEvent.UpdateScore);
但不做处理,发出Demo1CommandEvent.UpdateScore事件,被UpdateScoreCommand所接收,执行Execute方法:
ScoreModel.score++;
scoreServer.UpdateScore("http://xx/xx", ScoreModel.score);
dispatcher.Dispatch(Demo1MediatorEvent.ScoreChange, ScoreModel.score);
Model层更新数据,Server层更新分数,实际上只是打印输出,发出Demo1MediatorEvent.ScoreChange事件,又重新被CubeMediator类捕获到,执行
public void OnScoreChange(IEvent evt)
{
cubeView.Updatescore((int)evt.data);
}
由CubeView执行更新UI的操作。
(2)带Event前缀,例如EventCommand,EventView,EventMediator都可以直接访问到dispatcher,而无需再注入一个dispacher,类似全局变量和局部变量。
(3)在进行绑定时:Model层和Server层后面加了ToSingleton();
Inject注入,实际上就类似于声明一个变量。
Demo下载地址:https://download.csdn.net/download/dmk17771552304/10776694
原文链接在这里https://www.jianshu.com/p/663a36dce0de,但是他写的有问题,在后面我又重新梳理了一下整个运行逻辑。