Cocos2d-JS中的简易MVC框架指南(一)

2 篇文章 0 订阅
1 篇文章 0 订阅

1-(29)500.jpg

一、框架简介

今天开始我将把我写的一套适用于Cocos2d-JS的一套MVC框架分享给大家。首先我先简单介绍一下MVC,然后再逐步的介绍我写的mvc框架和在游戏中的具体应用。

MVC借用百度百科的解释: MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

这里实现的MVC比较简单明了,关系图如下:

4d60471fjw1eu2kz2rcuoj20fk0bpgme.jpg

节点功能

1.View:包括Scene和Layer,负责显示和接收用户反馈,将用户操作反馈通过消息发送给Mediator处理。

2.Mediator:包括DirectorMediator、SceneMediator和LayerMediator,持有View对象,负责接收消息进行处理,更新UI和数据。DirectorMediator负责维护场景间的关系和切换;SceneMediator负责当前场景的UI关系和切换;LayerMediator负责当前UI的管理。

3.Model:数据管理模型,负责维护客户端数据,监听服务器消息,将消息发送给Mediator进行处理再跟新View。

4.Facade:持有Model和Mediator对象。Fa?ade负责初始化框架,Facade会自动维护UI调用关系。

5.部分节点之间使用Notification进行消息派发。

框架的简单介绍就到这里,接下来我会详细介绍Cocos2d-JS中简易MVC各个模块的实现和应用。

二、数据模型Model

模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。数据模型Model在MVC结构中扮演者非常重要的较色。

在我写的这套MVC结构中Model作为本地数据逻辑存储对象来使用,主要负责监听服务器数据返回、通知控制器修改视图、处理数据逻辑和保存数据对象。

IModel作为model的基类,主要实现接口如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
game.IModel = cc.Class.extend({
     ctor: function  () {
     },
     init: function  ()
     {
         throw  new  Error( "SubClass not init subscribe function." );
     },
     //Use this function to send notification.
     send: function  (key, obj)
     {
         game.Notification.send(key, obj);
     }
});

ctor作为构造函数不必多说。Init函数子类必须去实现,在初始化Model对象时init函数会自动被调用,子类对象可以在init函数中做一些初始化操作等,在网络游戏项目中对服务器数据返回的监听也是放在init函数中进行注册。Send是进行消息分发的函数,有两个参数key、obj,key是消息的唯一id,obj是消息要传递的对象。在mvc的结构中model通过发送通知告诉mediator控制器数据变化,mediator再根据自己的逻辑进行处理然后控制View进行更新。一般Model可以被mediator持有。

在Fa?ade中提供了一个注册model的共有接口在游戏初始化中提供调用:

1
2
3
4
5
6
7
8
9
game.Facade.registerModel =  function  (cls, model) {
     model.init();
     var  isExist = game.Facade._modelMap.contains(cls);
     if  (isExist) {
         cc.log( "Model:"  + cls +  " have already exists!" );
     else  {
         game.Facade._modelMap.put(cls, model);
     }
}

cls为类名,model为实例对象,注册后在mediator中会提供接口方便获取注册的model实例,具体获取方法在介绍mediator时会讲到。注册model的方法如下:

1
2
3
4
//注册model
GameControler.registModel =  function  () {
     game.Facade.registerModel(model.UserModel,  new  model.UserModel());  //玩家数据
}

三、中介者Mediator

Mediator作为这套MVC框架的核心部分承载了大部分的功能。Mediator主要分三类:DirectorMediator,SceneMediator,LayerMediator。这三类Mediator都是继承自IMediator,IMediator的实现如下:

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
/********************************************************************************
The game.IMediator
@author ituuz
@date 2014-11-18
IView is the mediator of MVC, create your model to extend this class.
In this mediator can get model,current mediator and rootMediator.
********************************************************************************/
game.IMediator = cc.Class.extend({
     ctor: function  () {
     },
     show: function  ()
     {
         throw  new  Error( "SubClass not overwrite show function." );
     },
     getCurrMediator: function  () {
         var  med = game.Facade._directorMediator.currSceneMediator.currLayerMediator;
         if  (med ==  null ) {
             med = game.Facade._directorMediator.currSceneMediator.rootLayerMediator;
         }
         return  med;
     },
     getRootMediator: function  () {
         return  game.Facade._directorMediator.currSceneMediator.rootLayerMediator;
     },
     getModel: function  (cls) {
         return  game.Facade._modelMap.get(cls);
     },
     //Use this function to send notification.
     send: function  (key, obj)
     {
         game.Notification.send(key, obj);
     }
});

show函数需要它的子类去实现,show的主要作用就是该Mediator被创建时显示的内容。send函数是用来发送消息的。除此之外还有三个get函数,分别是获取当前Mediator、获取RootMediator和获取Model对象。

下面单独介绍继承自Mediator的这三类Mediator的作用。

首先是DirecotorMediator,它主要是来控制场景的创建和销毁。代码实现如下:

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
/********************************************************************************
The DirectorMediator
@author ituuz
@date 2014-11-18
Control the scene show or hide.
********************************************************************************/
game.DirectorMediator = game.IMediator.extend({
     sceneMediatorStack: null ,
     currSceneMediator: null ,
     ctor: function  (view) {
         this .currView = view;
         this .sceneMediatorStack =  new  game.Stack();
     },
     show: function  (parent) {
     },
     //Open the new scene and destroy the previous.
     showScene: function  (mediator) {
         if  ( this .currSceneMediator &&  this .currSceneMediator.rootLayerMediator) {
             this .currSceneMediator.rootLayerMediator._pDispose();
         }
         if  ( this .currSceneMediator) {
             var  layMedList =  this .currSceneMediator.layerMediatorList;
             while  (layMedList.size() > 0) {
                 var  med = layMedList.pop();
                 med._pDispose();
             }
         }
         this .currSceneMediator = mediator;
         this .sceneMediatorStack =  new  game.Stack();
         this .sceneMediatorStack.push(mediator);
         cc.director.runScene(mediator.currScene);
     },
     //Open the new scene and push the previous into the stack
     pushScene: function  (mediator) {
         this .currSceneMediator = mediator;
         this .sceneMediatorStack.push(mediator);
         cc.director.pushScene(mediator.currScene);
     },
     //Pop the current scene to destroy then go to the previous scene.
     popScene: function  () {
         var  pop =  this .sceneMediatorStack.pop();
         var  curr =  this .sceneMediatorStack.top();
         this .currSceneMediator = curr;
         cc.director.popScene();
     }
});

DirectorMediator除了继承了Mediator的几个函数外,还有几个控制Scene打开和关闭的函数,分别是showScene,pushScene,popScene,都是控制场景打开和关闭的。

然后介绍的是SceneMediator,它主要是控制LayerMediator的生命周期。控制着LayerMediator的创建和销毁。实现如下:

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
/********************************************************************************
The SceneMediator
@author ituuz
@date 2014-11-18
Control the layer show or hide.
********************************************************************************/
game.SceneMediator = game.IMediator.extend({
     currScene: null ,
     currLayerMediator: null ,
     rootLayerMediator: null ,
     layerMediatorList: null ,
     ctor: function  (view) {
         this .currScene = view;
         this .layerMediatorList =  new  game.Stack();
     },
     show: function  (obj) {
         if  ( this .currLayerMediator == undefined ||  this .currLayerMediator ==  null  ) {
             this .rootLayerMediator.init();
             this .rootLayerMediator.show( this .currScene, obj);
         else  {
             this .currLayerMediator.init();
             this .currLayerMediator.isRoot =  false ;
             this .currLayerMediator.show( this .currScene, obj);
         }
     },
     //Setting the root layer
     rootLayer: function  (layerMed) {
         this .rootLayerMediator = layerMed;
     },
     showRoot: function  () {
         this .show();
     },
     //Open the new layer and destroy previous layer.
     showLayer: function  (layerMed, obj) {
         this .currLayerMediator = layerMed;
         this .layerMediatorList =  new  game.Stack();
         this .layerMediatorList.push(layerMed);
         this .show(obj);
     },
     //Open the new layer and push the previous layer into stack.
     pushLayer: function  (layerMed, obj) {
         this .currLayerMediator = layerMed;
         this .layerMediatorList.push(layerMed);
         this .show(obj);
     },
     //Pop the current layer to destroy then go to the previous layer.
     popLayer: function  (obj) {
         var  layer =  this .layerMediatorList.pop();
         layer && layer._pDispose();
         this .currLayerMediator =  this .layerMediatorList.top();
         if  ( this .currLayerMediator) {
             this .currLayerMediator.freshen(obj);
         else  {
             this .rootLayerMediator.freshen(obj);
         }
     }
});

它同DirectorMediator一样,控制着LayerMediator,并且拥有几个控制它的函数,包括showLayer、pushLayer、popLayer,除此之外他还有个rootLayer和showRoot函数,这两个函数是用来设置和显示场景上的初始Layer的,cocos2dx中场景内必须有Layer才能显示内容,所以这两个函数设置并显示了root layer。同时SceneMediator还重写了show函数,在重写的show函数中初始化了LayerMediator并且调用了LayerMediator的show函数用来显示Layer。

最后我们介绍一下LayerMediator,这个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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/********************************************************************************
The LayerMediator
@author ituuz
@date 2014-11-18
The layer's logic.
********************************************************************************/
game.LayerMediator = game.IMediator.extend({
     currView: null ,
     backBtn: null ,
     mask: null ,
     isRoot: true ,
     ctor: function  (view) {
         this .currView = view;
     },
     show: function  (parent, obj) {
         if  ( this .isRoot)  return ;
         var  size = cc.winSize;
         this .mask =  new  cc.LayerColor(cc.color(0,0,0,200), size.width, size.height);
         parent.addChild( this .mask);
         cc.eventManager.addListener({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches:  true ,
             onTouchBegan:  this .onTouchBegan
         },  this .mask);
     },
     onTouchBegan: function (touch, event) {
         //Could to do sth.
         return  true ;
     },
     init: function  () {
         throw  new  Error( "SubClass must be overwrite init function and regist event in this function." );
     },
     freshen: function  (obj) {
         cc.log( "freshen" );
     },
     //Use this function to send notification.
     send: function  (key, obj)
     {
         game.Notification.send(key, obj);
     },
     subscrib: function  (type, callback, target) {
         game.Notification.subscrib(type, callback, target);
     },
     unsubscrib: function  (type, callback) {
         game.Notification.unsubscrib(type, callback);
     },
     //Switch scene
     showScene: function  (sceneMed) {
         game.Facade._directorMediator.showScene(sceneMed);
         game.Facade._directorMediator.currSceneMediator.showRoot();
     },
     //Push new scen into the stack
     pushScene: function  (sceneMed) {
         game.Facade._directorMediator.pushScene(sceneMed);
         game.Facade._directorMediator.currSceneMediator.showRoot();
     },
     //Pop current scene out of the stack
     popScene: function  () {
         game.Facade._directorMediator.popScene();
     },
     //Switch layer
     showLayer: function  (layerMed, obj) {
         game.Facade._directorMediator.currSceneMediator.showLayer(layerMed, obj);
     },
     //Push new layer into the stack
     pushLayer: function  (layerMed, obj) {
         game.Facade._directorMediator.currSceneMediator.pushLayer(layerMed, obj);
     },
     //Pop current scene out of the stack
     popLayer: function  (obj) {
         game.Facade._directorMediator.currSceneMediator.popLayer(obj);
     },
     //private
     _pDispose: function  () {
         var  that =  this ;
         that.destroy();
         that.currView.removeFromParent( true );
         that.mask &&  this .mask.removeFromParent( true );
     },
     destroy: function  () {
         throw  new  Error( "SubClass must be overwrite destroy function and delete event in this function." );
     }
});

这里面通用的几个pop、show、push函数就不多说了,主要说一下其他特殊的函数。首先是构造函数,它有一个参数view,这个view就是和这个Mediator对应的Layer,这里保存了个引用,方便使用。然后是重写的show函数,在该函数中的两个参数parent是该layer的父级容器,obj是在打开该Layer时传入的参数。在show里还对打开的Layer进行了模态处理。还有init函数,该函数是在该LayerMediator初始化时调用的,可以在该函数中初始化一些数据,或者注册监听等。然后LayerMediator中还提供了几个消息相关的函数,分别是发送消息send、注册消息subscrib、删除消息注册unsubscrib。还有freshen(obj)函数是在上层Layer关闭时调用的,obj是可以传递的值,在该函数中可以做一些UI刷新的功能。最后比较重要的两个函数都是有关销毁的。destroy函数是必须在子类重写的,在LayerMediator被销毁时调用,_pDispose是私有的函数,负责销毁公有的对象,不能显示调用。

Mediator在MVC中比较重要,除了自身的动能外,还承载了其他几个功能。首先Mediator中持有View对象,可以对View进行显示更新,同时也要响应View上的各种事件。然后Mediator可以注册和发送消息。同时Mediator还可以获取Model对象,进行操作。

四、显示层View

View的功能比较简单,View在MVC结构中负责显示和接收事件。在Cocos2d-JS中Scene和Layer都是View,View只负责显示和接收事件,不负责处理逻辑。在框架中View的实现分为两类,一类是IScene继承自cc.Scene负责场景显示,另一类是IView继承自cc.Layer负责场景上的层显示。IScene和IView的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
game.IScene = cc.Scene.extend({
        ctor: function  () {
           this ._super();
        },
        //Use this function to send notification.
        send: function  (key, obj)
        {
           game.Notification.send(key, obj);
        }
     });
game.IView = cc.Layer.extend({
        ctor: function  () {
           this ._super();
           return  true ;
        },
        //Use this function to send notification.
        send: function  (key, obj)
        {
           game.Notification.send(key, obj);
        }
     });

send函数的用途是在View接收到用户的触摸事件或其他事件时向Mediator发送消息,具体处理逻辑由Mediator来处理。

源码地址:https://github.com/yue19870813/cocos2d-js-mvc.git/

未完待续……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值