Unity3d之MVC框架的使用

Unity游戏的开发当中,我并无刻意地采用MVC框架,由于不像网站开发那样,Model,View,Controller在游戏这个领域里尚未很清晰的定义。
究其缘由,多是因为不一样游戏类型自己的软件架构能够相差很远,并且游戏里面的Object之间有大量的交互,因此垂直的MVC彷佛不是十分应景。

然而,某种程度的分离代码逻辑是必要的,能够提升代码的可维护性和重用性。
下面我说说本身的一些经验。

假设咱们在作一个马里奥:
对于游戏里的角色,我会采用这样一个结构。
Character Manager,它的做用是包含这个角色的Controller(s),并提供一个Dictionary
Controller,利用Reusable Models来处理角色在这个游戏中的某一状态的逻辑。
Reusable Model,是一个虚的概念,并非一个父类,一般这类Model都负责某一个特定的功能,能够重复利用,可看作游戏引擎的延伸。
我会将Character Manager和Reusable Model继承MonoBehavior,这样咱们就可以直观地知道这个角色是什么类型的Character,而且能够利用inspector调节Model的参数。



怎么将上面的架构应用在马里奥身上呢:
做为Character Manager,咱们能够采用Finite State Machine或者Behavior Tree。一个好处是它们都自然地提供了“Controller”。
例如Finite State Machine,它的每个State均可以看做一个Controller。
而Behavior Tree里面的Action Node,也能够看做是一个Controller。

在每个Controller里面,都会有指针指向一些Reusable Model。
例以下图Move State能够有一个Move Motor,专门来实现GameObject的移动,而Sprite则封装GameObject的表现,如动画、旋转、位置等等。
这些Reusable Model一般都提供丰富的参数可供调整,能够用于不一样游戏当中。

用户输入和游戏里面的消息,则会暂存在Character Manager里面的Blackboard里,供Character Manager使用,让它决定是否须要更换Controller。
例如马里奥里面我按左键,往左行动的信息会写在FSM的Blackboard里面,而后经过FSM的State转换机制 [2],从Idle State转换到Move State。
这样的好处是,往左的信息能够从Input Manager (图中没给出)那里得来,也能够从Enemy AI Manager(图中没给出)那里得来。
这样,一个类型(如拥有Idle,Move,Jump等状态)的FSM,就能够用在全部相似的角色身上,不管是玩家控制的仍是AI控制的。


最终在Unity里面会是这样一个状况,FSM,Sprite,MoveMotor都做为Component,而Controllers则包含在FSM里面。


以上方案虽然并不严格,可是在必定程度上提升了代码的可复用性和可维护性。
例如如今我基本都把MoveMotor,Sprite等Model写好,新项目就直接扔进来就能用;
MoveState,IdleState,JumpState等一些在平台游戏里经常使用的状态封装好,留出一些可调参数,例如状态间的转换。

比较原始的FSM会将State转换直接放在State里面,但这样大大下降了State的可复用性。所以能够尝试将State的转换做为一个可调参数。一些可视化的FSM的原理也是这样,利用连线将两个State连接起来,而后经过定义一些转换的条件。




====================================================================================================

Unity是基于组件的,作多了代码不免混乱。所以须要有更好的思路。一个思路就是MVC。可是,在Unity下如何使用MVC的思路来编程,没太多思路。前两天看到一个老外的文章,能够借鉴下。html

地址:https://www.toptal.com/unity-Unity3D/unity-with-mvc-how-to-level-up-your-game-developmentgit

例子下载:http://download.csdn.NET/detail/wuyt2008/9615891github

下面的图显示了老外的思路编程


老外设计的结构实际上是AMVCC架构

A是application,用来包含整个项目,并调度用哪一个control来响应mvc

M是数据app

V是视图,只负责做为事件处理的入口框架

第一个C是控制器,对全部程序逻辑进行处理ide

第二个C是组件,不一样的组件,组成视图、控制器或模型。函数




这么说,有点不明白,看下脚本,大概就能理解了。


BounceApplication.cs

[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. // 全部类的基类  
  5. public class BounceElement : MonoBehaviour  
  6. {  
  7.     // 让整个应用和全部实例便于访问  
  8.     public BounceApplication app {   
  9.         get {   
  10.             return GameObject.FindObjectOfType<BounceApplication> ();   
  11.         }  
  12.     }  
  13. }  
  14.       
  15. public class BounceApplication : MonoBehaviour  
  16. {  
  17.     // MVC的根实例  
  18.     public BounceModel model;  
  19.     public BounceView view;  
  20.     public BounceController controller;  
  21.   
  22.     //迭代全部控制器和通知数据  
  23.     public void Notify (string p_event_path, Object p_target, params object[] p_data)  
  24.     {  
  25.         BounceController[] controller_list = GetAllControllers ();  
  26.         foreach (BounceController c in controller_list) {  
  27.             c.OnNotification (p_event_path, p_target, p_data);  
  28.         }  
  29.     }  
  30.   
  31.     // 获取场景中的全部控制器  
  32.     public BounceController[] GetAllControllers ()  
  33.     {   
  34.         BounceController[] arr = { GameObject.FindObjectOfType<BounceController>() };  
  35.         return arr;  
  36.     }  
  37. }  

BounceModel.cs

[csharp]  view plain  copy
  1. using UnityEngine;  
  2.   
  3. // 包含与应用相关的全部数据  
  4. public class BounceModel : BounceElement  
  5. {  
  6.     // 数据  
  7.     public int bounces;   
  8.     public int winCondition;  
  9. }  


BounceController.cs

[csharp]  view plain  copy
  1. using UnityEngine;  
  2.   
  3. // 控制应用的工做流  
  4. public class BounceController : BounceElement  
  5. {  
  6.     // 处理小球碰撞事件  
  7.     public void OnNotification(string p_event_path,Object p_target,params object[] p_data)  
  8.     {  
  9.         switch(p_event_path)  
  10.         {  
  11.         case BounceNotification.BallHitGround:  
  12.             app.model.bounces++;  
  13.             Debug.Log("Bounce"+app.model.bounces);  
  14.             if(app.model.bounces >= app.model.winCondition)  
  15.             {  
  16.                 app.view.ball.enabled = false;  
  17.                 app.view.ball.GetComponent<Rigidbody>().isKinematic=true// 中止小球  
  18.                 //通知自身或者其余控制器处理事件  
  19.                 app.Notify(BounceNotification.GameComplete,this);              
  20.             }  
  21.             break;  
  22.   
  23.         case BounceNotification.GameComplete:  
  24.             Debug.Log("Victory!!");  
  25.             break;  
  26.         }     
  27.     }  
  28. }  

BallView.cs

[csharp]  view plain  copy
  1. using UnityEngine;  
  2.   
  3. // 小球视图  
  4. public class BallView : BounceElement  
  5. {  
  6.   
  7.     void OnCollisionEnter() {   
  8.         app.Notify(BounceNotification.BallHitGround,this);  
  9.     }  
  10. }  


BounceView.cs

[csharp]  view plain  copy
  1. using UnityEngine;  
  2.   
  3. // 包含与应用相关的全部视图  
  4. public class BounceView : BounceElement  
  5. {  
  6.     public BallView ball;  
  7. }  

BounceNotification.cs

[csharp]  view plain  copy
  1. // 给予事件静态访问的字符串  
  2. public class BounceNotification  
  3. {  
  4.     public const string BallHitGround = "ball.hit.ground";  
  5.     public const string GameComplete  = "game.complete";  
  6.     /* ...  */  
  7.     public const string GameStart     = "game.start";  
  8.     public const string SceneLoad     = "scene.load";  
  9.     /* ... */  
  10. }  

若是用这个思路来作,会清晰不少,可是感受就是程序的耦合度过高了。

======================================================================================
浅谈unity3d中使用MVC框架模式

MVC框架模式,相信不少人都不会陌生,数据-控制-显示分离的工做方式或者叫作代码结构会使软件(游戏)的结构清晰化,逻辑更明了。但因为MVC框架模式各部件均可以与彼此进行沟通,形成了不少新人在使用MVC的时候消息满天飞,解耦没成,耦合度更高了。我建议在使用MVC的时候,制定策略,让消息单向化,不要双向或造成网状。

好了,咱们下面讨论一下Unity3D是否可使用MVC,如何使用会比较好?(方法有不少种,这里我只写我比较认同的一种)
既然我写了有我比较认同的方式,那么在Unity3D中使用MVC至少我我的持同意态度,任何东西没有好与坏,只有适用不适用。

Unity3D自己的MonoBehavior脚本是一个重大突破,达到了组件式开发的目的。可是我依然要说,东西虽好,不能乱搞。我我的认为:组件式开发是好的方式,但组件自己倒是依靠传统的编程方式创建的,因此除开组件最适用的地方外,强制使用组件进行开发是违和的。MonoBehavior脚本,咱们能够将它理解为界面层与界面直接沟通的上层脚本,在他底部的控制、逻辑、数据等有必要用MonoBehavior脚本么?至少我认为是没必要要的(为何要用用不到的东西管理数据和逻辑呢?)。底部的控制、逻辑用什么实现好呢?方式不少,至少MVC框架模式是一个选择。而在使用MVC时,我我的认为模块内小规模使用MVC更合理,模块内的数据、控制、界面关系比较紧密,模块之间提供合理的接口进行跳转便可(不排除模块间消息沟通)。而模块内使用MVC时,也要遵循消息流向单向化,即:接收方永不发送消息,发送方永不接收消息。有人说不太可能,我给你们提供一个模型供参考:

1.      定义M只提供两种函数接口:操做、获取数据;并能够发送更新消息
2.      定义V只接收消息并控制界面显示、跳转、效果等
注:界面(暂且称之为UI)自身处理界面点击等操做直接调用M层进行处理或内部消化或发送给V进行控制跳转
3.      定义C实现必要逻辑(非界面自身逻辑)
咱们来看以上模型:
a. 用户点击->UI响应控制->调用M更改数据->发送更新界面消息->V接收消息->更新界面
b. 用户点击->UI响应控制->发送界面跳转消息->V接收消息->更新界面
c. 用户点击->UI响应控制->UI自消化
其实,通常的界面模块,用以上的模型就足够了。但有些模块比较复杂,须要不断的与数据和界面交互,这时候C才有意义。
以上方式,纯属我的比较承认的方式,在实际开发中,每一个人都会有本身的观点存在,但一个项目只能用统一的方式才方便沟通、交接、扩展…