> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/shape-components.md).

# 形状组件

了解为实体赋予 3D 形状和碰撞的不同组件。

Decentraland 中的三维场景基于 [实体-组件](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system) 模型，其中场景中的一切都是一个 *实体*，并且每个实体可以包含 *组件* 这些组件用于塑造其特性和功能。

实体的渲染形状由它使用的组件决定。

![](https://2460066822-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-417a02dcf0fd40f5032551a0c6996becac7e7166%2Fecs-simple-components-new.png?alt=media)

## 在 Creator Hub 中使用 Scene Editor

给实体添加形状最简单的方法是使用场景编辑器。你可以添加一个 **网格渲染器** 组件来提供一个基本形状，或者添加一个 **GLTF** 组件来引用文件中的 3D 模型。参见 [添加组件](/creator/content-creator-zh/chang-jing-bian-ji-qi/gou-jian/components.md#add-components).

## 基本形状

一些基本形状，通常称为 *原始体*，可以通过给实体添加一个 `MeshRenderer` 组件。

可用的形状如下。某些形状包含可选的附加字段，且仅适用于该形状。

* **盒子**:

  使用 `MeshRenderer.setBox()`，传入该实体。传入 `uvs` 作为一个额外的可选字段，用于映射纹理对齐。参见 [材质](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/materials.md) 了解更多详情。
* **平面**:

  使用 `MeshRenderer.setPlane()`，传入该实体。传入 `uvs` 作为一个额外的可选字段，用于映射纹理对齐。参见 [材质](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/materials.md) 了解更多详情。
* **球体**:

  使用 `MeshRenderer.setSphere()`，传入该实体。
* **圆柱体**:

  使用 `MeshRenderer.setCylinder()`，传入该实体。传入 `radiusTop` 和 `radiusBottom` 作为额外的可选字段，以修改该圆柱体。

  提示：设置以下任一项 `radiusTop` 或 `radiusBottom` 设为 0 可创建一个圆锥体。

以下示例创建一个立方体：

```ts
const myCube = engine.addEntity()

Transform.create(myCube, {
	position: Vector3.create(8, 1, 8),
})

MeshRenderer.setBox(myCube)
```

以下示例创建一个带有 `radiusTop` 值为 0 的圆柱体，这会生成一个圆锥体：

```ts
const myCone = engine.addEntity()

Transform.create(myCone, {
	position: Vector3.create(8, 1, 8),
})

MeshRenderer.setCylinder(myCone, 1, 0)
```

基本形状不包含材质。要给它添加颜色或纹理，你必须分配一个 [材质组件](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/materials.md) 给同一个实体。

要让基本形状可点击，或防止玩家穿过它，你必须给实体添加一个 *碰撞体* 通过一个 [MeshCollider](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md) 组件。

要更改一个已经拥有 `MeshRenderer` 组件的实体的材质，请运行 `MeshRenderer.setBox()` 或使用其他任一辅助函数，它就会覆盖原始形状。无需移除原始的 `MeshRenderer` ，也无需使用高级语法。

```ts
const myCube = engine.addEntity()

Transform.create(myCube, {
	position: Vector3.create(8, 1, 8),
})

MeshRenderer.setBox(myCube)

// 覆盖形状
MeshRenderer.setSphere(myCube)
```

{% hint style="warning" %}
**📔 注意**： `MeshRenderer` 组件必须通过以下方式导入：

> `import { MeshRenderer } from "@dcl/sdk/ecs"`

参见 [导入](/creator/content-creator-zh/chang-jing-sdk7/ru-men/coding-scenes.md#imports) 了解如何轻松处理这些。
{% endhint %}

## 3D 模型

对于更复杂的形状，你可以在 Blender 等外部工具中构建 3D 模型，然后将它们导入到 *.glTF* 或 *.glb* （二进制 *.glTF*). [glTF](https://www.khronos.org/gltf) （GL 传输格式）是 Khronos 提供的一个开放项目，为 3D 资源提供一种通用、可扩展的格式，既高效，又能与现代 Web 技术高度互操作。

要将外部模型添加到场景中，请添加一个 `GltfContainer` 组件到实体，并将其 `src` 设置为包含该模型的 glTF 文件路径。

```ts
const houseEntity = engine.addEntity()

GltfContainer.create(houseEntity, {
	src: 'models/House.gltf',
})
```

该 `src` 字段是必填项，在构造该组件时你必须为它赋值。在上面的示例中，该模型位于场景项目文件夹根目录下的一个 `models` 文件夹中。

{% hint style="info" %}
**💡 提示**：我们建议将你的模型单独放在 `assets/scene/models` 文件夹里。
{% endhint %}

glTF 模型可以包含其自带的嵌入式纹理、材质、碰撞体和动画。参见 [3D 模型](https://github.com/decentraland/docs-creator/blob/main/creator/3d-modeling/3d-models/README.md) 了解更多信息。要覆盖模型的材质，请使用 [GltfNodeModifiers](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/materials.md#modify-gltf-materials) 组件遍历所有玩家。参见 [修改 glTF 材质](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/materials.md#modify-gltf-materials) 了解更多详情。

为防止玩家穿过 3D 模型，或使模型可点击，你必须拥有一个 [碰撞体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md)，它可以嵌入在模型中，也可以通过一个 `MeshCollider` 组件。

请记住，所有模型、它们的着色器以及纹理都必须符合 [场景限制](/creator/content-creator-zh/chang-jing-sdk7/you-hua/scene-limitations.md).

{% hint style="warning" %}
**📔 注意**： `GltfContainer` 组件必须通过以下方式导入：

`import { GltfContainer } from "@dcl/sdk/ecs"`

参见 [导入](/creator/content-creator-zh/chang-jing-sdk7/ru-men/coding-scenes.md#imports) 了解如何轻松处理这些。
{% endhint %}

### 预加载 3D 模型

在某些情况下，自定义 3D 模型被添加到场景中，但不会立即使用。例如，只有当玩家与另一个对象交互时，自定义椅子模型才会加载。在这种情况下，第一次交互可能需要一些时间，直到模型下载并加载完成。

为避免这种情况，请使用 `AssetLoad.create` 方法，确保资源在需要之前已被下载。

```ts
import { AssetLoad } from "@dcl/sdk/ecs"

AssetLoad.create(engine.RootEntity, {
  assets: [
    "assets/scene/bundle1/explosion.glb",
  ],
})
```

更多信息请查看 [预加载资源](/creator/content-creator-zh/chang-jing-sdk7/you-hua/pre-load-resources.md) 文档。

### 3D 模型免费库

你也可以从一些免费或付费资源库下载 3D 模型，而不是自己制作。

为方便你开始，下面列出了一些提供免费或相对便宜内容的资源库：

* [IWB Catalog](https://dcl-iwb.co/)
* [Asset Ovi](https://assetovi.com/)
* [来自 Builder 的资源](https://github.com/decentraland/builder-assets/tree/master/assets)
* [SketchFab](https://sketchfab.com/)
* [Clara.io](https://clara.io/)
* [Archive3D](https://archive3d.net/)
* [SketchUp 3D Warehouse](https://3dwarehouse.sketchup.com/)
* [Thingiverse](https://www.thingiverse.com/) （主要为 3D 打印制作的 3D 模型，但也可适用于虚拟世界）
* [ShareCG](https://www.sharecg.com/)
* [CGTrader](https://www.cgtrader.com/)

{% hint style="warning" %}
**📔 注意**：请注意你下载内容所附带的许可限制。
{% endhint %}

请注意，在其中一些网站上，你可以选择下载模型的格式。始终选择 *.glTF* 格式（如果可用）。如果不可用，你必须先将它们转换为 *glTF* 之后才能在场景中使用它们。为此，我们建议将它们导入 Blender 并导出为 *.glTF* 再从那里导出。

### 优化 3D 模型

为确保场景中的 3D 模型加载更快并占用更少内存，请遵循以下最佳实践：

* 将你的模型保存为 *.glb* 格式，它是 *.gltf*.
* 如果你有多个模型共享相同纹理，请将带纹理的模型导出到单独的文件中。这样多个模型就可以引用同一个纹理文件，而该文件只需加载一次。
* 如果你的场景中有会出现和消失的实体，最好将这些实体池化并将它们放在地下，或缩放为 0。这会让它们更快出现，代价是它们在未使用时会占用内存。参见 [实体和组件](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/entities-components.md#pooling-entities-and-components)

## 拉伸形状

基本形状和 3D 模型都有默认尺寸，你可以通过更改实体的 `Transform` 组件。

```ts
const primitiveEntity = engine.addEntity()

MeshRenderer.setBox(primitiveEntity)

Transform.create(primitiveEntity, {
	position: { x: 8, y: 1, z: 8 },
	scale: { x: 4, y: 0.5, z: 4 },
})
```

## 设为不可见

你可以通过给实体添加一个 `VisibilityComponent`，并将其 `visible` 属性设为 *false*.

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 0, 4),
})
MeshRenderer.setBox(myEntity)

VisibilityComponent.create(myEntity, { visible: false })
```

该 `VisibilityComponent` 对于具有基本形状和 `GLTFContainer` 组件的实体，其效果相同。

如果实体不可见，其碰撞体仍可能阻挡玩家路径，和/或阻止点击其后方的实体，这取决于分配给碰撞体的碰撞层。

### 传播可见性

你可以使用 `propagateToChildren` 字段在 `VisibilityComponent` 用于将配置应用到实体子树中的每个子实体。如果 `propagateToChildren` 被设为 *真*，这些设置会向下影响所有层级的所有子实体。这可以帮你省去大量把每个子实体都标记为不可见或可见的繁琐工作。

```ts
// 父实体（显式不可见）
const parentEntity = engine.addEntity()
Transform.create(parentEntity, {
	position: Vector3.create(4, 0, 4),
})
MeshRenderer.setBox(parentEntity)
VisibilityComponent.create(parentEntity, { visible: false, propagateToChildren: true })

// 子实体（由于父实体而隐式不可见）
const child = engine.addEntity()
Transform.create(child, {
	position: Vector3.create(0, 1, 0),
	parent: parentEntity
})
MeshRenderer.setBox(child)
```

{% hint style="warning" %}
**📔 注意**：如果实体有其自己的 `VisibilityComponent`，这会覆盖来自父级的任何配置。

如果实体没有自己的 `VisibilityComponent`，那么其可见性将由最近的、带有一个 `VisibilityComponent` 和 `propagateToChildren` 设置为 *真*.
{% endhint %}

## 加载状态

如果一个 3D 模型相当大，渲染它可能需要一段明显的时间，这段时间会因玩家的硬件和许多其他因素而异。有时你需要确保模型已加载完成后再执行其他操作。例如，如果你想把玩家传送到高空中的平台，你需要先确保平台已完全渲染，再把玩家移动到那里，否则玩家可能会直接穿过平台掉下去。

要检查 3D 模型是否已完成渲染，请检查实体的 `GltfContainerLoadingState` 组件。该组件为只读，且存在于任何同时具有 `GltfContainer`组件。

该组件只有一个名为 `currentState`，其值来自 `LoadingState` 枚举中的值。

以下示例使用一个系统定期检查实体 3D 模型的加载状态。如果状态是 `LoadingState.FINISHED`，你可能会想在此执行自定义逻辑并结束系统执行。

```ts
export function main() {
	const meshEntity = engine.addEntity()
	GltfContainer.create(meshEntity, { src: 'models/Monster.glb' })
	engine.addSystem((deltaTime) => {
		const loadingState = GltfContainerLoadingState.getOrNull(meshEntity)
		if (!loadingState) return
		switch (loadingState.currentState) {
			case LoadingState.LOADING:
				console.log('网格正在加载')
				break
			case LoadingState.FINISHED:
				console.log('网格已完成')
				// 执行自定义逻辑
				break
			case LoadingState.FINISHED_WITH_ERROR:
				console.log('网格已完成，但可能存在问题')
				break
			case LoadingState.UNKNOWN:
				console.log('网格处于未知状态')
				break
		}
	})
}
```

## 高级语法

创建 `MeshRenderer` 组件的完整语法，不借助任何简化它的辅助函数，如下所示：

```ts
MeshRenderer.create(myBox, {
	mesh: {
		$case: 'box',
		box: { uvs: [] },
	},
})

MeshRenderer.create(myPlane, {
	mesh: {
		$case: 'plane',
		plane: { uvs: [] },
	},
})

MeshRenderer.create(myShpere, {
	mesh: {
		$case: 'sphere',
		sphere: {},
	},
})

MeshRenderer.create(myCylinder, {
	mesh: {
		$case: 'cylinder',
		cylinder: {},
	},
})
```

基础协议就是这样解释 MeshRenderer 组件的。辅助函数将这些细节抽象掉，并提供更友好的语法，但在底层它们输出的就是这种语法。

该 `$case` 字段允许你指定一种允许的类型。每种类型支持一组不同的参数。在上面的示例中， `盒子` 类型支持一个 `uvs` 字段中。

对 `$case` 支持的值如下：

* `盒子`
* `平面`
* `球体`
* `圆柱体`

根据 `$case`的值，定义相应形状的对象并传入任何相关属性都是有效的。

要向一个 `MeshRenderer` 组件添加到一个可能已经有该组件实例的实体上，请使用 `MeshRenderer.createOrReplace()`。像 `MeshRenderer.setBox()` 这样的辅助函数会处理覆盖现有组件实例的问题，但对一个已经有此组件的实体运行 `MeshRenderer.create()` 会返回错误。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/shape-components.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
