游戏数据的展示
游戏的业务逻辑到画面呈现的过程和 GUI 系统在结构上有相似之处,又有一些不同点。
在软件设计时,我们通常倾向与把数据和展示分离。在 GUI 系统中,数据通常被称为 Model ,展示被称为 View 。典型的 MVC (及其衍生品 MVP/MVVM 等)模式就是建立在此基础上。业务逻辑修改 Model ,经过展示模块,把 Model 映射到 View 中呈现给用户。
传统的面向对象设计中,很多人倾向于按对象划分,每个对象有数据部分和展示部分。对于游戏,我不喜欢这种设计。我更倾向于把数据和展示完全分离,再用 id 把同一个对象关联其数据模块和展示模块中分离的实体。这是因为,游戏尤其是虚拟世界广阔的游戏,屏幕展示的仅仅是虚拟世界很小的一部分。在同一时间,大部分的数据都不必展示。甚至数据也未必存在于本地的内存中。
我倾向于把游戏软件切分为 gameplay 和 view 两个完全分离的模块,各自有独立的数据结构和设计。gameplay 应该可以完全独立于 view 运转,而让图形引擎关心且只关心 view 部分。在制作游戏软件时,如何解决好 gameplay 的信息如何在 view 模块中展示出来,就成了必须考虑的设计点。
设计数据如何呈现,必须考虑图形引擎的对外接口是怎样的。图形引擎的使用接口通常有两种设计:立即模式(Immediate Mode)或驻留模式(Retained Mode)。所谓立即模式,就是每帧都是从空白开始,把需要渲染的对象依次提交,最终得到画面;而驻留模式则由引擎保留一个待渲染的对象集,用户可以增加或减少需要渲染的对象,并可以修改已经加入渲染集的对象的状态。在图形底层 API 中,一般都只提供立即模式,而图形引擎则会进一步封装。在 2D 引擎中,两种模式的设计都很多见;但 3D 引擎由于 3D 渲染对象的复杂度大大超过 2D 图片,几乎都是提供的驻留模式接口:即,创建一个可显示对象,并可以之后销毁。在对象存活时,可以随时修改它的各种状态,包括并不限于,材质、位置、播放动画、挂接新的对象等。
对于场景很小的游戏,view 就是完整 gameplay 的体现。每个 gameplay 中的实体都在 view 中有一个对应品。在 gameplay 中的实体状态发生改变时,我们可以对应的设置 view 中对象的状态,正确表现出来。虽然对象并不总在镜头内,但渲染引擎通常会根据摄像机的位置和角度裁剪掉不必显示的对象。
如果场景很大,或有多个场景同时运转(只显示其中一个),一股脑的把 gameplay 中所有对象交给 view 模块就不明智了。我建议再设计一个叫做 viewport 的模块,它用来表达 gameplay 中的一个需要展示出来的区域。viewport 应该是一个保留有自身状态的对象,在每个渲染帧开始时,viewport 去 gameplay 中查询该区域中应被显示出来的对象,每个对象有唯一的 id 。viewport 应和自身保留的上一次查询的对象的状态向比较,得到这次查询和上次查询的差异。例如,这次查询如果和上次查询的位置相同,当前的位置就不用表达在查询结果中。这样,能更好的配合驻留模式的引擎接口。
而 view 模块中,也不是所有的可显示对象都会在 gameplay 中有对应品。我们应该标记出那些会受 gameplay 影响的对象,方便筛选出来。例如,有些用于和用户交互的可显示对象就可能在 gameplay 中并不存在。还有一些固定的作为背景的场景、渲染气氛用的特性等等。
在每一渲染帧,遍历出所有 gameplay 对应的渲染实体,再和前面的 viewport 查询结果集相比较,就可以知道这些对象需要做那些状态修改。以及,是否有需要增加的新对象,或是某些对象应该暂时隐藏掉。这样,viewport 也代替了图形引擎做了一次更高效的裁剪。在同一时间,viewport 往往不只一个。比如,游戏的主画面和小地图就是两个独立的 viewport 。它们所关心的范围不同,关心的信息也不同。小地图关心的范围更大,但只关心需展示的实体很少的信息。
对于 gameplay 信息不完全在本地内存中的场合,例如网络游戏,这个模式依然有效。在 MMORPG 的客户端中,gameplay 的对应模块其实是完整 gameplay 数据(存在于服务器上)的一个局部映射。通过网络包逐步的把部分 gameplay 数据同步下来。我们依然可以利用这样的模式把映射下来的部分数据再筛选展示。这种场合,viewport 模块更像是一个高效的裁剪器,裁剪掉不需要用于展示的数据。和图形引擎的裁剪模块不同,它拥有更多的和图形无关的信息。例如,你在封闭的房间内,屋外的对象就不需要在渲染引擎中更新和展示。你在二楼时,用于显示的 viewport 或许也不需要关心三楼的玩家;但同时可以有另一个用于发出环境声的 viewport 可以帮你播放楼上玩家的脚步声。显然,只是基于空间位置信息的图形渲染模块很难做得更好。
一个设计良好的 viewport 模块(从 gameplay 中提取出当前需要传入 view 的信息)可以帮助游戏数据和展示有效的分离。我建议在游戏软件设计的早期就应该重点考虑它的具体设计。
Comments
Posted by: rawa459 | (25) June 13, 2022 08:20 AM
Posted by: rawa459 | (24) June 13, 2022 08:17 AM
Posted by: rawa459 | (23) May 15, 2022 04:35 PM
Posted by: rawa459 | (22) May 15, 2022 04:21 PM
Posted by: rawa459 | (21) May 15, 2022 04:17 PM
Posted by: rawa459 | (20) May 13, 2022 08:18 PM
Posted by: rawa459 | (19) May 13, 2022 07:20 PM
Posted by: rawa459 | (18) May 12, 2022 10:12 PM
Posted by: rawa459 | (17) May 12, 2022 12:48 AM
Posted by: rawa459 | (16) May 12, 2022 12:11 AM
Posted by: rawa459 | (15) May 12, 2022 12:06 AM
Posted by: rawa459 | (14) May 11, 2022 11:59 PM
Posted by: nick | (13) May 11, 2022 02:49 PM
Posted by: 嘎嘎嘎 | (12) May 10, 2022 02:05 PM
Posted by: rawa459 | (11) May 10, 2022 03:02 AM
Posted by: rawa459 | (10) May 10, 2022 01:59 AM
Posted by: rawa459 | (9) May 9, 2022 11:48 PM
Posted by: rawa459 | (8) May 9, 2022 08:14 PM
Posted by: 王 | (7) May 9, 2022 04:25 PM
Posted by: anti-rawa459 | (6) May 9, 2022 10:21 AM
Posted by: cupen | (5) May 9, 2022 03:22 AM
Posted by: rawa459 | (4) May 8, 2022 08:00 AM
Posted by: rawa459 | (3) May 8, 2022 05:23 AM
Posted by: rawa459 | (2) May 8, 2022 05:06 AM
Posted by: smallwhite | (1) May 7, 2022 07:29 PM