跳到主要内容
游戏模型

本页尚未翻译,当前显示英文版本。

继承与组合

模块相互构建,但这一切都不是子类化。 没有可供派生的基础模块,也没有可供扩展的层级结构:模块构成的是一张图。本页说明“继承”在这里指什么,以及真正完成这些工作的六种机制。

“继承”在这里指什么

one lista decorator, narrowing what is borrowedthe room owns who is inthe chat owns who hears under entry rulesunder delivery rules
同一份名单经由装饰器被借用两次:房间决定谁在里面,聊天决定谁能听到

这个词涵盖四种不同的机制:

  • 实体的 RPC 是实体的一部分。 它们不存在于别处:既不在父级上,也不在共享注册表里。参见 Entity。
  • 预设(preset)是一个具名的捆绑,而不是基类。属性、技能、投射物、掉落生成器和世界物件都是 entity 的预设:由实体(entity)视图应用的一组 aspect。正因如此,它们集中在 Entity Presets 这一页,而不是作为五个模块。应用预设会增加 aspect,但不会把你的类型置于任何东西之下。
  • 覆盖平台的某个步骤,就是在你的替代实现上加一个特性。 你不需要继承我们的实现,只需声明你自己的;具体用哪个版本按条件选择,平台默认实现作为兜底。参见 Extensibility。
  • 一个模块通过装饰器借用另一个模块,装饰器会收窄或扩充被借用的接口,其背后的实现可以替换。完整示例是房间内的聊天,见 Groups。

模块之间没有类层级,因为树只允许分支,而真实的功能会跨越分支。匹配(matchmaking)在房间(room)中预留席位;掉落通过地图放置物品;排行榜由房间关闭时的 hook 提供数据。

六种机制

每种机制都用一个特性声明在它所组合的对象旁边——这与 SDK 中其他一切遵循的是同一条声明式规则。

挂载点,类似文件系统。 模块可以挂载在根上(把多个接口组合成一个统一的接口),也可以挂载到某个命名空间中。如果第二个模块要占用已被占用的挂载点,它会在挂载时被拒绝,绝不会拖到第一次调用时。机制详见深入底层。

词法可见性。 名称的可见性跟随嵌套结构:全局声明在模块内部可见,局部声明绝不会向上泄漏。模块发出什么是另一个问题,由它自己的契约声明——模块只知道它自己声明的事件,或者注册到它上面的事件。

封装即契约。 模块永远不知道谁在调用它,也不知道为什么。它暴露什么、发出什么,就是它全部的公开契约;除了调用方的授权,调用方的任何信息都不会改变它的行为。

通过装饰器和控制反转复用。 一个模块通过装饰器引用另一个模块,而不是直接深入其内部,接口背后的实现可以替换。正是这种机制让你能用自己的模块替换我们的某个模块,而依赖它的模块毫无察觉。

声明扩展 API。 在群组上声明一个事件,group.Send.ChatMessage(…) 就会连同它的契约一起出现;声明数据 members,就会出现一个带类型的 getter。声明是代码生成的输入——这也是为什么需要做版本管理的是声明,而不是生成的代码。

一个模块,三条寻址轴。 所有实例、单个实例,以及单个实例的管理员,是三套不同的 API,而不是一套 API 加一个标志。完整说明见 Groups。

隐式参数

在实体内部,你永远不需要传入实体本身。接收者、调用方和环境上下文会自动绑定,因为这三者已经由调用发生的位置和发起者决定。机制详见 RPC。