我大多数时候向来不喜欢直接用别人的代码,本着能自己写就自己写的原则,客户端结构也一直都是我自己在维护.
代码结构
整个客户端大体上是分为Frame和Game两大部分.Frame为框架层,通用于所有项目.Game是游戏层,只能写当前项目才会用到的代码.
Frame:说是通用于所有项目有点夸大了,毕竟游戏类型太多了,商业游戏引擎都不敢说通用于所有游戏,但这确实是这部分设计的初衷.其实这部分就是沉淀下来的代码,就算是重新做一个新的项目,这部分代码也仍然可以直接使用.
Camera:再次对unity的Camera封装一层.提供封装所有的摄像机相关的常用操作.
Character:封装角色,游戏中所有的看起来像个人,或者能够当作一个人的都是角色.
ClassPool:对象池,适用于所有类对象,就是需要申请时先从未使用的对象列表时查找,有可以重复使用的就返回出去,没有的就创建一个.
CommandSystem:命令系统.
特点:
1百思特网.封装逻辑
2.连接逻辑与界面,以及项目中任何模块
3.延迟执行
4.线程安全,在子线程中可以使用延迟命令将逻辑放到主线程执行
5.日志打印
基于以上特点,命令系统一般是用在表示一件逻辑意义上的事件,命令中可访问任意项目代码,所以当一个事件关联多个模块时,应该将相关代码封装到一个命令中
比如数据改变时,即有数据存储的改变,也有界面相应的改变,那就需要将该逻辑封装到命令中,在合适的地方调用命令即可.调用方只需要关心触发了什么事件,而不需要关心事件具体百思特网内容.
命令系统的最原始灵感来自于天龙八部代码中的命令,天龙八部中的命令只是一个结构体,加上一堆无具体类型的参数,然后发送的时候指定那个参数是什么意思,用的时候再强转类型.
但是我设计的命令系统核心是逻辑即对象.由此衍生出一些其他的特性.延迟执行,线程安全等等.
Common:大多不知道怎么归类的以及公共使用的部分都放在这里了,比如工具函数,全局定义,枚举定义,等等.
Component:组件,思想倒是来自于unity,不过我也就一开始用了一个月的unity,就知道个组合代替继承这个说法.然后再次使用unity已经是2年后了.这里的组件核心思想是所有可以从类的核心中拆分出来需要单独更新的内容都可以写成一个组件.所以这里组件强调的是逻辑更新.并不是把所有的东西都放到组件里面.相当于组合与继承的结合.
DataBase:数据库相关,比如MySQL,SQLite,以及自己写过的数据表格.
DynamicAttackScript:代码中动态附加到GameObject上的脚本,其实这里 一般都是一些需要在面板上显示调试信息的脚本.
Effect:粒子特效的封装,提供一些特效的常用操作.
GameFramework:核心管理器.以及一些其他独立的系统.
GamePlugin:主要是提供调用外部插件的接口.另外一个项目写的插件,然后放到项目的指定目录里,就会自动调用.
GameScene:游戏逻辑场景.
游戏总体划分为多个逻辑场景,分别代表游戏不同的阶段,一般由所使用的资源和逻辑共同决定逻辑场景划分.
一个逻辑场景包含若干个流程,流程以树形结构存储.逻辑场景至少包含一个起始流程和退出流程.
流程表示逻辑场景内部的状态划分,流程的切换一般都会有界面的相应切换.
进入流程时的操作一般与退出流程时的操作对应,比如进入流程时打开了一个界面,那退出流程时就应该将此界面关闭.进入流程时禁用了某项操作,退出流程时就应该重新启用此操作.
流程之间不允许互相访问,流程之间应该是相对隔离的,仅允许在进入或者退出流程时判断上一个流程或者下一个流程的类型来执行不同的逻辑.
流程切换时仅会停止对旧流程的更新,启用新流程的更新,不会销毁任何流程.
逻辑场景切换时会销毁旧逻辑场景以及此场景的所有流程,加载并初始化新逻辑场景.
一个逻辑场景一般会使用若干个资源场景,并且根据流程切换资源场景显示.
其实主要作用就是区分不同游戏状态,逻辑隔离,不同逻辑之间尽量不会有耦合.
LayoutSystem:前身是C 写的UI框架,百思特网C 从opengl开始,封装UI,隐藏底层具体实现,只需要上层关心游戏界面逻辑即可.从C 转到unity后,这部分也只是把底层unity已经实现的部分替换掉,上层基本没多大变化.
MovableObject:字面意思就是可移动物体,也就是游戏中所有需要移动或者具有位置属性的物体的基类.
ObjectPool:物体池,与ClassPool的区别就是,这个池里存的是GameObject,ClassPool里存的是类对象.
也就是所有从磁盘预设实例化出来的GameObject都会存在这个池里,除非指定了销毁时是真正销毁,否则都是默认回收存起来.
ResouceManager:资源管理器,统一了从AssetBundle和Resources加载的方式,使上层只需要知道加载了某个资源,而不需要具体关心需要从哪儿加载,从哪儿加载是一开始读取配置文件就已经决定了的.一般情况下编辑器开发环境下从Resources加载,打包后从AssetBundle加载.也可以在编辑器开发环境下测试从AssetBundle加载.
Socket:网络部分.自定义的通信协议.虽然现在有很多成熟的商业序列化库用来进行网络通信,但是我还是从我自己实际情况触发,写一套自己的序列化通信协议.毕竟也不难,写出来效率也不低,优化也可以由自己完全掌握.