元数据

游戏编程模式

  •  游戏编程模式|200
  • 书名: 游戏编程模式
  • 作者: Robert Nystrom
  • 简介: 游戏开发一直是热门的领域,掌握良好的游戏编程模式是开发人员的应备技能。本书细致地讲解了游戏开发需要用到的各种编程模式,并提供了丰富的示例。全书共分20章,通过三大部分内容全面介绍了与游戏编程模式相关的各类知识点。首部分介绍了基础知识和框架;第二部分深入探索设计模式,并介绍了模式与游戏开发之间的关联;第三部分介绍了13种有效的游戏设计模式。本书提供了丰富的代码示例,通过理论和代码示例相结合的方式帮助读者更好地学习。无论是游戏领域的设计人员、开发人员,还是想要进入游戏开发领域的学生和普通程序员,都可以阅读本书。
  • 出版时间 2016-09-01 00:00:00
  • ISBN: 9787115426888
  • 分类: 计算机-计算机综合
  • 出版社: 人民邮电出版社
  • PC地址:https://weread.qq.com/web/reader/6b932b607159a2116b93835

高亮划线

第5章 原型模式

  • 📌 想要实现一个正确的clone()方法也是非常不容易的,这里会有很多语法陷阱。比如深拷贝和浅拷贝的问题。

    • ⏱ 2024-01-08 14:13:42
  • 📌 我们定义孵化函数,而不再是为每一个怪物类定义生成器类

    • ⏱ 2024-01-08 14:15:25
  • 📌 原型语言范式

    • ⏱ 2024-01-08 14:23:47
  • 📌 Self语言消除了这些区别。不管是查找方法还是域,你都是直接到对象当中去找。一个实例可以包含状态和行为(见图5-4)。你可以构建出一个只包含一个方法的对象。

    • ⏱ 2024-01-08 14:33:46
  • 📌 Self语言没有类,但是它可以使用委托来完成类似的功能。

    • ⏱ 2024-01-08 14:34:29
  • 📌 在一个基于原型的系统里面,任意对象都可以被用来克隆并创建出一个新对象。我觉得这里的数据模型也是一样的。

    • ⏱ 2024-01-08 14:57:03

第6章 单例模式

  • 📌 确保一个类只有一个实例

    • ⏱ 2024-01-08 15:08:51
  • 📌 instance_这个静态成员保存着这个类的一个实例,私有的构造函数确保它是唯一的。公有的静态函数instance()为整个代码库提供了一个获取该实例的方法。它也负责在第一次访问的时候初始化这个实例,也就是延迟初始化(lazy initialization)。

    • ⏱ 2024-01-08 15:09:40
  • 📌 当然,你的单例类本身的线程安全性完全是另外一个问题!这里只是确保它的初始化是线程安全的。

    • ⏱ 2024-01-08 15:41:24
  • 📌 但是静态类有一个局限:自动初始化

    • ⏱ 2024-01-08 16:03:07
  • 📌 你可以继承单例。这是一个强大但是经常被忽视的特性

    • ⏱ 2024-01-08 16:03:56
  • 📌 ·它们令代码晦涩难懂

    • ⏱ 2024-01-08 16:07:16
  • 📌 我们需要检查整个代码库来看是哪些部分访问了全局状态。直到你不得不在凌晨3点用grep命令从上百万行代码里检索出那个将静态变量设错了值的调用,你才会真正痛恨起全局状态量。

    • ⏱ 2024-01-08 16:08:08
  • 📌 全局变量促进了耦合

    • ⏱ 2024-01-08 16:30:15
  • 📌 ·它对并发不友好

    • ⏱ 2024-01-08 16:30:23
  • 📌 延迟初始化剥离了你的控制

    • ⏱ 2024-01-08 16:32:36
  • 📌 实例化一个系统需要花费时间:分配内存、加载资源等。如果实例化音频系统需要花费几百毫秒,那么我们需要控制进行实例化的时机。如果我们让它在第一次播放声音的时候延迟实例化,而游戏可能正步入高潮,那么此时的初始化将导致明显的掉帧和游戏卡顿

    • ⏱ 2024-01-08 16:32:33
  • 📌 看你究竟是否需要类

    • ⏱ 2024-01-08 16:37:33
  • 📌 服务定位器模式

    • ⏱ 2024-01-08 17:47:08

第7章 状态模式

  • 📌 允许一个对象在其内部状态改变时改变自身的行为。对象看起来好像是在修改自身类。

    • ⏱ 2024-01-08 17:47:35
  • 📌 你拥有一组状态,并且可以在这组状态之间进行切换

    • ⏱ 2024-01-08 18:41:55
  • 📌 状态机同一时刻只能处于一种状态。女主角无法同时跳跃和站立。事实上,防止同时存在两个状态是我们使用有限状态机的原因。

    • ⏱ 2024-01-08 19:09:57
  • 📌 如果你的状态类没有任何数据成员,并且只有一个虚函数方法。那么我们还可以进一步简化此模式。我们可以使用一个普通的状态函数来替换状态类。这样的话,我们的state_变量就变成一个状态函数指针。这个就是享元模式。(第3章)

    • ⏱ 2024-01-08 19:38:10
  • 📌 我们把两种不同的状态硬塞到一个状态机里面去了。为所有可能出现的组合建模,我们可能需要为每一种状态准备一组状态。解决方法比较直观,就是分开成两个状态机。

    • ⏱ 2024-01-09 11:31:41
  • 📌 你有一个游戏实体,它的行为基于它的内部状态而改变。·这些状态被严格划分为相对数目较少的小集合。·游戏实体随着时间的变化会响应用户输入和一些游戏事件。

    • ⏱ 2024-01-10 13:32:56

第8章 双缓冲

  • 📌 各类颜色像素到达软管的末端,软管将它们喷射到显示区域中,每次往每个像素上喷洒一点。那么它如何知道哪个颜色像素该往哪儿喷呢?在多数计算机中,答案是它从帧缓冲区(framebuffer)中获知这些信息。帧缓冲区是内存中存储着像素的一个数组(它是RAM中的一个块,其中每两个字节表示一个像素的色彩)。当软管往显示区域喷洒时,它从这个数组中读取颜色值,每次读取1字节。

    • ⏱ 2024-01-10 13:36:09
  • 📌 我们的程序一次只渲染一个像素,同时我们要求显示器一次性显示所有的像素——可能这一帧看不到任何东西,但下一帧显示的就是完整的笑脸。双缓冲模式解决了这一问题。

    • ⏱ 2024-01-10 13:43:22
  • 📌 定义一个缓冲区类来封装一个缓冲区:一块能被修改的状态区域。这块缓冲区能被逐步地修改,但我们希望任何外部的代码将对该缓冲区的修改都视为原子操作。为实现这一点,此类中维护两个缓冲区实例:后台缓冲区和当前缓冲区。

    • ⏱ 2024-01-10 13:50:37
  • 📌 当要从缓冲区读取信息时,总是从当前缓冲区读取。当要往缓冲区中写入数据时,则总在后台缓冲区上进行。当改动完成后,则执行“交换”操作来将当前缓冲区与后台缓冲区进行瞬时的交换

    • ⏱ 2024-01-10 13:50:57
  • 📌 交换本身需要时间

    • ⏱ 2024-01-10 13:51:30
  • 📌 我们必须有两份缓冲区

    • ⏱ 2024-01-10 13:51:50
  • 📌 这个模式的另外一个后果就是增加了内存使用。

    • ⏱ 2024-01-10 13:51:53
  • 📌 交换缓冲区的操作是整个过程中最关键的一步,因为在这一过程中我们必须封锁对两个缓冲区所有的读写操作。为达到最优性能,我们希望这个过程越快越好

    • ⏱ 2024-01-10 14:19:36
  • 📌 交换缓冲区指针或者引用

    • ⏱ 2024-01-10 14:19:39
  • 📌 这很快。

    • ⏱ 2024-01-10 14:19:51
  • 📌 缓冲区中现存的数据会来自两帧之前而不是上一帧。

    • ⏱ 2024-01-10 14:20:17
  • 📌 当我们要绘制第三帧时,在缓冲区中的数据来自第一帧,而不是来自最近的第二帧。在多数情况下,这并没有问题——我们往往在绘制前会清理整个缓冲区。但假如我们希望对缓冲区现存的某些数据进行复用,那么就必须考虑到哪些数据是比我们所预期的更提早一帧。

    • ⏱ 2024-01-10 14:24:08
  • 📌 在两个缓冲区之间进行数据的拷贝

    • ⏱ 2024-01-10 14:25:23
  • 📌 位于后台缓冲区里的数据与当前的数据就只差一帧时间。

    • ⏱ 2024-01-10 14:25:52
  • 📌 交换操作可能会花去更多时间。

    • ⏱ 2024-01-10 14:25:46
  • 📌 假如缓冲区是单个整体

    • ⏱ 2024-01-10 14:46:33
  • 📌 交换操作很简单,因为全局只有一对缓冲区,只需要进行一次交换操作。假如你通过交换指针来交换缓冲区,那么你就可以交换整个缓冲区而无视其大小,只是两次指针分配而已。

    • ⏱ 2024-01-10 14:46:39
  • 📌 假如许多对象都持有一块数据

    • ⏱ 2024-01-10 14:46:42
  • 📌 交换较慢。为实现交换,我们需要遍历对象集合并通知每个对象进行交换。

    • ⏱ 2024-01-10 14:46:45
  • 📌 假如不需要访问缓存的状态,那么我们就可以对其进行优化来使其达到与使用单块大缓冲区存储一系列对象状态一样的效率。

    • ⏱ 2024-01-10 14:46:59
  • 📌 此时的办法就是使用“当前”和“下一个”指针的概念并将它们作为对象内部的成员——相对偏移量。

    • ⏱ 2024-01-10 14:46:52

第9章 游戏循环

  • 📌 实现用户输入和处理器速度在游戏行进时间上的解耦。

    • ⏱ 2024-01-10 14:48:18
  • 📌 真实的游戏循环的第一个关键点:它处理用户的输入,但并不等待输入。游戏循环始终在运转

    • ⏱ 2024-01-10 14:51:30
  • 📌 两个因素决定了帧率。第一个是循环每一帧要处理的信息量。

    • ⏱ 2024-01-10 14:53:05
  • 📌 第二个是底层平台的速度。

    • ⏱ 2024-01-10 14:53:13
  • 📌 游戏循环模式的另一个要点:这一模式让游戏在一个与硬件无关的速度常量下运行。

    • ⏱ 2024-01-10 14:54:28
  • 📌 一个游戏循环会在游戏过程中持续地运转。每循环一次,它非阻塞地处理用户的输入,更新游戏状态,并渲染游戏。它跟踪流逝的时间并控制游戏的速率。

    • ⏱ 2024-01-10 14:54:42
  • 📌 让我们再试试稍复杂点的办法。我们目前的问题可以归结为:1.每次更新游戏花去一个固定的时间值。2.需要花些实际的时间来进行更新。

    • ⏱ 2024-01-15 17:36:44
  • 📌 多数游戏采用浮点数,而它们会带来舍入误差。你每次将两个浮点数相加,其返回的结果都可能出现左右偏差。Fred的机器做了比George的机器10倍多的运算,所以他累计了更多的误差。在他们的机器上,子弹将在不同的位置消失。

    • ⏱ 2024-01-15 17:38:20

第10章 更新方法

  • 📌 通过对所有对象实例同时进行帧更新来模拟一系列相互独立的游戏对象。 ^22651409-19-425-457
    • ⏱ 2024-01-31 17:04:02

读书笔记

第5章 原型模式

划线评论

  • 📌 而原型模式提供了一种解决方案。其核心思想是一个对象可以生成与自身相似的其他对象。如果你有一个幽灵,则你可以通过这个幽灵制作出更多的幽灵。如果你有一个魔鬼,那你就能制作出其他魔鬼。任何怪物都能被看作是一个原型,用这个原型就可以复制出更多不同版本的怪物。 ^262631416-7O447crxT
    • 💭 用一个Spawner接收传入的Monster,然后做克隆。
    • ⏱ 2024-01-08 13:40:41

划线评论

  • 📌 原型模式效果如何 ^262631416-7O48Izg09
    • 💭 原型模式的主要优势在于它可以避免初始化对象所需的开销,特别是当对象的创建过程复杂或者成本高昂时。例如,如果一个对象的构造过程包括数据库查询、网络请求或者读取文件等操作,那么复制一个已经创建好的对象就会比重新创建一个新的对象更有效率。
    • ⏱ 2024-01-08 14:50:58

划线评论

  • 📌 这样的话,每一个怪物类只要包含孵化函数指针即可: ^262631416-7O49lC2t9
    • 💭 用C#重写
public delegate Monster SpawnCallback();

public class Spawner
{
    private SpawnCallback spawn;

    public Spawner(SpawnCallback spawn)
    {
        this.spawn = spawn;
    }

    public Monster SpawnMonster()
    {
        return spawn();
    }
}
- ⏱ 2024-01-08 15:00:35

划线评论

  • 📌 头等公民类型 ^262631416-7O46J2CK4
    • 💭 “头等公民”(First-Class Citizen)这个术语在编程中通常用来描述一种语言特性,这种特性允许某种类型的元素(如函数、对象或者值)被像其他类型的元素那样使用。这意味着这种元素可以作为函数的参数,可以作为函数的返回值,也可以赋值给变量。
    • ⏱ 2024-01-08 14:20:32

划线评论

  • 📌 为了调用C++里面的一个虚函数,你需要找到该对象实例的虚表指针,然后通过该指针去调用实际的方法。 ^262631416-7O47us6Ks
    • 💭 上次面试没答上来!
    • ⏱ 2024-01-08 14:32:13

第6章 单例模式

划线评论

  • 📌 确保一个类只有一个实例,并为其提供一个全局访问入口。 ^262631416-7O49oGaVs
    • 💭 单例模式的定义
    • ⏱ 2024-01-08 15:01:20

划线评论

  • 📌 ·通过服务定位器来访问。到现在为止,我们假设全局类就是像Game那样的具体类。另外一个选择就是定义一个类专门用来给对象做全局访问。 ^262631416-7O4hKIyi9
    • 💭 单例模式的缺点主要是由于它经常被用作全局访问点,导致代码高度耦合,而不是由于它的单一实例特性。单一实例本身并不是问题,问题在于如何管理和访问这个实例。
    • ⏱ 2024-01-08 17:08:55

第7章 状态模式

划线评论

  • 📌 “图灵完备”意味着一个系统(通常指的是一门编程语言)是足够强大的,强大到它可以实现一个图灵机。 ^262631416-7O5rTiQjI
    • 💭 图灵机(Turing machine)又称为确定型图灵机,是英国数学家艾伦·图灵于1936年提出的一种抽象计算模型,其更抽象的意义为一种数学逻辑机,等价于任何有限逻辑数学过程的强大逻辑机器。
    • ⏱ 2024-01-09 11:30:23

第8章 双缓冲

划线评论

  • 📌 外部代码无法存储指向某块缓冲区的持久化指针。 ^262631416-7O79wKlVr
    • 💭 每一次都要去获取当前正确的缓冲区的位置
    • ⏱ 2024-01-10 14:23:20

本书评论