十年前非常幸运, 偶然的机会接触到电子词典游戏开发. 当时步步高朗文4980支持zwBasic语言, 文曲星则支持GvBasic以及Lava, 不少玩家用来写游戏, 我也是其中一员. 现在回想起当中的一些优化方法, 还是相当有意思. 当时zwBasic解释器提供给basic程序的内存空间是9kb, 并且没有文件读写的功能. 在这种情况下制作rpg或者战棋游戏几乎不可能, 光是地图数组就能把内存耗光. 于是只能制作一些类似简单的格斗游戏之类的小游戏. 即便如此, 由于CPU速度的缓慢以及解释器本身效率问题, 游戏的性能优化直接决定了这个游戏能不能玩.
今天看来, 一个程序代码, 一定要拆成很多个部分, 分别放在不同的函数里面, 然后在使用的时候调用. 这样不仅结构清晰, 而且易于扩展. 但是在当时的电子词典上面不是这个情况. 因为函数调用的前后需要一些入栈出栈的操作. 如果将一些使用非常频繁的, 而且占用时间不多的语句放进一个函数里面, 大量调用这个函数, 那么整个游戏就会变得非常的卡.
刚开始的时候, 我不使用任何函数, 写了一个格斗游戏, 流畅性还非常不错. 后来我看了一些pc平台上的游戏代码, 发现使用函数很棒, 看起来简洁明了, 思路清晰. 因此我把我的格斗游戏进行了大量的修改, 一层层用函数模块化. 改好之后代码赏心悦目, 让我心花怒放. 结果一运行, 发现简直日了狗了, 流畅的格斗变成了ppt翻页展示的效果, 一拳过去击中对方还要等两秒他的HP条才会下降, 更不用说按键延迟(没有多线程, 只有所有逻辑和绘图语句全放进一个大loop里面).
但是我又不甘心使用本来那个完全没有函数的版本. 最后我对所有的函数都进行了评分. 评分公式就是 函数被调用的次数 / 函数里面的语句数量
, 只有分数低的, 才有资格保留其函数结构. 其余通通打回原形. 可惜当时zwBasic不支持 inline 函数, 不然我就不用这么痛苦的把一些重复代码复制粘贴的到处都是了.
这样一来, 游戏代码的可读性大大提高, 而游戏的性能却不损失多少.
在一开始的时候, 游戏中所有的坐标, 都是以屏幕左上角为(0, 0)圆心. 后来在看到爸爸用AutoCAD画机械图的时候, 坐标原点是可以随意更改的. 我想了一会, 觉得应该是有个坐标转换引擎, 记录了不同的点使用的是哪一个坐标转换公式.
如果自己写一个坐标转换公式, 那么在画游戏界面的时候, 就可以对屏幕进行分区处理, HP区域和主战斗区域就可以分别使用不同的原点来确定自己的坐标, 而人物本身的动画也可以使用自己的坐标原点, 这简直是非常爽的一个系统.
但是事与愿违, 原因还是cpu速度太慢, 每一次进行绘图之前都要使用不同的公式来计算一个自定义坐标对应屏幕的坐标值, 速度非常慢. 最后不得不放弃. 但是后来, 文曲星电子词典平台的Lava语言, 不再是解释执行, 而是像Java一样有一个虚拟机, 这时候速度就非常快了, 坐标引擎也完全没有性能问题了.
我在文曲星做格斗游戏的时候, 看到已经有一些经典作品, 比如模仿木棍小人的格斗游戏. 木棍小人是非常简约风格的格斗游戏, 人物由圆形和长方形构成头, 身体, 四肢. 那么在战斗的时候, 身体的各个部分都要经过计算. 这样的游戏当时在 Flash 平台上并不少见.
这个游戏代码非常高端, 但是运行起来就比较卡. 而且当时我不太喜欢简约风格, 我喜欢酷炫的卡通人物形象的格斗, 就像拳皇一样的. 而且贴图不需要计算一个人四肢的运动情况. 最后我使用了贴图方案, 效果非常好.
当时的图形api并不提供屏幕缓冲区, 所有的图片一画出来就立刻显示在屏幕上, 因此所有的动作都需要擦除屏幕, 然后再画, 否则双人格斗不一会儿搞得满屏幕都是人物轨迹. 如何擦除也是一个重要的性能优化问题. 首先是语句摆放的顺序问题.
Loop: (各种逻辑代码) 在屏幕上绘图(); // A 擦除屏幕(); // B (各种逻辑代码) End Loop
考虑上面这个代码, A和B的顺序问题. 如果先A后B, 则屏幕闪烁非常厉害, 简直比ppt还ppt. 但是如果调换A和B的顺序, 则表现就好得多了.
其次是擦除全屏幕还是擦除部分屏幕的问题.
类似的优化还有很多, 这些优化在今天看来都特sb, 但是给人很大的启发: 游戏开发, 很重要的一点就是要结合实际情况来做优化. 完美的理论, 都要与设备性能做权衡, 在理论美与现实快当中做权衡, 这个权衡的过程, 就是思考与进步的过程.