物体
虚拟世界里处理物体的准则是:把一切都整合起来。
实现一个虚拟世界时,构成它的那些实体就是它的对象。这些实体之间的关系就是它们的属性。对象与属性合在一起,构成了虚拟世界中可触摸的部分,而物理赋予这部分以生命。事情并不非得如此——理论上你可以设计出这样一种系统:对象只是基本方程之间相互作用的产物,就像数字那样。你要是打算那么做,祝你好运。
设计者必须认识到:虽然他们的虚拟世界里许多对象与另一些对象在特性上千差万别,但从根本上说它们是同一回事。它们可以被归入一些在概念上有用的分组——玩家角色、活动生物、容器、房间、可携带物体、固定景物,等等——但把这些分组当成互不相干、彼此隔绝的集合,是一个大错。如果你把它们看作一个通用抽象对象的特化,而不是各自抽象层级的根,你会得到好得多的一致性(也对自己这个世界理解得好得多)。
举例来说,假设你决定让自己的玩家角色拥有一个 “health”(生命值)属性。那么用这同一个属性来记录活动生物的生命值,就是很自然的一步。你也许还想给普通物体一个 “wear”(磨损)评分,用来追踪它们离散架还有多远。那么你就应当想到,“wear” 衡量的东西和 “health” 是同一类,因此你该用 “health” 而不是 “wear” 1。
这也许显得相当显而易见,但很多时候它偏偏不是 2。早期,MUD 设计者同时也都是程序员。直到他们接触了面向对象编程语言的思想之后,其中一些人才愿意承认:嘿,也许房间和玩家角色说到底就只是对象而已?这有点讽刺,因为事实证明,虚拟世界的对象与 C++ 这类面向对象编程语言里的对象对应得并不好。要解释为什么,我得先岔开一段(不过我会尽量说得短些)。
在面向对象编程语言里,有一个对象类的层级。单个对象是某个类的实例。因此,你可以说达布隆是一种硬币,硬币属于财宝这一类,财宝是一种可携带物体,可携带物体是一种(根)对象。虽然我在这里用了 “object” 这个词,但在编程的意义上,这些应当叫类。Doubloon33 可能是 “doubloon” 这个类的一个实例,那样它才是一个对象。面向对象编程的威力在于它能消除重复:我不必去定义 “get doubloon1”、“get doubloon2” 等等,只需要定义 “get doubloon”。
如果对象可以是多个类的实例,或者类可以是多个超类的子类,你拥有的就是多重继承系统。否则就是单继承系统。对虚拟世界设计来说,多重继承系统极为便利。如果一个房间里有达布隆、一便士和一只明代花瓶,那么当我发出 “get coin” 命令时,我想要的是把达布隆和便士捡起来;如果我试着发 “get antique”,我想要的是达布隆和那只花瓶。达布隆既是硬币又是古董,但并不是所有硬币都是古董,也不是所有古董都是硬币。这种情况很常见:并非所有哺乳动物都是食肉动物,也并非所有食肉动物都是哺乳动物;并非所有流行歌手都是男性,也并非所有男性都是流行歌手。方块皇后主要是一张方块,还是主要是一位皇后?
遗憾的是,程序员很可能想用 C++ 或它的某个衍生品(例如 Java),而这些语言只支持单继承{勘误:C++ 其实确实有某种形式的多重继承,用的(说来有点讽刺)是 virtual 关键字。}。他们看到虚拟世界里有 “对象”,就把它们对应到自己那套面向对象系统里的 “对象” 上,然后在编码进行到 95% 的时候撞上麻烦。在单继承系统里伪造多重继承是可能的,正如在扁平系统里伪造单继承也是可能的。这要花很多工夫,里面还有大量重复;不过仍然办得到。
办不到的是命令的继承。面向对象编程会把一段段代码(称为方法)关联到对象类上。这些方法本身不是对象;它们不存在于继承树里。换句话说,我不能说 “punch” 方法是 “hit” 方法的子类。如果我想要 30 种不同的打东西方式,就必须把它们一一实现。如果我想让效果随被打对象的软硬而不同,每一种都得把这套逻辑重写一遍。对大型或复杂的虚拟世界来说,这很快就会变得令人抓狂地枯燥,无论实现还是维护都是如此。面向对象编程的目的是消除无谓的重复,不是制造重复。
出现这种局面,是因为虚拟世界的 “对象” 对编程语言里的 “对象” 来说是个糟糕的选择。应当把命令当作对象才对。如果把 “hit object” 和 “hit soft_object” 定义成(面向对象的)类,那么像 “slap cushion” 这样的命令就是某个命令类(在这里就是后一个)的实例,据此就能找到并运行对应的代码。这本质上是个编程问题,而不是设计问题,但由于虚拟世界的历史包袱,它已经进入了设计范式。设计者往往按对象和方法来思考,而他们本该思考的是多重继承的层级和命令。
好,题外话到此为止。
所以,虚拟世界里的对象类通常会被组织成某种层级。这样可以共享属性,共享属性会促成整合,整合又带来一致性。那种因为 “hit” 这个名字已经被 “hit creature” 占用、只能改用 “ring bell” 的日子已经过去了 3。
如果去问,大多数设计者大概都会赞成整合是好事这个想法。可惜的是,他们往往并不怎么考虑这件事。比如说,法术与技能有很多共同之处——它们都是把某项专门任务做到某一水准的能力,而这种能力是必须学来的。但很少有虚拟世界认为这两者有任何关联。同样,器物也能以效果或附魔的形式,拥有玩家角色靠技能或法术拥有的那种东西。如果我能学会施放闪电箭法术,那为什么不能教会一根魔法法杖也这么做?这种思路已经在单人角色扮演游戏里用过,例如拉瑞安工作室(Larian Studios)的《神界》(Divine Divinity),但在虚拟世界里并不常见。
关于物体的这一大段讨论,虽然大多讲的都是文字世界,但它同样适用于图形世界。
图形世界在功能上一般远不如文字世界丰富,但差距正在缩小;迟早有一天,用单继承来组织虚拟对象类会成为一个问题。没有多重继承、也没有对动作进行量化的能力,你能走完 95% 的路程,但到了那最后的 5%,这两样东西就会变得很重要。