【游戏编程扯淡精粹】游戏编程设计模式

本文最初写于2018/9/4

毛星云 RIP

如何练习设计模式

  • 基本盘:长期维护一个大型工程,持续积累

    • 维护一个设计模式表格,日常查找使用
  • 多学几门编程语言和编程范式,看不同的语言如何更好地解决问题
  • 开发DSL和框架/架构在高维度解决问题,直接干掉设计模式,减少业务逻辑层的代码量

所以我们只能通过复杂的程序来学习设计模式。
你不管看别人的程序也好,自己写程序练习也好,那必须要复杂,复杂到你不用设计模式就做不下去,这才能起到学习设计模式的作用。

维护一个表格

  • 设计模式名称
  • 适用情形/案例
  • 代码示例
  • 详细描述

实际使用的时候,是从当前情景匹配适用情形去找到适合的设计模式

资源链接

【游戏设计模式】之四 《游戏编程模式》全书内容提炼总结 - 知乎
miloyip/graphvizuml: 使用 Graphviz 绘画 UML 图

并发设计模式

Active object - Wikipedia
Balking pattern - Wikipedia
Guarded suspension - Wikipedia
Reactor pattern - Wikipedia

编程语言和设计模式

Design Patterns in Dynamic Languages // Python
Channel (programming) - Wikipedia // GO
Hygienic macro - Wikipedia // Scheme

设计模式问答

工厂模式(factory Method)的本质是什么?为什么引入工厂模式? - 知乎

工厂就是封装复杂创建流程,非必要不要滥用设计模式,短平快实现需求

UML

UML方案

我选择的三种UML方案:

  • Graphviz
  • draw.io
  • Microsoft Viso

Graphviz的方案比较轻量,适合程序员,可以用Git进行版本管理,但是有学习门槛

常见UML

  1. 类图
  2. 顺序图
  3. 状态图
  4. 活动图
UML 适用情景
类图 类OOP设计
顺序图 时序,前后端交互
状态图 状态机
活动图 多线程fork/join流程

我个人最常用的是顺序图和状态图,比源码和文字描述的表现力更强

经典设计模式

经典设计模式主要适用于C++,C#语言的OOP编程范式

设计模式的目标

目标是得到高扩展性的代码

在应对频繁需求变更和迭代的过程中能够:

  • 快速实现新需求
  • 容易理解,容易调试
  • 扩展代码,而不是修改代码

设计模式的原则

  • 面向接口
  • 接口细粒度,调用功能是面向接口的
  • 单一职责
  • 避免继承一个类

组件模式 & 桥接模式

参考:

  • Unity的MonoBehaviour和Component
  • Ogre的OgreRoot和Plugin

桥接模式将抽象类和抽象类的组件进行分离。从而达到抽象类和抽象类中的组件可以独立的变化。

适配器模式

参考:

  • 生活中的转接口
  • 将list封装为stack

适用情形,两头的代码已经都别人写好了,我来对接时,写一个适配器进行转接

适配器模式的作用是将一个类的接口转换成客户所需要的接口。适配器器模式分为类适配器和对象适配器。

类适配器

采用多继承的方式,将目标接口和要适配的类进行集成。调用的时候,采用多态的机制,调用要适配的类实现即可。

对象适配器

对象适配的核心思想是客户端需要调用某种方法,而Adaptee没有该方法,为了使客户端能够使用Adaptee类,需

要提供一个包装(Wrapper)类Adapter。对象适配器与类适配器不同的地方是,适配器和被适配者不是继承的关

系,而是组合的关系。

命令模式 & 解释器模式

参考:

  • Lua的vmloop
  • Unity的Coroutine机制

可以将Coroutine机制封装一下,把一个命令包装成一个Coroutine,再把Coroutine串联在一起

命令模式是将一个请求封装为一个对象,从而使你可用不同的请求对造成不同的行为结果。

工厂模式

主要是因为ctor是特殊函数,不支持虚函数

所以包装成工厂函数,就可以使用一般的多态机制,更加灵活

简单工厂模式

一个工厂生产多种产品,告诉产品的类型,工厂就会生产对应的产品。如果新增产品的种类,那么需要更改工厂的源代码。

class Factory {public:Product* createProduct(productType type) {switch (type) {case TypeA:return new ProductA();case TypeB:return new ProductB();case TypeC:return new ProductC();default:return nullptr;}}
};

工厂方法模式

工厂方法模式的核心思想是:建立一个工厂基类。当我们想生产一种产品的时候,我们继承工厂基类,派生出生产

对应产品的派生类。使用派生出的工厂生产对应的产品。

抽象工厂模式

抽象工厂模式允许生产不同种类的产品。从而有应对了工厂模式只能生产单一种类产品的问题。

建造者模式

当需求的特点是流程的步骤类型相同(有一条步骤类型明确的流水线),但是步骤的具体却是不一样的。每种工人,在实现某一步骤是不一样的。指挥者调用建造者,来通过每种步骤的不一样,来表现出不同的产品。

案例:

  • Ogre的Resource类

代理模式

代理(Proxy)这个词在具体领域,比如网络,可能有非常多的概念

生活中的中介可以看作代理,中介会帮你完成你本身完成不了的工作

你只需要与中介对接,向中介提出请求,中介去执行,并将结果返回给你,你不需要知道中介是怎么做的

编程中,可以看作是Client-Server模型,Proxy Server做了一个抽象隔离,让Client使用简单的接口完成复杂的任务

为其他对象提供一种代理以控制对这个对象的访问。这样实现了接口和实现的分离。

原型模式

案例:

  • RPG游戏的怪物类型机制

本质是数据驱动地去构造一个类型

一般的RPG需要制作大量的换皮怪来应对玩家的等级和装备成长

可以通过功能模块的组合来拼装出一个新的怪物类型,不需要编写代码

核心思想是通过使用拷贝构造函数,来对自身进行拷贝,生成一个新的对象,来进行对象的创建。

装饰器模式

参考:

  • Python的装饰器

装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。在不必改变原类文件和使用继承

的情况下,动态地扩展一个对象的功能。

单例模式

参考:

  • Ogre的OgreSingleton

责任链模式

一种fallback处理机制

案例:

  • 查找路径,比如lua require查找模块机制
  • 游戏事件处理责任链,遍历事件处理器,直到事件被处理

组合模式

案例:

  • 文件系统的文件树

文件和目录都继承自节点

删除文件就是删除节点,删除目录是删除子树

外观模式

就是包装类

比如游戏引擎对图形接口和物理引擎都会重新包装一遍,因为原始的接口集合太复杂太难用了

享元模式

案例:对象缓存或者对象池

就是复用内存和对象

享元模式是为了应对大量细粒度对象重复的问题。程序中存在大量细粒度的对象,每次要使用时都必须创建一个新的对象,既影响了运行效率又增加了内存消耗。享元模式和对象池的区别,享元模式的享元池存储的是种类,一个种类只有一个实例,所有对象共享这一个实例。而对象池中可能存再多个实例。

策略模式

从多个alternative中选择一个

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法的变化不

会影响到使用算法的客户。

迭代器模式

C++/C#自带,这已经不是设计模式了

游戏设计模式

沙箱模式

原始的沙箱模式没有实践意义

沙箱这个概念需要了解:

  • 脚本执行网络代码需要沙箱隔离,参考Lua沙箱
  • 引擎开发过程中,分离出引擎层,提供一个沙箱环境用来编写Demo

这个沙箱机制ZeloEngine已经实现,Demo全部在Lua脚本沙箱中编写运行,可以动态切换,开发Demo不会导致频繁编译引擎

【ZeloEngine】沙箱机制_游戏编程扯淡精粹-CSDN博客

脏标记模式

案例分析:

  • UI脏矩形优化

    • UI界面View没有更新,就不需要重新计算View
  • transform树更新计算脏标记优化
    • transform节点更新只影响节点的子树,不影响其他节点不需要更新

transform树的脏标记更新ZeloEngine已经实现

需要框架,内容太多,建议单独查

事件队列

参考:

  • 饥荒的StateGraph和EntityScript

其他设计模式

控制反转 & 依赖注入

典型案例是Java Spring框架

我并不了解Spring,这里只作为案例

控制反转+依赖注入,可以理解为一种基于配置和类反射的工厂模式

下面是一个Spring的配置文件,spellChecker被注入textEditor的属性中,运行时会解析xml,去构造对象

伪代码:textureEditor.spellChecker = new me.leehao.SpellChecker(...)

spellChecker是一个接口,具体是什么类由xml配置来选择,这就是依赖注入

这么做的好处是:

  1. 依赖于抽象接口,而不是具体的实现类
  2. 构造实现类被抽离到配置,和代码解耦,不需要硬编码`new me.leehao.SpellChecker(…)``
  3. 可以单元测试打桩
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><!-- Definition for textEditor bean --><bean id="textEditor" class="me.leehao.TextEditor"><property name="spellChecker" ref="spellChecker"/></bean><!-- Definition for spellChecker bean --><bean id="spellChecker" class="me.leehao.SpellChecker"></bean>
</beans>

MVVM

参考:

  • WPF

Combinator

WIP

【游戏编程扯淡精粹】游戏编程设计模式相关推荐

  1. 【游戏编程扯淡精粹】如何学习编程语言

    [游戏编程扯淡精粹]如何学习编程语言 文章目录 [游戏编程扯淡精粹]如何学习编程语言 如果你没有学过编程 如果你只是想提升工作效率 如果你想学习计算机科学与技术 如果你已经是熟悉了一门编程语言 如果你 ...

  2. 【游戏编程扯淡精粹】UE5 蓝图

    [游戏编程扯淡精粹]UE5 蓝图 最近新学蓝图编程..还没看蓝图VM代码,有些点不保证准确 本文主要是从使用角度,分析你为什么需要学习蓝图,蓝图适合做什么,不适合做什么 最后 Bonus,跟一下 Bl ...

  3. 【游戏编程扯淡精粹】工作两年总结

    [游戏编程扯淡精粹]工作两年总结 大纲 正文: 做了什么,接下来做什么? 如何阅读<游戏引擎架构> Roadmap & Milestone 个人知识系统 个人技能成长的依赖倒置 最 ...

  4. 【游戏编程扯淡精粹】调试方法论

    [游戏编程扯淡精粹]调试方法论 文章目录 [游戏编程扯淡精粹]调试方法论 故障树分析法 Bug跟踪系统 & Bug交流分享 面对Bug的心态 如何在工作中正确高效地修bug? 找到原作者和模块 ...

  5. 【游戏编程扯淡精粹】EASTL源码阅读

    [游戏编程扯淡精粹]EASTL源码阅读 侯捷先生在<漫谈程序员与编程> 中讲到 STL 运用的三个档次:"会用 STL,是一种档次.对 STL 原理有所了解,又是一个档次.追踪过 ...

  6. 【游戏编程扯淡精粹】工作第三年总结

    工作第三年总结 文章目录 工作第三年总结 #1 做了什么 自研路线 Lua 脚本系统 ToolX #2 职业发展 如何做事 技术中台化 内卷的职业市场 个人成长 #3 心态建设 Owner vs 打工 ...

  7. Steam编程区解谜游戏A=B全解(一)

    这是一款以"A=B"这门编程语言(esolang?)为依托的解密游戏,这里我假定你已经玩过一点这个游戏了,如果没有,后面有游戏规则以及一些背景故事. 第一章(A=B) 1-1 1- ...

  8. python趣味编程100_《Python游戏趣味编程》 第8章 勇闯地下一百层

    知乎视频​www.zhihu.com 图书简介可以看这里:童晶:<Python游戏趣味编程>新书上架了​zhuanlan.zhihu.com 本章我们将编写一个勇闯地下一百层的游戏,键盘控 ...

  9. 编程一个最简单游戏_一个关于AI编程的游戏

    点击上方"机器学习与统计学",选择"置顶"公众号 重磅干货,第一时间送达 周末推荐一个正在玩的游戏,挺好玩的. <异常>是一个关于AI编程的游戏,在 ...

最新文章

  1. 机房重构(个人版)——类图
  2. 机器学习基础专题:高斯判别分析
  3. python读取文件名-python读取文件名并改名字的实例
  4. c语言解逻辑问题的一般步骤,C语言面试题---逻辑短路问题
  5. python爬取贴吧所有标题的评论_用BS4爬取贴吧文章的作者信息时,如何兼顾爬取高亮的作者信息?...
  6. 网络通信-1(InetAddress、UDP、TCP、DatagramPacket、DatagramSocket、UDP通信示例)
  7. 计算机不能上网 检查路线,腾达(Tenda)路由器不能上网的解决方法
  8. 总结const、readonly、static三者的区别【收藏、转载】20190614
  9. [转载] python histogram函数_Python numpy.histogram_bin_edges函数方法的使用
  10. OptiStruct 11.0有限元优化分析视频教程
  11. 单反相机坏点和噪点测试软件,正确对待相机的坏点和噪点
  12. win10安装MinGW
  13. python ui界面设计(二)
  14. 安卓开发实战讲解!史上最全的Android面试题集锦,深度好文
  15. 移动端项目功能点及实现方案 (图片居多)
  16. Win11怎么共享文件夹?Win11创建共享文件夹的方法
  17. centos7 修改和优化ssh
  18. 2021年中国反剽窃软件市场趋势报告、技术动态创新及2027年市场预测
  19. 《来自Percal25号行星的哥顿人》
  20. pandas python groupby_python – 如何在Pandas groupby之后获得多个条件操作?

热门文章

  1. 西门子S7-200PLC自由口初始化
  2. java教程pdf(java教程视频完整版)
  3. teamviewer一直验证账户_奇葩的Synchrony Bank ID验证过程(解锁Amazon Store Card)
  4. 有哪些布局精心、长久的骗局?
  5. Java入门篇——安装Java SE14
  6. activemq官方文档分析
  7. 走出计算机安全防范的六个误区
  8. 一文了解CDN应用加速
  9. 数组.列表.集合.应用
  10. html画布直线代码,图文详解如何用html5 canvas画一条直线