游戏编程模式-设计模式
最近在学
一些建议?
- 抽象和解耦让扩展代码更快更容易,但除非确信需要灵活性,否则不要在这上面浪费时间。
- 在整个开发周期中为性能考虑并做好设计,但是尽可能推迟那些底层的,基于假设的优化,那会锁死代码。
- 快速地探索游戏的设计空间,但不要跑得太快,在身后留下烂摊子。毕竟你总得回来打扫。
- 如果打算抛弃这段代码,就不要尝试将其写完美。摇滚明星将旅店房间弄得一团糟,因为他们知道明天就走人了。
- 但最重要的是,如果你想要做出让人享受的东西,那就享受做它的过程。
设计模式
命令模式
将请求或操作封装成一个命令对象,使得客户端可通过调用命令对象来执行请求
命令是具现化的方法调用,命令模式是一种回调的面向对象实现
- 优点
- 解耦调用者和接收者:调用者只需持有命令对象,而不需要知道具体的操作细节
- 易于扩展:容易新增命令,而不需要修改现有的代码
- 支持撤销和重做、组合命令
- 缺点
- 类的数量增加:每个具体命令类都需要单独定义,系统更复杂
- 命令的参数化:复杂的操作,命令对象需要传递大量参数
享元模式
核心思想是将对象的共享部分抽取出来,将多个对象中的共有状态存储在一个共享对象中,而将各自独有的状态保存在对象外部。
一些特点:
- 唯一需要硬件支持的模式, 注重效率,能减少内存消耗,提高性能
- 享元对象一般是不可变的
观察者模式
使得多个观察者对象在被观察的对象状态发生变化时自动收到通知并更新自己
适用于不相关模块之间的通信问题,不适用于专注于一个特性或层面的单一代码块/模块内
- 设计观察者模式,最好基于函数而不是基于类,注册成员函数指针为观察者,而不是Observer接口实例
- 存在的问题:技术/可维护性
- 难的是要记得销毁被观察者和观察者
- 即使有GC,但 失效观察者 问题依旧可能存在,即被观察者保留了对观察者的引用,最终有对象僵死在内存中,也就是内存泄漏
原型模式
使用现有实例来创建特定种类的对象,通过拷贝创建新对象
优点:允许动态地创建新的对象实例,避免重复的初始化操作
缺点:克隆方法实现可能需要考虑深拷贝与浅拷贝,尤其是在对象包含复杂结构或外部资源时
原型语言范式:
- self语言
- 原型继承:所有对象都是基于现有对象复制和修改来的
- 更像是面向对象的语言,没有类概念,所有的数据结构都是对象、控制结构和方法调用都是对象调用
- 动态绑定、动态继承:实际支持多个父类,父类只是一个特殊的字段,可以继承父类或者在运行的时候修改它们
- JavaScript是基于原型的语言
- 跟 self 不同的是除去了基于原型语言的核心操作“克隆”
- 状态存储在实例中
- 行为通过原型的委托被存储在独立的对象中,代表了一系列特定类型对象的共享方法
数据模型构建原型:
- 代码只是驱动游戏的“引擎”,游戏是完全由数据定义的
- 游戏数据达到一定规模时,可以考虑使用原型和委托重用数据
单例模式
核心:实例全局化,确保一个类只有一个实例,且提供一个全局访问点来访问唯一实例
- 优点:
- 访问便利
- 不使用就不会创建实例,运行时初始化,延迟初始化
- 可继承单例
- 缺点:
- 是一个全局变量,促进耦合,对并发不友好,容易死锁
使用单例的解决方案
- 重新评估是否需要类:单例常用于“管理器”类,但实际中可以避免单例,例如直接将管理器逻辑直接移到类本身,让对象自我管理
- 将类限制为单一实例,用断言函数assert
- 为实例提供便捷的访问方式
- 依赖注入:做参数传给函数,但存在对象不属于方法函数的签名的情况
- 处理横切关注点:某些依赖在多个地方需要使用,面向切面编程处理这类关注点,而不必每次传参
- 从基类中获得:让子类通过继承获取共用依赖
- 通过其他全局对象访问:将需要的依赖集中在一个核心对象中,避免创建多个单例
- 通过服务定位器访问:定义一个类专门给对象做全局访问
状态模式
允许对象在其内部改变自身行为
枚举和分支:一系列标记成员变量只能存在一个true时定义Enum
有限状态机(FSMs):可看作最简单的的图灵机,不过不是图灵完备的
- 拥有一组有限状态集合,可在状态间切换,同一时刻只能处于一种状态
- 根据外部输入或事件触发状态间转换
状态模式应用:
定义状态接口
为每个状态定义类
状态委托
三种主对象委托给另外的附属对象的模式行为区别
- 状态模式——状态委托
通过改变主对象代理的对象来改变主对象的行为 - 策略模式
将主类和部分行为解耦 - 类型对象模式
使多个对象通过共享相同类型对象的引用来表现相似性
- 状态模式——状态委托
适用场景:
- 一个游戏实体的行为基于它的内部状态而改变
- 状态可被严格划分为相对数目较少的不相干项目
- 实体响应一系列输入或事件
注意:
- 为状态实例动态分配空间时,需要考虑碎片化问题,用静态状态的话就不会占用太多的CPU和内存资源
其他状态机
- 并发状态机
- 可同时拥有多个状态,允许多个状态机并发处理输入
- 是多个有限状态机的组合,彼此独立但可能会相互影响
- 层次状态机
- 将相似行为状态分组,形成层级结构,即状态可以有父状态
- 可以减少状态数量并简化状态转换
- 下推自动机
- 状态栈,可存储历史信息,不适合并行处理
一些需要注意的
- 面向对象就是让对象自己管理自己
- 两种易出错的代码
- 有复杂的分支
- 随时间可变的状态
- 继承是一种强大的代码重用方式,也可能使代码变得耦合