龙之谷手游项目总结(客户端篇)
从线上办公到这个月来到互娱,差不多过去一个半月了,这一个半月里面自己全面得学习了客户端以及服务端设计的一些思路,最后也算是做出了个(半)成品。
长远得来看,目前自己更倾向于做客户端,所以在这次项目实战中同步方式也是采用了相对状态同步服务端更简单帧同步。
从一开始导师就一直在强调逻辑与视图分离,但说实在的,真正的逻辑-视图分离还是通过自己一点一点一天一天得去重构原来的“屎山”慢慢体会到其中的精髓的。
相对于状态同步,帧同步对于逻辑与视图的分离的要求比较高。
Unity自带的Mono组件中的Update可以视作逐帧更新,但是这里的帧数是浮动的,Unity会根据电脑自己额性能去调整帧数,这么一来如果我们在不同的硬件上运行同一个项目几乎100%会出现因帧数不同而引起的视图层表现不一致的问题。
为了解决这个问题,我们就必须将逻辑层与视图层分离开。帧同步的核心就是保证逻辑层逐帧运算完全一致,为了达到这个目标我们便需要在客户端自己实现一个定时器去保证逻辑帧帧数的稳定。由于视图层的一切更新都完全依赖逻辑层运算得到的结果,所以在保证逻辑运算正确的情况下,即使我们的视图帧的帧数是不稳定的(不开锁帧)我们也可以保证视图最后的“目的地”是完全一样的(类似有限状态自动机那种感觉,一样的input影响必然带来一样的output)
在明确视图与逻辑分离的目标之后我们开始考虑一个问题,我们整个游戏的逻辑离开了视图层该以什么为核心去驱动呢?
假设我们的视图层与逻辑层已然分离,那么两者该如何进行数据的交互呢?
这项目的后半段时间里,我先后采用了两种方式去实现,简单用一句话来说就是“轮询与回调”
轮询可以理解成遍历,这里我们轮询的对象是(静态)数据堆。
这里我们先不考虑客户端与服务端的交互,假设在某一帧逻辑层的一个函数ActionLogic()完成了一次运算,那么这个时候我们便把它运算得到的结果扔到相应的静态数据结构中,其他逻辑层的运算也是如此。于此同时,视图层的每一帧中都会轮询静态数据(结构),看看他们里面有没有算好的数据,如果有的话我们就把他拿出来进行视图层的更新
回调的话就是另一种完全不同的方式了。
因为有了回调函数,对于逻辑层的任意运算,在调用的时候都会传一个回调给视图层(当然前提是我们在所有管理类初始化的时候就Bind好了相关函数),剩下的处理与轮询没有什么大的差别。
除了逻辑与视图的分离之外,逻辑模块之间的解耦也是很有必要的,不是说我们要把模块完全独立出来,我们的目的是不同模块之间都是不透明不会相互干扰的,这么一来模块的拓展性和可移植性都会得到极大的提升。与此同时,bug定位之类的工作也会变得相对容易。
逻辑模块的解耦还有一个重要的目的就是保证模块的单向依赖,在此基础上设计合理的层次结构,至于有什么好处,这个看自己体会吧
下面贴个我的实习项目中客户端部分的模块层次与依赖关系的简单图示

简单讲解一下,上面的Entrance是游戏主循环的入口,与此同时也是逻辑层与视图层更新的所在
逻辑层与视图层的所有管理都通过Core这个接口来访问,其中的几个Mgr都是单例
基础类库主要提供一些中间存储以及常用但与游戏模块的联系不大的函数
服务端部分以及客户端服务端交互部分下次会合在一起写,哪天得闲就来写写
发表评论