游戏架构杂想

游戏是一个模拟虚拟世界的实时程序,它接受用户的输入,然后输出图像和声音:

输入

游戏程序的输入是一个有限的消息集合。实际运行中,每条消息都带有timestamp用以记录发送时间。两条消息相等不单内容相等,timestamp也必须相等。因此给定一组初始状态,同样的消息序列必定可以得到唯一确定的运算结果。

不同的游戏所接受的消息集合各不相同。例如:最基本的飞行射击游戏(《1945》),它接受的消息集合为 { 前,后,左,右,射击 };而只有一个按钮的UI界面,它接受的消息集合为{ 光标位置(x,y),点击 }。通常需要将各种输入设备的硬件信号映射到游戏所支持的输入集合:

最简单的做法就是直接把所有硬件消息作为游戏的输入集合。例如对于PC来说,就是鼠标移动+所有的鼠标/键盘按键;对于Console来说就是摇杆+按键。

输出

游戏程序的输出为图像和声音,通过专门的API向显示/声音设备发送指令,将逻辑世界以人类的认知方式描绘出来:

Game World

游戏逻辑的核心过程就是以一定的频率,对虚拟世界中的每一件物体执行运算,从而模拟世界的运转。每个运算周期就是一个逻辑帧,其中每个虚拟物体都会依次获得CPU时间来执行如下3个步骤的运算:

  1. 根据外部输入的消息改变自身的状态
  2. 根据虚拟世界的状况决策将要采取的行为(AI系统)
  3. 在虚拟世界的物理框架内执行特定的行为(物理系统)

所有的虚拟物体所要执行的运算都不外乎以上3个部分。RPG中的怪物就是包含所有3个步骤的最好的例子。有些物体只需要其中之一二,例如:由玩家操纵的勇者不需要Think,而像桌子这样的静态物体最多只会有Act。

模块拆分和并行化

从程序角度对主要任务进行描述之后,模块的拆分就变得显而易见了。

首先基本框架可以分为Input,GameWorld,Render和Sound四个部分。这四个部分的功能相对独立,并且以类似的模式运作。Input以特定的频率读取硬件输入并映射到游戏输入集合;GameWorld也是以特定的频率对整个世界进行运算;Render同样按照自己的频率从GameWorld采样瞬时数据并转化为多边形/像素输出到图形硬件(类似于照相机);Sound同理,只不过输出的是声音而已。理想情况下,只要通信条件满足,这四个部分可以任意部署到不同的Client上运行(这里,Client可以是一个线程,一个进程,甚至一台远端机器),而相互之间除了通信协议之外不需要知道任何东西。

其次对于游戏中的虚拟物体,采用Core+Proxy的方式来表示。Core是在GameWorld中足以保证其逻辑的完整表述,而Proxy则是在其他模块中维护专门数据的分身。例如在渲染模块中会存在一个对应的Render Proxy,其持有根据Core生成的多边形和纹理数据。Core和Proxy之间存在通信管道,更新后的数据会由Core广播给各个Proxy(或者由各个Proxy在需要的时候向Core查询?)。嗯,这其实就是经典的Model-View结构

最后值得思考的并行点在Think和Act这两个阶段。如果两个物体的Think(或Act)之间没有关联,是完全可以分配到不同的workflow中并行执行。不同类型物体之间的依赖关系可以通过静态分析得到,这可以作为执行过程中workflow分配的依据。

你可能感兴趣的:(游戏架构杂想)