云脚本程序员指南

本指南介绍如何使用网格云脚本 API 和开发人员工具来生成环境(这些环境从 Unity 中的项目开始,然后上传到网格集合)。 建议先阅读 Azure 中的“设置云脚本”基础结构,以熟悉网格云脚本的概念和基本体系结构。

本部分介绍网格云脚本 API 的功能和接口,该 API 用于编写在环境中驱动行为的脚本。

基本 DOM 结构

DOM 结构镜像 Unity 场景的结构。 应用程序的“场景”成员对应于网格云脚本组件附加到的游戏对象。 以下网格云脚本 API 类使用编辑器中创建的 Unity 对象映射一对一:

  • GameObject (& 转换组件) -> TransformNode
  • 浅色组件 -> PointLightNode、SpotLightNode、DirectionalLightNode
  • 动画器组件 -> AnimationNode (和派生类,请参阅下文)
  • Box Collider 组件 -> BoxGeometryNode
  • Sphere 对撞机组件 -> SphereGeometryNode
  • Capsule 对撞机组件 -> CapsuleGeometryNode
  • 网格对撞机组件 -> MeshGeometryNode
  • 文本网格 Pro 组件 -> TextNode
  • 刚性组件 -> RigidBodyNode

例如,如果创建具有光组件(设置为点光)和附加球体碰撞体的游戏对象的场景,则场景将包含包含两个子级的 TransformNode:PointLightNode 和 SphereGeometryNode。

此外,某些网格云脚本 API 对象没有相应的内置 Unity 组件。 这些是可以在 Unity 中创建的其他组件,这些组件是网格工具包包的一部分。

  • 网格云脚本组件(如上所述)
  • WebSlate 组件

将 Unity DOM 映射到网格 DOM

可以使用网格云脚本 API 不知道的组件创建场景。 这些操作根本不存在于场景的网格云脚本 DOM 中。 但是,GameObjects 的完整场景结构将在 DOM API 中镜像为 TransformNodes。

Unity 具有 GameObject/组件 API 形状;但是,网格云脚本 DOM 具有单个树结构。 网格云脚本 API 中的 TransformNode 具有子级,可以是其他 TransformNode,也可能是映射到组件的其他节点。 我们可以将此视为关联游戏对象的组件及其转换组件的子项的合并列表。

Rect 转换

如果添加使用 RectTransform 的组件(例如文本网格 Pro 组件),则游戏对象不会在网格云脚本场景图中显示为节点。 仍然可以移动、启用和禁用此类组件,但为此,需要使用常规转换组件在另一个游戏对象中使用 RectTransform 包装游戏对象。

属性更改事件

可以通过调用 AddPropertyChangedEventHandler 层次结构中的任何节点来订阅属性更改事件。 必须以字符串的形式传入属性的名称。

还可以通过订阅 DomObjectPropertyChanged 事件来订阅所有事件。 当 DOM 中的任何属性发生更改时,将调用此属性。

对象生命周期

创建节点时,节点是未父级节点。 这意味着,在将子级显式添加到场景或其后代之一之前,它们不会在场景中可见。 同样,将节点的父级设置为 null 会将其及其后代从场景中删除。

有时,你希望暂时禁用节点,但不保留它在场景中的位置的记录。 因此,每个节点都有一个“活动”标志。 设置为 false 时,它将禁用节点及其后代。

可以在 Unity 中创建属于场景但已禁用的游戏对象和组件。 这些节点将显示为网格云脚本场景层次结构中的节点,但其活动标志设置为 false。 将活动标志设置为 true 将在 Unity 场景中启用它们。

克隆和重新父级

可以在网格云脚本 API 中克隆和重新分析节点;相应的 Unity 场景将相应地更新。 如果克隆节点,它将克隆该节点及其所有子级(包括可能位于相应 Unity 对象中的子级,但网格云脚本不可见)。

可以克隆或重新父级节点,这些节点对应于 Unity 组件。 这是通过基于网格云脚本节点表示形式重新创建这些 Unity 组件来实现的。 只有可以通过网格云脚本 API 创建的节点可以克隆或重新父化。 如果在 Unity 中创建组件并设置未反映在相应网格云脚本节点中的字段,则克隆 Node 本身时,这些字段将重置为其默认值。 因此,我们建议克隆或重新父级转换节点,以便在其中操作在 Unity 中创建的对象。 这些设置将始终正确保留所有原始 Unity 设置。

用户

API 中有提供用户属性的各种位置。 该 User.Identifier 属性是一个持久标识符字符串,即使用户离开并重新加入也是如此。 用户显示名称也可通过 User.DisplayName。 用户从中连接的事件 ID 可通过 User.ConnectedEventId访问。

在开发过程中,可以在“开发人员设置”中的网格云脚本组件编辑器中模拟用户显示名称、标识符和事件 ID,如下所示。

模拟用户属性

头像

虚拟形象是场景中用户的表示形式。 它们可用于将用户传送到给定位置、在场景之间旅行以及检测与触发器卷的冲突。

信息对话框

可以在网格云脚本中使用自定义消息在Microsoft网格应用程序中弹出屏幕空间对话框。 SceneNode 包含此函数。 ShowMessageToParticipants(string message, IReadOnlyCollection<Participant> participants) 格式文本标记 可用于控制文本属性(颜色、粗体等)。

输入对话框

网格云脚本可以使用自定义消息从网格事件中的与会者请求文本输入。 CloudApplication 提供方法 Task<string> ShowInputDialogToParticipantAsync(string message, Participant participant, CancellationToken token)格式文本标记 可用于控制文本属性(例如颜色或粗体)。

CloudApplication

ICloudApplication 接口是开发网格应用的起点。 它在“App.cs”中作为_app变量提供。 除了场景, ICloudApplication 还为所有可用类型创建函数。 它还具有许多其他方法,但它们供内部使用。

InteractableNode

MeshInteractableSetup 是网格工具包包的一部分的自定义 Unity 组件。 将它附加到 Unity 中的游戏对象时,当用户单击该游戏对象或其子对象中的任何活动碰撞项时,它都会引发单击事件。

下面显示了一个简单的示例,其中 MeshInteractableSetup 组件将添加到与框对撞机相同的游戏对象:

简单输入示例

WebSlateNode

WebSlate 是网格工具包包的一部分的自定义 Unity 组件。 若要将 WebSlate prefab 添加到场景,请从菜单栏中选择 GameObject>Mesh Toolkit>WebSlate。 分配给 WebSlate 实例的 URL 属性的网站呈现在此 prefab 的象限上。

下面显示了一个示例,其中 WebSlate prefab 已添加到场景中并分配了 URL:

        var webSlateNode = Root.FindFirstChild<WebSlateNode>(true);
        webSlateNode.Url = new System.Uri("https://en.wikipedia.org/wiki/Color");

WebSlate 示例

侦听单击

下面是一个简单的网格云脚本脚本,每次单击多维数据集时都会旋转多维数据集。 将内部App.cs存根StartAsync方法替换为此代码。

        private float _angle = 0;

        public Task StartAsync(CancellationToken token)
        {
            // First we find the TransformNode that corresponds to our Cube gameobject
            var transform = _app.Scene.FindFirstChild<TransformNode>();

            // Then we find the InteractableNode child of that TransformNode
            var sensor = transform.FindFirstChild<InteractableNode>();

            // Handle a button click
            sensor.Selected += (_, _) =>
            {
                // Update the angle on each click
                _angle += MathF.PI / 8;
                transform.Rotation = new Rotation { X = 1, Y = 0, Z = 0, Angle = _angle };
            };

            return Task.CompletedTask;
        }

点击信息

可以通过查看属性更改事件参数来找出哪个用户单击了碰撞体。 还可以从事件参数中读取单击的联系人正常和位置。 这些坐标将相对于 InteractableNode 的本地坐标空间。

动画 师

可以创建 Unity 动画器并将其添加到场景中,并通过网格云脚本控制它。 Mesh 工具包插件将浏览 Unity 项目中的资产,对于找到的每个动画器,它将在网格云脚本项目中的“AnimationScripts”文件夹中生成类。 此类派生自 AnimationNode,可用于从网格云脚本控制动画器。 将 Animator 作为组件添加到 Unity 中的游戏对象时,你将找到生成的类的相应实例作为相应 TransformNode 的子级。 使用此类的 API,可以控制动画器。

网格云脚本编程模型是服务器权威的,我们仅支持一小部分动画器功能。 这是因为我们在服务器上为动画器建模,并期望所有客户端将准确同步到服务器模型。 因此,目前仅支持以下 API:

  • 状态设置(对于每个层,类中有一个相应的属性,可根据动画器中的可用状态设置为枚举)。 状态会立即设置,而不是通过转换设置。
  • 浮点变量设置:仅公开浮点变量,并且仅用于绑定到动画器中的“动作时间”。
  • 层速度设置

在某个状态中,可以创建动画剪辑,但对可以在 Unity 场景中设置的值没有限制。 还支持循环动画剪辑。 动画器以下功能不支持通过 AnimationNodes:

  • 转换:如果将转换添加到动画器,则无法通过网格云脚本 API 触发这些转换(服务器不建模转换)。
  • 变量(除了浮点数以外,用于驱动运动时间)。 不支持用于驱动转换逻辑或速度乘数的变量。
  • 镜像状态、周期偏移量和脚 IK。

后期联接和动画器

当客户端加入网格事件时,它们会同步到所有正在运行的动画节点的当前状态和本地时间。 如果有长时间运行的动画处于某个状态,则播放时间将设置为延迟加入时动画的正确当前时间。 但是,如果状态触发事件,这些事件将不会在已加入的后期客户端中触发。 某些其他方案可能无法按预期工作;例如,如果通过在状态开始时启用 AudioSource 来触发声音,则 AudioSource 仍将在后期加入客户端中启用,但将在音频剪辑的开头开始播放。

动画处理器初始状态

建议创建具有不执行任何操作的默认状态的动画器。 当场景开始在 Unity 中播放时,它将激活所有动画师并开始播放其默认动画。 这种情况可能发生在网格云脚本服务连接发生之前;因此,无法同步这些状态和行为可能不如预期。

动画器重新父母和克隆

无法通过网格云脚本 API 创建 AnimationNodes。 创建 AnimationNode 的唯一方法是导出包含动画器组件的 Unity 场景。 如果尝试克隆或重新父级 AnimationNode,将收到错误,因为无法支持此操作。 仍可以克隆或重新父 级 AnimationNode 的父 级,因为这对应于可以克隆和父级的包含 Unity Game 对象。

有关生成的代码的说明

生成的代码将从动画器、层、状态和变量的名称中删除空格;例如,变量名称“my var”在代码中变为“myVar”。 因此,可以创建不会生成有效代码的动画器。 例如,如果你有两个名为“my var”和“myVar”的变量,则生成过程中会出现错误,并显示一条消息,要求你重命名变量(s)。

LightNode

PointLightNode、DirectionalLightNode 和 SpotLightNode 都映射到 Unity Light 组件(其类型设置为相应的值)。 可以通过 LightNode API 设置这些灯的基本参数。 还可以通过 API 手动创建光。 通过 API 创建轻型节点将使无法通过网格云脚本 API 设置的参数保留为其默认值。

GeometryNode

BoxGeometryNode、SphereGeometryNode、CapsuleGeometryNode 和 MeshGeometryNode 分别映射到 Unity 的 Box Collider 组件、Sphere 对撞机组件、胶囊对撞机组件和网格对撞机组件。 还可以通过网格云脚本 API 创建它们。 如果 MeshInteractableSetup 附加到其游戏对象或其父级之一,则启用和禁用 Geometry Nodes 将添加和删除它们。

通过 API 创建几何图形节点会将无法通过网格 API 设置的参数保留为其默认值(例如,物理材料将设置为 none,isTrigger 将设置为 false)。

RigidBodyNode

向对象添加一个刚性组件将使其运动置于网格物理的控制之下。 如果不添加任何代码,一个 Rigidbody 对象将通过重力向下拉取,并将对与其他对象的碰撞做出反应。

注意GeometryNode.Friction 将返回 staticFriction。 但是,如果在网格云脚本端设置,它将同时更新 staticFriction 客户端和 dynamicFriction 客户端。

触发卷

当几何图形节点的属性设置为 true 时 IsTrigger ,可以充当触发器卷。 此标志对应于 IsTrigger Unity 中对撞机的属性,并且在运行时无法更改。 当几何图形是触发器时,它将生成 Entered 任何 Exited 启动/停止与其重叠的虚拟形象。

注意:需要将 Unity 对象添加到 TriggerVolume 层中,以允许远程传送梁忽略它,因为层中的 Default 碰撞体会阻止传送梁。

TextNode

TextNode 映射到 Unity 的 TextMeshPro 组件。 如果将 TextMeshPro 组件添加到场景中,网格云脚本场景层次结构中将有相应的 TextNode。 这样,你可以在运行时设置组件的文本。 还可以通过 TextNode API(粗体、斜体、下划线、删除线和颜色)更改基本文本属性。 目前无法通过 API 创建 TextNode;必须通过将它们添加到 Unity 中的场景来创建它们。 此外,不能直接克隆 TextNode—必须改为克隆 TextNode 的父 TranformNode。

网格

网格当前是网格云脚本 API 的“隐藏”组件。 它们可以在 Unity 编辑器中创建,并且可以通过操作其父游戏对象/转换组件来操作,但不能以编程方式创建它们,也不能通过网格 API 在运行时编辑其属性。

视觉对象脚本

可以创建 Unity 脚本计算机并将其添加到场景中,并通过网格云脚本控制它。 网格工具包插件将浏览 Unity 项目中的资产,对于找到的每个脚本计算机,它将在网格云脚本项目中的“VisualScripts”文件夹中生成类。 此类派生自 VisualScriptNode,可用于从网格云脚本处理操作与脚本计算机关联的 Unity 变量。 将脚本计算机作为组件添加到 Unity 中的 GameObject 时,你将找到生成的类的相应实例作为相应 TransformNode 的子级。 使用此类的 API,可以控制脚本计算机的变量。

状态同步

默认情况下,网格会自动将一个客户端上的视觉脚本执行的场景更改复制到所有其他客户端。 若要了解通过可视化脚本进行的更改,必须满足以下先决条件:

  • 脚本计算机组件位于 GameObject 上,它是网格云脚本场景根的后代。
  • 已启用网格云脚本组件的“启用视觉脚本”选项。

如果未满足上述任一条件,网格视觉脚本运行时将继续复制场景更改,但网格云脚本将仍然对这些更改保持不可见。

初始状态

建议视觉脚本在启动时不修改或依赖于共享状态。 On Start 事件通常在网格云脚本服务连接发生之前发生;因此,无法同步该时间点的状态,并且行为可能不需要。

延迟加入

当客户端加入网格事件时,它们会同步到所有 Visual Script 节点的当前状态。 以前在其他客户端上引发的任何状态更改事件都不会在已加入的后期客户端上引发。 某些其他方案可能无法按预期工作;例如,如果通过启用 AudioSource 来响应 On State Changed 事件来触发声音,则 AudioSource 仍将在后期加入客户端中启用,但将在音频剪辑的开头开始播放。

重新父母和克隆

无法通过网格云脚本 API 创建 VisualScriptNode 。 创建 VisualScriptNode 的唯一方法是导出包含脚本计算机组件的 Unity 场景。 如果尝试克隆或重新分析 VisualScriptNode,将收到错误,因为无法支持此操作。 仍可以克隆或重新父级 VisualScriptNode 的父级,因为这对应于可以克隆和父级的包含 Unity GameObject。

有关生成的代码的说明

生成的代码将从脚本计算机和变量的名称中删除空格;例如,变量名称“my var”在代码中变为“MyVar”。 因此,可以创建不会生成有效代码的脚本计算机。 例如,如果你有两个名为“my var”和“myVar”的变量,则生成过程中会出现错误,并显示一条消息,要求你重命名变量(s)。

其他网格云脚本主题

将资源添加到网格云脚本服务

如果需要为要使用的网格云脚本服务添加资源,则需要将其作为嵌入资源添加到 C# 项目文件中。 这可以通过 Visual Studio 中的项目 UI 或通过直接将以下行添加到 .csproj 文件中来完成:

<EmbeddedResource Include="<my_resource_file>" CopyToOutputDirectory="PreserveNewest" />

请注意,这是如何打包 scene.map,可在 .csproj 文件中查看以供参考。

使用网格物理

Mesh Physics 将注意在客户端之间同步刚性体的运动。 网格云脚本TransformNode.PositionTransformNode.RotationRigidBody.Velocity不会RigidBody.AngularVelocity使用最新的模拟状态进行更新。 但是,如果在网格云脚本服务中设置了这些更改,客户端将应用更改。 请注意,更改单个属性会使其他属性保持不变。 例如,如果仅设置位置,速度不会改变,刚性体将继续运动,旧速度从新位置。 鉴于网格云脚本服务未使用刚性体的最新运动状态进行更新,建议仅为新的刚性主体设置这些状态。

如果 TransformNode 克隆 RigidBodyNode 了,则克隆的正文将注册并移交给 Mesh Physics 客户端之间的同步。 注意:克隆的刚性体将从原始刚性体开始的位置、旋转和速度。 如果这些内容应该不同,则必须在网格云脚本中显式设置它们。

后续步骤