形状组件
了解为实体提供 3D 形状和碰撞的不同组件。
Decentraland 中的三维场景基于 实体-组件 模型,其中场景中的一切都是一个 实体,而每个实体都可以包含 组件 ,这些组件塑造其特性和功能。
实体渲染出的形状由它使用的组件决定。

在 Creator Hub 中使用 Scene Editor
为实体赋予形状的最简单方法是使用 Scene Editor。你可以添加一个 网格渲染器 组件来提供一个基本形状,或者一个 GLTF 组件来引用文件中的 3D 模型。参见 添加组件.
基本形状
几种基本形状,通常称为 基元,可以通过给实体添加一个 MeshRenderer 组件。
可用的形状如下。若干形状包含可选的附加字段,且这些字段是该形状特有的。
盒体:
使用
MeshRenderer.setBox(),传入实体。传入uvs作为一个额外的可选字段,用于映射纹理对齐。参见 材质 了解更多详情。平面:
使用
MeshRenderer.setPlane(),传入实体。传入uvs作为一个额外的可选字段,用于映射纹理对齐。参见 材质 了解更多详情。球体:
使用
MeshRenderer.setSphere(),传入实体。圆柱体:
使用
MeshRenderer.setCylinder(),传入实体。传入radiusTop和radiusBottom作为额外的可选字段,以修改圆柱体。提示:设置以下任一项
radiusTop或radiusBottom0 即可变成圆锥体。
以下示例创建一个立方体:
以下示例创建一个圆柱体,且其 radiusTop 为 0,这会生成一个圆锥体:
基本形状不包含材质。若要为其添加颜色或纹理,必须为同一实体分配一个 材质组件 到同一实体。
若要让基本形状可点击,或防止玩家穿过它,你必须为实体添加一个 碰撞体 通过一个 MeshCollider 组件。
若要更改一个已经拥有 MeshRenderer 组件的实体的材质,请运行 MeshRenderer.setBox() 的实体的形状,可以使用 MeshRenderer ,也无需使用高级语法。
import { MeshRenderer } from "@dcl/sdk/ecs"
3D 模型
对于更复杂的形状,你可以在 Blender 之类的外部工具中构建 3D 模型,然后将它们导入到 .glTF 或 .glb (二进制 .glTF). glTF (GL 传输格式)是 Khronos 发起的一个开源项目,为 3D 资源提供一种通用、可扩展的格式,该格式高效且与现代 Web 技术具有高度互操作性。
要将外部模型添加到场景中,请添加一个 GltfContainer 组件到实体,并将其 src 设置为包含该模型的 glTF 文件路径。
该 src 字段是必需的,因此在构建组件时必须为其赋值。在上面的示例中,该模型位于一个 models 位于场景项目文件夹根目录下的文件夹中。
glTF 模型可以包含其自身嵌入的纹理、材质、碰撞体和动画。参见 3D 模型 以了解更多信息。若要覆盖模型的材质,请使用 GltfNodeModifiers 组件遍历所有玩家。详见 修改 glTF 材质 了解更多详情。
若要防止玩家穿过 3D 模型,或让模型可点击,你必须拥有一个 碰撞体,它可以嵌入在模型中,也可以通过一个提供 MeshCollider 组件。
请记住,所有模型、其着色器和纹理都必须符合 场景限制.
预加载 3D 模型
在某些情况下,自定义 3D 模型被添加到场景中,但不会立即使用。比如,自定义椅子模型可能只会在玩家与另一个对象交互时加载。在这种情况下,首次交互可能需要一些时间,直到模型下载并加载完成。
为避免这种情况,请使用 AssetLoad.create 方法,确保资源在需要之前已下载。
更多信息,请查看 预加载资源 文档。
免费的 3D 模型库
不必自己构建 3D 模型,你也可以从一些免费或付费素材库中下载它们。
为了帮助你入门,下面列出了一些提供免费或相对便宜内容的素材库:
Thingiverse (主要为 3D 打印而制作的 3D 模型,但也可适用于虚拟世界)
请注意,在这些网站中的一些上,你可以选择要下载模型的格式。始终选择 .glTF 格式(如果可用)。如果不可用,你必须先将它们转换为 glTF 之后才能在场景中使用它们。为此,我们建议先将它们导入 Blender,再导出为 .glTF 然后从那里导出。
优化 3D 模型
为确保场景中的 3D 模型加载更快并占用更少内存,请遵循以下最佳实践:
将你的模型保存为 .glb 格式,它是……的更轻量版本 .gltf.
如果你有多个模型共享相同的纹理,请将模型与纹理分别导出到单独的文件中。这样,多个模型就可以引用同一个纹理文件,而该文件只需加载一次。
如果你的场景中有会出现和消失的实体,或许最好将这些实体池化,并将它们保持在地下,或缩放为 0。这有助于它们更快地出现,代价是它们在不使用时仍会占用内存。参见 实体和组件
拉伸形状
基本形状和 3D 模型具有默认尺寸,你可以通过更改实体的 Transform 组件。
设为不可见
你可以通过给实体添加一个来让实体不可见 VisibilityComponent,并将其 visible 属性设置为 false.
该 VisibilityComponent 对具有基本形状和 GLTFContainer 组件的实体,同样适用。
如果实体不可见,其碰撞体可能会阻挡玩家路径和/或阻止点击其后面的实体,这取决于分配给碰撞体的碰撞层。
传播可见性
你可以使用 propagateToChildren 字段上的 VisibilityComponent 以将配置应用于实体子级树中的每个子级。如果 propagateToChildren 设置为 true,这些设置会影响向下所有层级的所有子级。这能为你省去大量逐个将每个子实体标记为不可见或可见的繁琐工作。
加载状态
如果 3D 模型相当大,渲染可能需要一些明显的时间,这段时间会因玩家硬件和许多其他因素而异。有时,在执行另一项操作之前,你需要确保模型已加载完成。例如,如果你想把玩家传送到高空中的平台上,你需要先确保平台已完全渲染,再把玩家移动到那里,否则玩家可能会直接从平台上掉下去。
要检查 3D 模型是否已完成渲染,请检查实体的 GltfContainerLoadingState 组件。该组件为只读,并存在于任何同时拥有 GltfContainer组件。
该组件只有一个名为 currentState,其值来自 LoadingState 枚举。
以下示例使用一个系统定期检查实体 3D 模型的加载状态。如果状态为 LoadingState.FINISHED,你可能希望在那里执行自定义逻辑并结束该系统的执行。
高级语法
创建一个 MeshRenderer 组件的完整语法,不使用任何简化辅助函数时,看起来如下:
基础协议就是这样解释 MeshRenderer 组件的。辅助函数将其抽象出来,并提供更友好的语法,但在幕后它们输出的就是这种语法。
该 $case 字段允许你指定允许的类型之一。每种类型支持不同的一组参数。在上面的示例中, 盒体 类型支持一个 uvs 字段中,指向你自己的流媒体基础设施。
支持的 $case 如下:
盒体平面球体圆柱体
根据 $case的值,可以为对应的形状定义对象,并传入任何相关属性。
要向一个可能已经有此组件实例的实体添加一个 MeshRenderer 组件,请使用 MeshRenderer.createOrReplace()。像 MeshRenderer.setBox() 这样的辅助函数会处理覆盖现有组件实例的问题,但直接在一个已经有此组件的实体上运行 MeshRenderer.create() 会返回错误。
最后更新于