在
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链接起来,然后通过定义一些转换的条件。
究其原因,可能是由于不同游戏类型本身的软件架构可以相差很远,而且游戏里面的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
BounceModel.cs
BallView.cs
BounceNotification.cs
如果用这个思路来做,会清晰很多,但是感觉就是程序的耦合度太高了。
======================================================================================
浅谈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才有意义。
以上方式,纯属个人比较认可的方式,在实际开发中,每个人都会有自己的观点存在,但一个项目只能用统一的方式才方便沟通、交接、扩展…
参考 :Unity下的MVC编程