zoukankan      html  css  js  c++  java
  • 2019.10.17 MVC

    来源:https://blog.csdn.net/yupu56/article/details/53728489

    参考:StrangeIoC是一个超轻量级和高度可扩展的控制反转(IoC)框架,专门为C#和Unity编写

    https://github.com/strangeioc/strangeioc

    在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的思路来编程,没太多思路。前两天看到一个老外的文章,可以借鉴下。

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

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

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

    老外设计的结构其实是AMVCC

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

    M是数据

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

    第一个C是控制器,对所有程序逻辑进行处理

    第二个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才有意义。
     
    以上方式,纯属个人比较认可的方式,在实际开发中,每个人都会有自己的观点存在,但一个项目只能用统一的方式才方便沟通、交接、扩展…
  • 相关阅读:
    复制带有random指针的单链表
    loadrunner常见问题
    【转】性能测试、负载测试、压力测试的区别
    文件存储结构inode与RAM结构建立联系
    inode表元数据,存储在物理存储体上
    debug宏起作用应用
    linux内核常用函数或宏
    file、inode在应用层和驱动层之间的联系_转
    内核交互--sysfs
    内核交互--procfs
  • 原文地址:https://www.cnblogs.com/LiTZen/p/11691725.html
Copyright © 2011-2022 走看看