strangeioc test learn
文中所有步骤根据StrangeIoc框架示意图来的,这个图得下载地址:http://pan.baidu.com/s/1hs3bOAo
我们首先要创建root,作为启动框架的脚本,资料里面说这个类还做了初始化,到底是什么初始化,暂时也还不了解,先创建出来再修改。
/// <summary>
/// 图解上面的ROOT创建完成,用作启动框架
/// </summary>
public class Demo1ContextView : ContextView {
void Awake()
{
this.context=new Demo1Context(this);
}
}
挂载在层次面板中的空物体身上:
按照图解第二步应该是创建MVCSContext,这个脚本做了为框架做了哪些职责?用来做模块绑定映射的,映射是StrangeIoc框架一个核心点
/// <summary>
/// 框架中的MVCS Context创建完成,用来做 进行绑定映射
/// </summary>
public class Demo1Context : MVCSContext {
public Demo1Context(MonoBehaviour view) : base(view)
{
}
//进行绑定映射
protected override void mapBindings()
{
//绑定映射分为四类绑定
//model
injectionBinder.Bind<ScoreModel>().To<ScoreModel>().ToSingleton();//数据模型绑定
//server
injectionBinder.Bind<IScoreService>().To<ScoreService>().ToSingleton();//指定执行IScoreService绑定ScoreService
//command
commandBinder.Bind(Demo1CommandEvent.REQUEST_SCORE).To<RequestScoreCommand>();
commandBinder.Bind(Demo1CommandEvent.UPDATE_SCORE).To<UpdateScoreCommand>();
//mediator
mediationBinder.Bind<CubeView>().To<CubeMediator>();//完成view和mediator的绑定
//既然我们已经创建了一个开始命令*(StartCommand),这个命名
//是要立即调用的,接下来做命令绑定
commandBinder.Bind(ContextEvent.START).To<StartCommand>().Once();
//once 表示只会触发一次
}
}
因为
StartCommand是要首先立即调用的,前面的model,server,command,mediator绑定之后不是立即调用,model,server..都是后续完善的。
/// <summary>
/// 这是我们的开始命名,需要继承控制器 做一些初始化操作
/// </summary>
public class StartCommand : Command
{
/// <summary>
/// 当这个命名被执行的时候,会默认调用Excute方法
/// </summary>
public override void Execute()
{
Debug.LogError("Start StrangeIoc FrameWork...");
}
}
点击运行游戏,测试效果图:
创建分数模型,和Iscoreservice接口
/// <summary>
/// 数据模型 承载数据就行,并不需要继承
/// </summary>
public class ScoreModel
{
public int Score { get; set; }
}
/// <summary>
/// 创建service接口
/// </summary>
public interface IScoreService
{
//模拟连接服务器请求分数
void RequestScore(string url);
//模拟收到分数
void OnReceiveScore();
//模拟更新分数
void UpdateScore(string url, int score);
IEventDispatcher dispatcher { get; set; }
}
接下来在service中创建Scoreservice用来实现接口
public class ScoreService : IScoreService {
public void RequestScore(string url)
{
Debug.LogError("Request score from url:" + url);
OnReceiveScore();
}
public void OnReceiveScore()
{
//模拟服务器接收到的数据
int score = Random.Range(1, 100);
//开始派送事件(通知事件)
dispatcher.Dispatch(Demo1ServiceEvent.REQUEST_SCORE,score);
}
public void UpdateScore(string url, int score)
{
Debug.LogError("Update score to url" + url + "new score" + score);//给服务器更新分数
}
/*
注入
*/
[Inject]
public IEventDispatcher dispatcher { get; set; }
}
第二部分 视图层 view 和mediator
public class CubeView : View{
private float mCountTimer = 0f;
public Text mText;
[Inject]
public IEventDispatcher dispatcher { get; set; } //局部的派送器
/// <summary>
/// 初始化函数(由对应的mediator来调用,如CubeMediator)
/// </summary>
public void Init()
{
}
/// <summary>
/// 游戏体自身控制逻辑
/// </summary>
void Update()
{
mCountTimer += Time.deltaTime;
if (mCountTimer > 0.5f)
{
this.transform.position = (new Vector3(Random.Range(-11, 10), Random.Range(-2, 5), 0));
mCountTimer = 0f;
}
}
/// <summary>
/// 玩家交互
/// </summary>
void OnMouseDown()
{
print("click this gameobject.");
//in...
//玩家点击之后开始要涨分数,所以从这里开始调用框架
// 其实内部逻辑是一个事件管理器的注册与通知,同时事件id为枚举类型
dispatcher.Dispatch(Demo1MediatorEvent.CLICKDOWN);
}
/// <summary>
/// 提供给外界用来更新UI分数的方法(由对应的mediator来调用,如CubeMediator)
/// </summary>
/// <param name="score"></param>
public void UpdateScore(int score)
{
mText.text = score.ToString();
}
}
view层和外界的交互是通过mediator,一个view对应一个mediator
/// <summary>
/// 每个模块(view)对应一个自身的mediator
/// </summary>
public class CubeMediator : Mediator
{
/*
注入:是为了方便访问数据,比如注入view,model.....
*/
[Inject] //依赖注入
//将对应的view注入进来,这个脚本会自动添加到view模块上面
//见下面图示
public CubeView cubeView { get; set; }
[Inject]
public ScoreModel scoreModel { get; set; }
[Inject(ContextKeys.CONTEXT_DISPATCHER)]//全局dispatch //一定要添加任何地方都可以这么访问到全局派发器
public IEventDispatcher dispatcher { get; set; }
/// <summary>
/// 注册,模块激活时调用,当挂载到对应模块上面时候
/// </summary>
public override void OnRegister()
{
cubeView.Init();
dispatcher.AddListener(Demo1MediatorEvent.SCORECHANGE, OnScoreChange);
cubeView.dispatcher.AddListener(Demo1MediatorEvent.CLICKDOWN, OnClickDown);
//通过dispatcher发起请求分数的命令
dispatcher.Dispatch(Demo1CommandEvent.REQUEST_SCORE);
}
/// <summary>
/// 解注册 模块销毁时候调用,解注册所有之前添加的事件
/// </summary>
public override void OnRemove()
{
dispatcher.RemoveListener(Demo1MediatorEvent.SCORECHANGE, OnScoreChange);
cubeView.dispatcher.RemoveListener(Demo1MediatorEvent.CLICKDOWN, OnClickDown);
}
/// <summary>
/// CubeMediator向CubeView回传数据的CallBack
/// </summary>
/// <param name="varEvt">回传的服务器数据</param>
public void OnScoreChange(IEvent varEvt)
{
//int varData = (int) varEvt.data;//解包数据
//scoreModel.Score = (int) varEvt.data; 这里也可与注入数据模型
//取数据模型当中数据
cubeView.UpdateScore(scoreModel.Score);
// cubeView.UpdateScore(varData);
}
/// <summary>
/// 加分函数
/// </summary>
public void OnClickDown()
{
dispatcher.Dispatch(Demo1CommandEvent.UPDATE_SCORE);
}
}
第三部分 创建Command命令,模块之间的通信通过dispatch来进行...在mediator里面访问dispatch,这样就降低了模块的耦合性。在此之前我们先定义好事件id类型
/// <summary>
/// 命令类型
/// </summary>
public enum Demo1CommandEvent
{
REQUEST_SCORE,
UPDATE_SCORE,
}
/// <summary>
/// 分数模块的事件类型
/// </summary>
public enum Demo1MediatorEvent
{
SCORECHANGE,//1
CLICKDOWN,
}
/// <summary>
/// 服务器事件类型
/// </summary>
public enum Demo1ServiceEvent
{
REQUEST_SCORE,
}
看看是如何进行绑定的,没有绑定模块之间的通信就构建不起来。
/// <summary>
/// Command要和事件id绑定起来,在context里面进行绑定
/// </summary>
public class RequestScoreCommand : EventCommand {
//please check context binding......
/*
//绑定映射分为四类绑定
//model
injectionBinder.Bind<ScoreModel>().To<ScoreModel>().ToSingleton();//数据模型绑定
//server
injectionBinder.Bind<IScoreService>().To<ScoreService>().ToSingleton();//指定执行IScoreService绑定ScoreService
//command
commandBinder.Bind(Demo1CommandEvent.REQUEST_SCORE).To<RequestScoreCommand>();
commandBinder.Bind(Demo1CommandEvent.UPDATE_SCORE).To<UpdateScoreCommand>();
//mediator
mediationBinder.Bind<CubeView>().To<CubeMediator>();//完成view和mediator的绑定
*/
[Inject]
public IScoreService scoreService { get; set; }
/// <summary>
/// 注入数据模型
/// </summary>
[Inject]
public ScoreModel scoreModel { get; set; }
/* 访问全局派发器的方式
/// <summary>
/// 访问全局派发器
/// </summary>
[Inject(ContextKeys.CONTEXT_DISPATCHER)]
public IEventDispatcher dispatcher { get; set; }
或者将command继承修改成eventCommand
*/
public override void Execute()
{
/*
tip:prevent destory.
*/
Retain();
//注册派送器:派送标签 Demo1ServiceEvent.REQUEST_SCORE 派送事件:OnComplete
/*
notice:waring register dispatcher handler pos.
*/
scoreService.dispatcher.AddListener(Demo1ServiceEvent.REQUEST_SCORE, OnComplete);
scoreService.RequestScore("http://www.ciso.com");
}
/// <summary>
/// 服务器回包
/// </summary>
/// <param name="varEvt">回包数据</param>
void OnComplete(IEvent varEvt)
{
Debug.LogError("Request score from server :" + varEvt.data);
//销毁注册的派送器
scoreService.dispatcher.RemoveListener(Demo1ServiceEvent.REQUEST_SCORE,OnComplete);
//将服务器回包数据存于数据模型中
scoreModel.Score = (int) varEvt.data;
//将回包数据传递给 CubeMediator
dispatcher.Dispatch(Demo1MediatorEvent.SCORECHANGE, varEvt.data);
/*
tip:release,used to save resources
*/
Release();
}
}
只有通过了绑定才能在mediator里面通过事件注册通知的方式去发送请求。
//通过dispatcher发起请求分数的命令
dispatcher.Dispatch(Demo1CommandEvent.REQUEST_SCORE);
好接下来我们通过图解来更加深入的了解一下这个流程:
创建更新分数Command命令,并进行绑定。
public class UpdateScoreCommand : EventCommand
{
[Inject]
public ScoreModel scoreModel { get; set; }
[Inject]
public IScoreService scoreService { get; set; }
public override void Execute()
{
int varData=scoreModel.Score++;
scoreService.UpdateScore("http://www.ciso.com", varData);
dispatcher.Dispatch(Demo1MediatorEvent.SCORECHANGE,varData);
}
}
所有的数据请求,数据回传都是 通过全局的dispatch来实现的,这样能够将各个模块之间的耦合性降低,提高效率。最后还是使用图解和文字将“请求分数”“回传分数”“显示分数”进行解释,能够使自己对这个流程能够有更加深入细致的理解,了解游戏分层思想!
首先,模块入口是从下面这个玩家交互开始的:
接下里开始寻找哪里注册了这个事件类型,由于我们知道Cubeview视图层,每个视图层都有一个Mediator进行对应,主要用来处理模块之前的沟通通信,那我们来到这个脚本,可以看到对应的事件类型(Demo1MediatorEvent.CLICKDOWN),前请提示,之所以我们能这么做是因为我们在ContextView里面进行了view和各自mediator的绑定
好,我们接着看CubeMediator脚本
由于这里只是在模块激活时进行了提前注册,所以当玩家点击事件之后,全局派发器进行事件派发(通知),我们就会来到这里执行后面的对应的函数(/委托),那我们看看OnClickDown函数执行了什么?
我们回到ContextView可以看到,到底是谁和上面的事件id进行绑定
这里我们需要知道,当全局派发器进行派发的时候,根据事件id进行派发的,比如上面这个,很明显它是和UpdateScoreCommand这个脚本进行了绑定,所以全局派发器执行后,会自动跳转到这个脚本内部执行excute函数:
接着我们再看哪里注册对应这个事件id类型,很明显我们又在CubeMediator里面找到注册的地方
开始执行OnScoreChange函数:
好了,这里就完成服务器数据的回传。其实整体结构流程和MVC框架大致相同,不同的是StrangeIOC开创了binding方式和全局派发器的使用,关于请求分数的部分,自己可以多多练习,这里就不详细叙述了。
ps:
我也是第一次开始接触strangeioc框架学习,所以本文难免有疏漏之处,还望多多见谅。