<<游戏之旅>>笔记

最近读了云风大大的书, 感谢云风, 对我有所帮助。
选读 第1章, 第2章, 3.1, 3.2, 3.3, 3.5.2, 4.4.4, 8.1, 第9章, 13, 14

对我有所启发的points

  • 写email的建议
  • C和C++的关系
  • C++的开发经验
  • 学习C++阅读次序
  • template的技巧
  • 开发的成功和失败的经验
  • 游戏编程几个反思
  • 后记中编程实践和思考方法
  • 致谢中云风父亲的背景以及教育方法

2. 算法

2.1 程序 = 算法+数据结构

计算机解决的问题,看成一个需要求解的函数,

  • 算法:将输入转化为输出的方法。
  • 数据结构: 需要解决问题中的信息用计算机进行的数字描述方法,以及相应的对这些信息的操作。

2.1.1 算法

时间和空间之间寻求平衡。

  • 空间换时间的做法,非常广泛,即预处理
  • 时间换空间,不容忽视,即使重复计算。

依赖平台特性:

  • 非并行计算机,不考虑用并行来提速。
  • 空间允许随机访问,不是纸带机的顺序。

2.1.2 数据结构

简单定义:对数据的组织方法,还有对数据组织方式的处理方法。
C++已经提供了常见的数据结构,为什么还要学?

  • 学蕴含思想
  • 根据实际情况对数据结构进行改造,更高效。
1. 线性表
有限逻辑上的有序数列,有确定的前驱和后驱;
分为成:数组和链表;

数组:
2. 堆栈、队列和串
3. 树、二叉树及其他

树:
有层次的数据集的组织方式。

GUI界面,通常用树来组织;
游戏中的对象管理,用树来解决管理上的层次问题。

二叉树:
严格来说不算是树。
表达式计算,数据压缩,排序查找方面有很多用途。

四叉树、八叉树:
空间状态划分,

  • 四叉树:平面
  • 八叉树: 空间

空间: 场景空间+ 调色盘算法等(向量空间)

图:
节点没有父子关系,纯粹的点和边的集合。
节点和节点之间允许加上一些与它有关的数,称为权(weight), 带权图称为网络。
节点和节点之间可以有方向,也可以无方向。

图应用于现代网络游戏中,多服务器设计,或者三维游戏中的大场景描述,值得开发者好好研究。

映射表
std::map,STL最复杂的容器。

禁忌思想,

  • 模拟人的记忆过程,从某点开始,想临近区域扩展解。经过的地方一概进入禁忌状态,为了短期搜索回来,造成循环搜索,会有一个记忆寿命,超过若干步骤后解决禁忌。这个步骤叫做Tabu Length, 禁忌长度。禁忌长度过大会导致计算量增加,过小则会进入循环搜索。
  • 蚂蚁队伍在附近找最高点。任意选择一个起点,一开始就保持队伍胡乱地向周围爬。蚂蚁队伍遵循一个原则,就是不能回到最近经过的位置。

算法一种思想,自己动手实践,了解这些方法,慢慢地就可以真正运用它到实际中去。不光光要会描述一个问题,以适应不同的算法;还要了解更多的算法会让我们更快解决棘手问题。

2.4 优化

  • 数学方法的改进
  • 预运算来节省时间(空间换时间避免重复运算)或是重复运算来节省空间
  • 简化算法求得近似来取代精确解(或最有解)
  • 改进数据组织方式,用更少的操作处理更多的数据,甚至避免冗杂数据的处理。

碰到棘手的问题,不要先急于找Google,或者问别人,应该自己思考最好的解决方法。每个人都有自己无数的解决方法,每次独立的思考,都是对思维的一次开阔。

<>

C
C语言本身是简洁的。语言本身仅仅只是提供了一种用计算机角度实现算法的符号。
C语言函数,让问题分而治理它。
全局变量,可以让函数之间不通过输入参数来访问一些公有的数据。
最让人诟病就是指针的设计。从高级语言的角度来讲,程序员不需要指针。我们只需要有可以指带数据的标识之物。

BASIC

比较解释语言和C语言的不同

解释型语言:

  1. 每运行一次,解释器就读一次,对符号进行翻译
  2. 中间状态以严格规定保存起来,在后面的语句需要时被重新加载
    C语言
  3. 在编码后需要多做一些工作,即他们会被翻译成为机器码,运行时就不需要翻译。
  4. 编译器对代码同类向合并,最后的机器码会很简洁。

逻辑学教育,BASIC,适合理解编程。

3. 编程语言

3.3 C++

C++的不同理解

第一印象可能是为了面向对象的设计,实际上C++是一种支持各种编程范式的语言。它支持面向过程的编程,基于对象的编程,面向对象的编程,以及使用template实现的泛型编程。

C对于C++的优势

简洁,而非高效;适合做小内存的嵌入式系统开发。

但大多数游戏平台,C++比C更适合

3.4 汇编语言

合适场合使用汇编,能让程序跑得更快。
了解汇编,能让你了解代码最终会以什么形态运行于CPU,对理解高级语言有所帮助,
在紧要关头帮忙,程序出现莫名错误,又不能在源码级调整程序。

4. 前Windows时代

4.3 保护模式下的开发工具

个人主页建立之初,我罗列出了个人兴趣 :…很多。但是大而全的效果是,我一样都做不好,而且和其他人的个人主页相比没有特色。稍加考虑,立刻砍掉了除了游戏开发之外的所有项目。

  • 翻译allgero文档:自己做了一个辅助翻译的工具;细致了解; 交朋友
  • 写关于游戏编程技术方面的小文,各处转载

  • 利用假期,走访网上认识的从事游戏开发的朋友,慢慢踏上了游戏制作的道路。

学习新的技术,翻译一本相关的英语著作可以算是捷径。

  • 比囫囵吞枣的读一遍英文原文要有效的多。
  • 因为有责任感,必须用心搞清楚每一个句子的意思,以免错误而误导阅读你的译作的人们。
  • 即使对此有所了解,翻译后也能更上一层楼。
  • 英语水平也会提高。

4.4 闲话allegro

4.4.4 几何图形和3D

图形引擎提供,可以绘制一些几何图形,点,线,多边形等等,但制作平面游戏这些并不是必须的,因为游戏中的一切,事先都可以用预先制作好的图片代替掉。

3D游戏,却是基于多边形。通过空间多边形的网络信息,把一些平面的图片,做一些变形,映射到多边形网格上。
相关的:

  • 透视映射
  • 矩阵,三维空间中变换的工具, 自带一套矩阵运算。
  • 实现了一套定点数的运算该改善游戏中需要的数学运算速度。

1,定点数
巧妙的利用定点数,可以避免许多浮点误差问题,是很有价值的。

浮点数:计算机用一种纯小数加指数的形式表示实数。
定点数:利用整数运算来模拟小数的方法,那就是定点数来表示小数。

高16位,表示整数位;低16位,表示小数部分;实际是60000+倍,小数点的位置是固定的。

精读在1/6*10^4, 十进制小数点后四位的精度。

定点数的一些计算技巧和优势:

  • 原本复杂的三角函数计算,可以依照所需要的精度,预先制作好查询表,以空间换取时间。
  • 角度表示方式不是360和2pi, 而是64表示直角, 256表示圆周角。

4.5

云风关于写email的建议:

  • 回复收到的每封email, 即使没空写, 也让对方知道自己已读。
  • 使用纯文本,发送过大附件前请求
  • 合适的标题,能概括内容;偏题时,修改标题。
  • 删除尽可能多的引信,尤其是对方的签名,对主题表达无意义。
  • 合理分段,表达自己意见,别赞同两字多写一点。
  • 发出之前,读一遍,修改掉错别字和语法错误,核对一次收件人是否正确
  • 不要随意公开转载私人信件,即使转载也注明出处。

8. 引擎中的优化

“风魂”中很多蹩脚的编码方法,不成熟的整体架构、非系统的编码规范和不严谨的接口定义,但对于自己干了这件贻笑大方的事情,自己却不后悔。敢于把自己的无知展现给世人,是一种勇气。

只要保持真诚谦逊,错误的存在就能得到理解;整体上或许不完善,但还是有后来者,依旧可以从中学到闪光点,避免踏上弯路。

9. C和C++

9.1 从C到C++

1, C只提供了CPU本来就提供的操作,把他映射到更容易让人理解和描述的书写形式上。为了让程序员使用,引入了栈上临时变量,和堆上动态变量的概念,而不是直接面向寄存器。

2, C对大块的数据,使用数据指针来表达。

3, C一个个函数组织起来,函数之间按层次调用,去处理那些不同结构的数据

4, C是对汇编语言的一种抽象, 正如汇编语言是对机器底层指令的一个最小幅度的抽象,为了人类方便控制机器。C程序员要做问题描述和机器模型之间的桥梁。

5, C是容易学的,它非常接近机器模型,而机器模型非常简洁,有条理;有效率,接近机器模型;困难性,离实际描述太远,在问题描述和机器模型之间找到对应关系,会随着问题的复杂性提高而急剧增加。

困难性的说明
高级语言往往改为对问题本身抽象,把问题归为特定的类别,然后,语言本身只解决这些被抽象的问题,来简单编程的难度,但往往只能针对特定的类型

C++面向对象的说明

  • 既然保持强大的抽象能力,又保持底层符合机器模型的优势,在设计变得简单的情况下不失效率。
  • 将对象分为不同的类型,每个对象都是这种对象的实例。类型的设计是有层次的,就好像动物是一种大类型,而哺乳动物和爬行动物都属于动物。

9.2 C vs C++, 效率至上

C++中混有C语言的好处:

1, 更广泛的移植性
2, C的思考方式,会让不致于陷入面向对象和泛型编程的泥沼,只是说很容易钻进设计方法的牛角尖
3, 良好的C语言接口可能使得模块的使用方法更容易理解,对多人合作和多语言编程是一种好事
4, C简洁, 编译速度快

9.3 优雅的C++

将C细化,又不增加额外的开销;
不对程序员有过多限制。

9.3.1 宏

宏的作用

1, 定义常数
2, 代码生成
3, 内联代码
4, 对编译流程作出选择

1, 定义常数

#define PI 3.1415926f;
const float PI;

C++会选择inline函数+template定义常数,最终编译器会把它优化成一个常数,而没有对函数调用的消耗。

经典的如:

#define min(a, b) ((a<b)?(a):(b))

template <typename T> const T& min(const T &a, const T &b) {
return a<b ? a:b;
}

9.3.2 const修饰以及类型转换

const修饰

const chat 而不是 C 的 char

将函数参数写成const;
成员变量修饰成const, 只有在构造的时构建他们;
为成员函数增加一个const, 表达这个函数不会修改类的成员变量

类型转换

C中几乎任何类型都可以任意转换,缘于C汇编的根。
C++用static_cast 以模板的语法表达看起来可以互相转换的类型互换

const和const之间的转换,只能通过const_cast转换。

严格描述每个对象的const性质,可以帮助在编译期发生错误减少。

9.3.3 隐藏实现

好的C++程序会把所有数据都声明成private的,尽量在同一类中暴露过多的public方法,而protected慎用,至少尽量不用在成员数据上。

作为良好的面向对象设计,继承类很少去扩展基类的功能,这样子作为具体实现,被隐藏于接口后,结构变得干净整洁。

9.3.4 引用而非指针

指针是导致资源被遗忘释放的罪魁祸首。

  • 当旧的指针被赋予新值,旧的值消失,如果消失的值是唯一指向某个对象的地址,那么这个对象控制的资源将永远留在内存中,直到进程结束被系统回收。

引用和指针在实现本质上是完全相同的,但引用的语法只能在构造的那一刻被赋值,并永远不能修改。

C++优雅设计中,会用引用代替成员变量中的指针,而减少实现类的时候不小心犯错误。

9.3.5 命名空间

引用外部的名字空间到当前位置。可以防止不同库之间的名字冲突。

9.4 C++的误区

自己的一条准则: 尽可能地使用结构最简单的工具来完成任务,直到这个工具不合适。

举例:

  • 能用C写的程序,不用C++;能用C的原生数组的情况不用std::vector。能用std::vector的情况不要用std::map, 能自己写的代码不用第三方库。

解释:

  • 没有完美的程序,也没有完美的原则。
  • 不主张写C++的标准库,重写MFC或者广为大众所使用的代码。大多数C++程序员,没到达这个技术理解。

9.4.1 类层次过细

会造成间接调用引起的损耗。

9.4.2 滥用操作符重载

为了让用户写出的类适用于现存的模板,让用户构造的对象和C++原生的类型有相同的表现,操作符重载是有意义的。
如果仅仅是让程序看起来紧凑,或者是类使用起来“好玩”,那就是对其的滥用了。

9.4.3 滥用标准容器

用std::vector就不想再碰语言中的原生数组,用过std::string, 就不知道 const char* 是为何物,这是许多C++程序员的通病。

std::map可以这样子做,
如果只是为了创建一张key-value的对应表可供查询,完全可以用
key-value对应的关系记录下来,只做一次排序,在检索的时候可以使用二分查找查找,一种快捷又节省内存的方法。

9.4.5 滥用多重继承

会造成实现过于复杂,几乎所有的多重继承问题,都可以化为组合方式来解决。

9.4.6 忽视C++高级特性的复杂度

如果把两种复杂度高的特性运用于项目中,其复杂度不是两倍,而是平方。

9.4.7 学习C++

我现在无法把自己学会的东西,理解的东西借助文字教给入门者,因为许多的知识需要自己在实践中领悟。过多的细节,太快地展现出来反而会增加学习的复杂度,难以消化。

因此,需要

1,学
2, 用
3, 思考
2, 回到1

如果期望C++成为自己开发中的利器,应该尽量多用C++做项目,尽量可能地体验更多的设计方法,用心去写程序,而不是单单去实现而已。自己写出的代码,多多思考,对感觉不好的部分重新设计。

推荐一个阅读次序:

1, 先学会C,只是也是C++子集的部分。
2, <>
3, <> 和 <>
4, C++ 标准文档(1998定制,电子档),遇到问题不是去查某本教材,而是直接翻阅文档。
5, <>,深入了解C++设计的根源以及C++编译器实现方法入手.
6, <<深度探索C++物件模型>>对追求高效的C++程序员尤为受用。

进阶学习: 需要一定的开发经验才能阅读

  • <>, <>, <>
  • 泛型编程<>,有点难,但是花上一定的时间一定有收获。
  • <>template使用自信,对泛型编程感觉良好,
  • <>想更精通标准库,C++程序员案头必备的参考手册。
  • <>, 与前面的effcive系列一样

9.5.3 再论动态内存分配

//…

理解动态内存的捷径,自己写一个内存分配器。从一大块给定的内存上,分配出用户提交的内存。

9.6 template

template最初只为了取代C++语言的宏设计,后来被赋予了实例化的特性,可以针对某些特别的类型做特殊的操作。

9.6.1 封装C++的成员函数的调用

9.6.4 避免重复代码

大部分程序错误的根源:

  • 在多个地方表达相似的概念,意味着日后改动一个地方,就必须记得改动相似的所有地方。而直接复制这些代码,导致编译器并不知道这些地方的相似性,不能为你提供帮助。而人,随着项目扩大,几乎不可能记住做过多少次这种复制动作。

没有意识到在重复,可以用加强对代码糟糕味道的嗅觉敏感度和提高作为编码者的责任心来达到。

举个blit()例子

9.6.5 选择最佳的容器

很多语言都提供了一种未定义类型的变量,可以用来保存各种不同类型的变量。而C++没有提供这样的类型,但是可以用template来模拟一个,也就是设计一个容器存放不同类型的对象。

对于一个容器到底是保存

  • 对象指针
  • 对象值

  • 对于体积较大的对象,保存指针, 复制很快

  • 对于体积较小的对象,保存对象值, 减少间接;减少指针空间的占用

可以利用模板在编译期间由编译器自动进行;

9.6.5 延迟计算

由于涉及到了关于重载运算符的内容,暂且通读。大概是讲,可以自定义一种运算方式,但是又要对这种运算方式进行优化,所以需要模板的帮助,在编译期就对他进行不同函数的选择。

9.6.7 编译时的计算游戏(未完成)

三个数排序
+

9.7 小结

早犯一天错误,就可以早一天改正错误。

13. 开发方法

13.1 失败的经验

1, 过多的工作压力压到一个人身上。
2, 过分的弹性工作制
3, 没完没了的变化和返工
4, 没有及时的测试
5, 项目的主导严重偏向了某一职位上

1, 过多的工作压力压到一个人身上。
迫于压力,无法学习新的东西,使用好方法解决问题。独揽大局,只能解决眼前碰到的bug, 耦合度太高。

2, 过分的弹性工作制
兴奋时效率高,停下来几天没有进展也可能发生。到了项目后期,bug重重,受到挫折之后,失去新鲜感,假借弹性工作制之名,导致怠工拖垮项目。放弃的时候,并非没有压力,可能只是不知道下一步该怎么做,或是问题太多,无法入手,项目已经失控。

3, 没完没了的变化和返工
想法太多,什么都想加入进来,导致最后漏洞很多。

> 4, 没有及时的测试
很少有严格的测试,而是把错误积累。

5, 项目的主导严重偏向了某一职位上
程序员,策划,美术,盲目的跟从和固执的坚持自己都可能会影响整个项目.

13.2 成功的经验

1, 引擎和实现的分离
2, 结对编程(XP 极限编程)
3, 随时方便地测试
4, 尽早发现结构上的问题, 并尽早重构
5, 其他

1, 引擎和实现的分离
早期的程序员都是研究图形显示的技术开始的,总想表达更绚丽的图案,游戏程序变成了代码的集合。
许多程序员都是模块见耦合度过高,对于软件的整体稳定性不利。
和图形图像打交道的人,整个团队一个人就够了,他不需要去管任何的游戏的逻辑。

其他还包括,图像处理模块,声音,网络,时钟,文件读写,windows窗口控制,需要和操作系统直接打交道的东西,不管多简单都应该分离。

2, 结对编程
找个水平差的不太远的程序员和自己配成一对,只有一台计算机,大家选一个人坐在键盘前,另外一个人口述。两个人需要不断的交流,频率不应该低于一分钟一次。整个设计思想是由后面只动口不动手的人主导,而由键盘操作的人实现。由于人的思维速度是快于键盘输入的速度的,那么观看的人有空闲的时间可以用来思考,很容易看出代码和结构的问题。

潜在问题的代码,在XP极限编程中,被称做代码的坏味道

好处:

1, 促进参与项目的程序员的自身的提高。水平较低的学习新东西,水平较高的把思路说出来整理思路。
2, 参与项目人员互换位置,使得维护繁杂的文档不再那么重要,一旦有人离开,项目不会受到影响。大家的交流更顺畅,关系更融洽。
3, 提高工作效率。单独工作遇到问题刷网站,而这种方法是交流解决问题。互相监督和激65励。

3, 随时方便地测试
测试应该从开发者开始,从项目一开始就开始。

4, 尽早发现结构上的问题, 并尽早重构

5, 其他

脚本和版本控制。
软件开发的素养。

14. 编程和游戏

技术派在挖空心思模拟出更真实,更绚丽的画面;创意派在为自己构思的游戏中的一个绝妙的主意沾沾自喜。
而忽略了一些更重要的东西。

14.1 操作

任何一个游戏都是玩家和程序之间通过操作设备进行交互产生乐趣。

14.2 角色设定

生动的角色,RPG,增加游戏气氛

14.3 操作技术

3D和2D

  • 应该由游戏本身的需要决定。
  • 3D技术问题是通过一些近似算法而不是数学上严格的方案,得到令人满意的效果。
  • 2D技术怎样控制和管理不断膨胀的图片数量

14.4 浅谈网络游戏

社会性
除了传统的游戏它自身的娱乐性之外,还存在一种社会性。
因为社会性,就更需要在游戏中设计完备的经济体系,追踪货币的流通,实物的交换,弄清游戏社会中的经济是如何运作的。

看似不是程序的事情,但非程序的策划很难去做:

  • 大规模的数字采集和统计的工作
  • 大规模所要求的服务器架构,需要减轻数据库负担,减轻服务器压力,必须在设计上针对硬件水平作出精简。只有熟悉软件架构的人才能去做。

平衡性

  • 传统游戏中渡过来的方法,多数是依靠经验而不是数学推算来解决,慢慢的会变得不那么有效果。
  • 程序员有数学和计算机运算工具的能力,可以完善为平衡而更理论化的工具。

14.5 小结

一个好的游戏程序员和策划之间的界限往往很模糊,所以游戏程序员除了编程方面的修养,各个领域的只是都应该有所涉猎,以提高自身的修养。

后记

技术问题,固然本源的方法,理论变化不大。但计算机这种偏重工程的实践,一旦实际化到具体上,却有日新月异的变化,想要搞清楚问题,不是朝夕之功,只好借着自己对旧知识的理解去参悟新的知识。

致谢

记得在初中毕业前夕,我曾经萌发过放弃上重点高中乃至上大学的想法。但是,面对盛怒之后,老泪纵横的父亲,第一次看到父亲的眼泪时,我理解了,理解了一个求知欲如此强烈的人却由于时代的错误,甚至连高中都没有机会去读,在繁忙的工作中,熬夜自学考上大学的人,怎能不对自己的儿子在学业上有更高的期望呢。