游戏中的战争迷雾

说到战争迷雾(Fog of War, FOW), 其实还是非常普遍的一项技术, 在RPG, RTS等游戏中得到了广泛的应用 
游戏中的战争迷雾_第1张图片 游戏中的战争迷雾_第2张图片
但是关于这方面的资料非常少, 经常会有人问怎么做, 所以就有了写这篇文章的想法
从应用上来说, 有两种:
  • 探索范围: 通常在RPG游戏里, 用于标记走过的地图区域
  • 视野范围: 通常在RTS游戏里, 用于标记友方单位可以看到的区域
当然, 在魔兽争霸3中, 两中迷雾都存在. 下面以第二种为例详细说明, 第一种看完自然就明白怎么做了


好了, 既然是个可见性问题, 那么就有阻挡视线的地方和可以看到区域, 我们抽象一下, 把地图像寻路那样表示成二维格子
游戏中的战争迷雾_第3张图片
绿点代表有视野的单位, 红色区域代表视野阻挡, 那么白色区域就是可见的, 灰色就是不可见的
求解这个可以与不可见区域的问题, 其实就是一个FOV(Field of Vision, Field of View)问题, 算法上来说, 五花八门, 参见[1]

有了这个可见性的区域信息, 怎么绘制到场景里呢? 
既然可以表示成一个二维数组, 那么就可以把这些信息写入一张2D纹理, 然后把这个纹理投影到整个地图(不管3D还是2D, 原理一样)
那么, 纹理会不会太大? 按我们的经验来讲, 一般一米一个格子的精度是完全够用的, 256x256的大小可以适用于大多数的游戏
如果嫌锯齿太难看, 做个模糊就行了. 3D游戏中更是可以在shader中针对视野内和视野外使用不同的着色方式(比如去色)
游戏中的战争迷雾_第4张图片
原理其实看起来很简单, 这也是多数帖子和文章可以提到的, 但是实际做下来, 性能还是比较棘手的一个问题
目前来说, 我们在此基础上做了一些优化:
  • 降低更新频率
    • 如果游戏是30FPS的, 那么其实没有必要每帧都去计算这个可见性. 经验值的话, 0.5秒一次差不多
    • 由此带来的跳变问题, 可以保存相邻再次计算的结果, 写入迷雾纹理的两个通道, 在shader中按时间进行插值过渡
    •  游戏中的战争迷雾_第5张图片游戏中的战争迷雾_第6张图片

  • 大量移动单位和大视野不动单位[2][3]
    • 这个用个循环暴力计算显然不现实, 除非你地图很小-_-. 所以, 优化思路就是针对移动的单位重新计算, 针对不动的单位只算一次
    • 首先, 我们可以用坐标(X,Y)和视野半径(R)三个值确定出一个唯一值K代表一个提供视野的单位U
    • 对于每帧新加入(K当前不存在)的U, 定义其生命周期L=MAX. 其余的就对U的L进行递减
    • 移除L<=0的U. 针对于L=MAX的, 计算其可见的格子, 如果可见, 则格子的计数C加1
    • 每个格子的可见计数C>0的表示可见, 否则就是不可见, 然后更新到纹理上
    • 对于0
  • 预计算FOV
    • 如果视野阻挡不会变化的话, 可以针对每个点计算其最大视野范围的可见性信息, 缓存起来
    • 如果用1bit表示每个格子, 最大视野范围20, 地图大小256x256个格子的话, 所需要的内存空间为41x41x256x256bit=13.13MB, 如果再剔除掉视野阻挡内的点, 应该更小
另外, 对于小地图, 正好可以把迷雾纹理与背景直接混合, 做到二次利用
对于探索范围的表示, 其实只要在格子的计数器上做点手脚就好了
YY一下, 3D的体积迷雾是不是可以用Volumn纹理实现:)

参考资料
[1] http://roguebasin.roguelikedevelopment.org/index.php?title=Category:FOV
[2] http://www.gamedev.net/topic/530346-efficient-fog-of-war/
[3] http://www.gamedev.net/topic/549305-fast-fog-of-warexploring-in-rts/
[4] http://www.gamedev.net/topic/615615-line-of-sight-in-tiles-base-map-fog-of-war/
[5] https://code.google.com/p/libfov/

你可能感兴趣的:(游戏逻辑)