固体:容器
虚拟世界里的物体可以处在各种层级之中。主要的一种是类层级(X 是 Y 的一种),但其他可能还包括组合体层级(X 是 Y 的组成部分)、空间层级(X 在 Y 上面)和容器层级(X 被 Y 容纳)。后两者是相关的,我们马上就会看到。
容器层级很有用,因为它们给了玩家一种物理手段,让他们能在虚拟世界里成批搬运物体。它们并不是必需的:早期计算机操作系统没有子目录或文件夹这类东西,同样,一些早期的虚拟世界(最著名的是《Shades》)也没有容器。不过,它们非常有用。在采用节点式实现的虚拟世界里,这一步其实早就做到了:房间可以容纳角色,角色可以携带(也就是“容纳”)物体,因此,再让物体也能容纳其他物体,并不是多么了不起的想象飞跃。
使用坐标系统处理位置的虚拟世界,通常必须改用节点式系统来处理容装关系。虽然也可以扩展坐标式的做法,让物体能被装在背包、口袋、腰带之类的地方随身携带,但在实践中这对玩家太不方便了。对程序员来说同样不是乐事:他们得把任意物体锁死在角色的坐标系上,好让角色走到哪里、物体就跟到哪里。因此,大多数图形化虚拟世界会为容装关系单独维护一份物品栏,只把坐标系统用在顶层(顶层更容易用图形方式显示)。
图形世界在呈现物体之间的空间关系上没有困难。你能看见瓶子在桌上、椅子在桌边、靴子在桌下,等等。这要归功于坐标系统强加的那种刚性的空间结构。节点式系统没有这种底层结构:如果两个物体都在一个房间里,这丝毫说明不了两者之间的空间关系。就连两个相邻房间之间的连接关系也不是隐含的,必须明确写出来。有些节点式虚拟世界——例如 Skotos 开发的那些——确实在物体之间有一套关系网络。不过,没有绝对坐标就很难做推断:如果 X 挨着 Y、Y 挨着 Z,那么 X 挨着 Z 吗?当然,在对容器的内容物建模时,坐标系统也有同样的问题。幸运的是,大多数玩家不关心“X 在包里”以外的事——他们并不真的在意到底在包里的哪个位置,只要它不会到处移动、想拿的时候拿得到就行。
容器能装的东西通常是有限的。它们只能容纳一定体积(或数量)的物体。有时只有尺寸小于某个限度的物体才装得进去(你没法把一把汤匙塞进啤酒瓶里),有时只有尺寸大于某个限度的物体才留得住(你没法把铅笔存在网兜里)。容器可以是刚性的或柔软的、不透明的或透明的、能合上的或一直敞开的——这些属性与许多其他物体的属性属于同类。不过,容器有一些特殊问题,是其他物体不会遇到的。
大多数容器是内容器(endocontainer),也就是说,它们把东西装在自己里面。箱子、袋子、碗、篮子——它们都把所装的东西包围在内。不过,还有一类较小的外容器(exocontainer):它们把别的东西托在自己外面。谱架、帽架和圣诞树就是外容器的例子。外容器会“穿戴”它们托着的东西。
有些虚拟世界把外容器与内容器当成完全不同的两回事,理由是:靠包围来装载,与靠钩挂、系绑或别的方式来装载,有本质的区别。当然,这两个概念确实互不相交,但它们的共同点足够多,一般来说把它们当成同一种东西更好。“take music from stand” 和 “take stand from case” 本质上是同一种动作,可以用几乎相同的功能来实现。
关键的差别在两个方面。
第一,不透明内容器的内容可以瞒过窥探的眼睛,而外容器托着什么,你总是看得见。这对文字世界不算太糟,但图形世界必须能构造出动画图像,来配合玩家们几乎任意尝试的各种穿戴组合,这可能非常棘手 1。
第二,对于内容器,容器会保护自己的内容物。如果你击打一个内容器,里面所装的东西受到的伤害可能会减少。对外容器则相反:容器自身受到的伤害可能会减少,因为伤害被外容器托着的东西吸收了。铠甲就是靠这个原理起作用的。
这就引出了一个非常特殊的内容器:玩家自己的角色。尤其是在奇幻世界里,角色携带或穿戴的东西对位置很敏感。你不会把头盔戴在胳膊上,也不会把靴子穿在胸口。图形虚拟世界和文字虚拟世界都有这个问题。
图形世界采用了单机电脑角色扮演游戏的一套标准:显示角色的图像,通常还标出各个“装备槽”,玩家像给纸娃娃穿衣一样给它打点装扮 2。这虽说不上多有沉浸感,但与物品栏总体的运作方式是一致的。文字世界要操心的事更多,因为玩家想指称身体部位。“wear socks”“wear cravat” 之类还行,但玩家想要的是 “wear ring on left index finger”。这就意味着,身体应当被当作一种组合体来处理。玩家通常并不想有条不紊地把身体拆成部件,而且除了在恐怖类型里,人们也不会想要用部件重新拼出新身体来。尽管如此,如果玩家想要指称一个组合体的各个部分,这个组合体就必须被构建出来,或者被伪造出来;无论哪种做法,实现起来都非常烦人,而且等于在向玩家暗示:组合体这种玩法是行得通的。先是身体,接着是成套的铠甲,然后是马匹,再然后是马车——不知不觉间,你已经走得比原计划远得多,响应时间也开始吃紧了。如果你确实决定要实现身体,那就当场定下来它是不是一次性的,并坚持到底。你不会被说动的,你的意志坚如钢铁……
那么书呢?书里有页,但那些页按固定顺序排列,所以它们基本上是组合体。如果有人想 “read page 24”,你会允许吗?还是说,你会让书短到永远只有一页?
把容器变成组合体,可能非常有诱惑力。小心。
把容器变成生成器同样很有诱惑力,但危险小得多。一袋坚果无论你拿出多少,里面总还有坚果,这样挺好——只要你不让任何人把它翻个底朝天。商店里的一个衣帽架可以挂有固定数量的外套,每一件都用惰性求值的方式创建(也就是说,只有有人试图取一件外套时,才会创建出一件来)。
关于容器,最后要提的是负重(encumbrance)这个概念。这是桌面角色扮演游戏里的一个老观念(有人会说它已经过时)。一般来说,一个容器 3 能容纳的物体重量取决于它的力量;它能容纳的件数取决于容器本身的尺寸和相关物体的尺寸(不过对外容器来说,这还涉及某种可用接触点的度量——也就是容器的灵巧度)。然而,物体往往很别扭,它们拖慢你的程度比重量或形状相近的另一些物体大得多。撑开的伞比收起的伞更难拿;氦气球比足球更碍事(这两样东西放了气都更好拿)。
负重这一构想,是要量化某个东西凭自身的属性,在多大程度上影响其容器的各项属性。它是一个不完美的抽象,但有时很有用。即便一个虚拟世界把物体的精确尺寸都存了下来,有些计算代价高昂的运算仍然只能近似处理——比如背包问题(把一个装满的背包倒空,再把原来装的东西全装回去,这可不简单——就像玩三维拼图)。
即便在二维里也很棘手。KiSS 娃娃(Kisekae Set System——电脑上的数字换装娃娃)经常出现问题,比如蓬松的衬衫会从套在外面的夹克上穿出来。 ↩︎
这又是 KiSS 娃娃。想了解更多关于 KiSS 的内容,可以看看 http://otakuworld.com/kiss/。警告:小孩子特别喜欢这东西。 ↩︎
因为角色也是外容器,所以这里也包括角色。这就是面向对象编程在发挥作用。 ↩︎