0 0 开始写游戏才发现架构这件事确实很重要哎~ 以及这本书确实是既通俗易懂又干货满满!我这种小白都能愉悦的读完~
几个重要的问题
- 相同数据的共享
- 相同代码的共享
- 性能
- 扩展性
- 令人犯错概率
典型的设计
- 命令模式
- 封装命令(类似闭包)
- 可以在命令生效的时候再传入需要操作的对象
- 享元模式
- 数据共享
- 缓存命中的问题与数据的存放方式有关
- 观察者模式
- Event/EventHandler
- 可以考虑将链表挂在观察者or被观察者身上
- 原型模式
- 使用类模板/函数传参/类方法 等方式来实现
- 对数据使用原型模式
- 单例模式
- 使用编译时或者运行时单例
- 全局的并不一定都是好的,会有多线程、访问无限制等问题
- 注意延迟初始化会造成卡顿的问题
- 可以以传参/挪到基类的方式替代
- 状态模式(FSM)
- 功能
- 进入、退出时切换状态
- 状态有各自的接收消息、转换规则
- 状态有各自的运行中的逻辑
- 扩展
- 并发状态机:两个状态机,处理两组关联较小的状态,对于其之间的耦合可以用if简单应付
- 层次状态机:对状态机进行继承
- 状态栈:为状态机增加历史记录
- 功能
- 双缓冲
- 如果交换数据的时间比写入数据的时间小得多,那么可以用这个
- 在需要统一变换的过程中很好用,例如显示图片、帧之间的大量对象的状态切换
- 游戏循环
- 使用sleep来减少发热
- 对优先级比较高的进行固定循环,其他的视时间循环,例如分离physics和rendering
- 解释器模式
- 内部操作:将数据和函数编码为简单的字符,方便使用
- 外部数据:压栈,在编译到函数编码部分的时候出栈使用
- 控制流:实现if、while等
- 制作一个图形界面来方便使用和减少出错
- 子类沙盒
- 在父类上放上子类可能需要的所有函数
- 子类只能在父类的范围内调用函数,不能调用任何外部函数
- 可以获得封装性
- 继承会造成“脆弱的基类”的问题,可以将所需要的数据传递给基类而后获取其上的功能
- 类型对象
- 将类型封装为对象,其不同表现作为数据
- 组件模式
- 组件之间通信的问题,可以直接引用其他组件来处理,或者建立一个小型的事件系统
- 事件队列
- 将事件按照顺序进入队列然后依次处理
- 注意事项:
- 会剥夺发送者对事件的控制
- 全局的总是有些糟糕
- 避免死循环
- 对象的所有权(释放对象的问题)
- 可以使用环状缓冲区
- 可以汇总事件进行处理
- 服务定位
- 在使用的时候查找所需要的服务然后使用
- 依然有全局的问题
- 服务不能使用的时候,可以考虑返回空服务,或者各方处理异常
- 数据局部性
- 缓存命中率会很大程度的提高速度
- 方法
- 将需要循环处理的数据放在相近的内存块,例如组件模式中的同组件(如果是按照组件类型进行循环的话)
- 减少if-else的可能性,例如试图将需要判断的if-else移到循环外
- 热/冷分解,将用的比较多的数据放在一起
- 避免继承和指针集合
- 脏标记模式
- 如果修改的次数比使用的次数多,或者更新数据的计算过程比较长
- 注意脏标记的粒度,比如为每个物体都设置脏标记,还是所有物体一块设置一个
- 对象池
- 可以考虑扩容、清理现存对象、不创建对象的方法来处理对象池不够了的问题
- 空闲分区
- 将数据按照他的位置组织数据结构,来高效定位
- 将战场划分为网格,网格内可以采用链表循环等
- 考虑分区更多的依赖是战场面积还是对象数量
- 四叉树/二叉空间分割/k-dimensional树/层次包围盒