在学习quake3源码时, 有必要先从总体上了解 quake 的设计思路和结构。
以下内容主要来自Michael Abrash对quake的综述(quake1到quake3总体结构变化不大),原文见http://www.gamedev.net/reference/list.asp?categoryid=40#133。
DOOM采用的是一个对等的网络体系结构(peer-to-peer),其中每个玩家机器运行一致的游戏引擎。这种方式对于只有两个玩家的游戏非常好,但是对于支持多人游戏,即通过Internet网的游戏则非常困难,所以quake采用了不同的网络体系结构。
quake是一个client-server模式的程序,如下图所示:
所有的游戏动作和模拟计算都在服务器上进行, 而所有的输入,输出则放在客户端处理。每个客户端为每一帧处理键盘,鼠标,游戏柄事件,并发送消息给服务器。服务器收到这些消息后,在一个指定的时间片内(帧频率以内)完成计算,并返回处理结果给客户端。客户端在下一帧数据接受之前渲染这些结果。这种方式在单人模式下也是一致的。即服务器和客户端在一个进程内,之间通过memory buffers通信。在多人模式下,客户端和服务器在不同的进程里,运行在不同的机器上。
client-server模式对于多人游戏是非常有利的,因为它提供了多玩家直接的简单同步机制。甚至在单玩家模式下, client-server也是有用的,它可以支持更好的调试。
1. 通信
对于client-server模式来说,最感兴趣的就是能够通过internet来玩游戏。 quake从一开始就被设计为支持多人游戏,包括通过internet。但是通过internet通信要比在通过LAN通信有更多的通信延迟,甚至数据报丢失。
在早期,quake采用了可靠的数据传输方式。在这种方式中,数据包发送出去后,必须得到对方确认的消息,如果没有收到确认消息,则整个游戏将停止直到重发成功。为了减少消息发送的数量,客户端应该只发送那些会导致游戏状态改变的消息。当然如果客户端发生改变游戏状态的消息,但是没有发送给服务器,则累计一定时间后,整个游戏世界的状态都将是不正确的。
可靠的数据传输方式的问题是如果数据包丢弃了,它需要花很长的时间去等待,然后重发。如果服务器到客户端 ping的时间为 200ms,则一个丢弃的数据包会导致几个200ms的时间延迟。
现在,quake仅仅使用可靠数据传输方式来传输一些关键信息,如分数,等级变化等。对于当前游戏状态,如玩家位置,物品位置等,它只在一定时间间隔发送,而不是这些数据一变化就广播这些数据给所有相关的客户端。并且这些信息的发送并不要求确认报文。即采用的是发出去后不管的原则。
2. 延迟
client-server模式潜在地带来了玩家动作大的延迟,如按下跳跃键,玩家就可以看到结果,比如他的视角变成了天空。这个动作先得发送给Server, 然后再返回。 在LAN中, 这个延迟非常接近no time,而在internet中,则会有几百ms的延迟。长延迟导致游戏非常的难玩。这个问题带了了一种可能性:即在客户端和服务端都运行一些游戏逻辑, 这样客户端可以提高响应速度。
快速的响应大部分时间都能工作的很好,但是对于客户端的模拟计算则存在一些严重的问题。首先它使得通信和游戏逻辑变得非常复杂,因为不是只有一个在服务器上的中心模拟器,而是有一打或更多的模拟器需要同步。 这样就是要求客户端模拟器在有冲突产生的时候,有一个机制来判断哪些决策是对的, 然后回滚其他的模拟器中的一些动作。非常地糟糕,这是一个不可避免的悖论,如一个玩家发射了一个火箭,然后看到它击中了对手并且导致对手死亡,但是过了一会他发现对手奇迹般地复活了。然后 quake也存在一些延迟,但是它不会产生上述情景。
3. 服务器
quake服务器维护着游戏的时间和状态,执行对象的移动,物理计算,和怪物AI。对于服务器方面来说,大部分感兴趣的是数据驱动模型。世界的描述是通过对象的位置和类型,墙的位置,还有存储在数据库中的相关信息来体现的。对象和怪物的行为都是通过编程实现的,通过内建的解释器, Quake-C控制。 quake服务器是由数据库驱动的,玩家不仅可以增加新的世界场景,还可以创建新的游戏元素,如能自动锁定目标的高级火箭筒, 自动导航的飞机等,这些都不需要写一行的C或汇编代码。这种灵活性不仅使得quake成为一个开发性的平台,而且可以让更多的人参与到游戏的开发中(不需要重新编译代码)。甚至我们可以在quake服务器运行中增加新的世界场景和quake c程序。
如果你想看看quake-c的代码是啥样的,可以到http://www.gamers.org/dEngine/quake/去看看。
4. 客户端
服务器和通信层是quake关键部分, 但是客户端才是玩家直接面对的, 客户端实现了一个3D引擎。客户端处理键盘,鼠标,游戏手柄,声音混合,和2D画图如菜单,工具栏,消息栏等,这些不是复杂的部分。真正的创造性的是3D引擎。 quake 3D引擎的挑战主要是两个方面:支持所有角度3D观察(而不是DOOM的2.5D),采用光照提高了图像逼真度,更精确的像素绘制。当然quake 3D在性能上也是独一无二的。
和服务器一样, 3D引擎也是数据驱动的, quake的数据主要分成两类:the world和the entities。每个场景都包含一些墙的几何体,门,和一些需要画到这些表面的纹理(bitmaps)。quake引擎内存存储了三角形mesh, 玩家的纹理, 怪物和其他可移动的对象,即entities。最早,我们计划是在一个渲染通道中完成这些显示工作。但是考虑到大量的墙,怪物和成百的多边形在一个通道中渲染效率比较低。现在已经采用了不同的方式来做并行渲染了。
世界被存储在一个BSP树的数据结构中。 BSP树非常的容易理解,这里就不列出细节了,后面有专门针对BSP的解释。对于quake来说, BSP树带来了两个好处:多变形裁剪变得很容易,分割的空间都是凸多边形的。
关于 PVS有一点就是:它的计算代价是非常大的。在四处理器的Alpha系统中做一次PVS计算需要15到20分钟。
quake光照技术带来了更加真实的光源和阴影。它为每个多边形通过执行一个单独的光照映射(基本上是一个纹理图)。