Tile Based Engine的设计 - 精灵链表

 通常说来, 第三人称 2D 游戏中通常把景物和精灵分开处理 (至少我是这样的) 尤其是游戏机上, 硬件对精灵有支持. 现在我们的显卡多也支持显存间的 keycolor 检查 Blt 操作, 实际就是用来加快精灵处理的 (也包括景物)

   精灵在运动时, 往往是基于像素的 (虽然有人喜欢简化设计, 精灵在停止的时候仍旧是站在格子里) 而景物却是静止在格子中. 如果能使用更有针对性的方法分别绘制, 将可以提高游戏的速度. 本文的观点源于云风早前所写的斜视角图形引擎的设计系列. 并在近期实践(制作一商业A.RPG游戏)中得到完善.

  实际上, 游戏中每帧图象, 没有必要每次用各个图素合成, 尤其在使用了大量如Alpha轮廓, 透明等大运算操作的情况下, 屏幕上并未更改的区域重复被运算非常的浪费时间. 所以我们可以借鉴游戏机的做法, 场景图面创建的稍稍比屏幕大一圈, 只在屏幕移出窗口时再补绘场景, 把精灵提出, 每帧重绘于场景上.

  合成精灵与场景有三大问 题, 一是如何处理精灵的遮挡问题, Isometric Tile Engine 的遮挡处理一文阐述了云风的观点; 其二是如何清除上一帧的精灵: (当然这一步也可以省略, 改为将场景层和精灵层合成到第三缓冲区) 如果不想多建立一屏幕后台缓冲区, 减少合成图层时的数据移动量, 我们可以采取在绘制精灵前保存精灵原处的场景图象. 具体实施方案在后面将有详细解说。

  最后一大要点是以正确的次序绘制精灵了. 这里我想使用一个链表, 串起场景中的所有精灵, 姑且将它称为精灵链表吧. 同时我们还需要另一个链表保存屏幕上可见的精灵, 减少我们处理精灵的数量.

   场景精灵链表是创建场景时创建的, 在某精灵消失(死亡)后从链表中删除, 精灵也可以在游戏时被创建加入链表. 而屏幕精灵链表却是绘制屏幕时动态生成, 保证其中精灵满足从后到前的顺序. 这样我们只需要在产生链表后, 依链表次序将精灵绘制到屏幕就可以了 :-)

那么整个处理过程如下:

  根据当前的屏幕精灵链表清除上帧图象中所有的精灵
  清除当前屏幕精灵链表
  遍历整个场景精灵链表, 处理每个精灵的动作和状态, 如果精灵正在屏幕上, 就将其按前后次序插入屏幕精灵链表.
  将即将绘制的精灵位置的场景保存
  按屏幕精灵链表次序绘制精灵到屏幕
  显示一帧图象
  循环这些步骤

   第一步清除上帧精灵的具体操作中, 我们可以为每一个精灵分配一个缓存图, 保存绘制前的场景. 但这样比较浪费. 因为不在屏幕上的精灵根本用不着这个缓存图. 所以我们可以进一步优化为, 精灵进入屏幕时才分配缓存图, 移出屏幕就释放掉. 不过内存分配实际是一个很消耗时间的过程. 当精灵大小都类似时,进一步的优化方案是限定屏幕上可以同时出现的精灵的数量, 初始化时就统一分配一批等大的缓存图(我称其为缓存池), 其每一个都足已容纳最大的精灵. 当某个屏幕精灵需要缓存图时, 就从缓存池中找一个没有使用的供其使用, 精灵移出屏幕后, 放弃对缓存图的控制即可.

  关于第3步将存在于屏幕的精灵插入屏幕精灵链表的操作, 涉及如何判定精灵的前后关系. 我们可以在作图时, 就将精灵的重心定为其参考点保存中图素的文件中. 利用这个参考点的坐标就可以完成精灵的前后判定了 ;-)

  本文总结了云风近期制作游戏中的些经验, 希望对后来者有所借鉴. 成文仓促, 错误在所难免. 提及方法也是我的个人观点, 游戏设计的魅力在于其设计时可以不遵循常例,任意发挥, 在此欢迎大家一起探讨, 指出错误和不足.

 
云风