> 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\&token=181a73c9-363a-4328-9874-6fed49447dc3)

## 在 Creator Hub 中使用场景编辑器

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

## 基本形状

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

可用的形状如下。部分形状包含可选的附加字段，这些字段是该形状特有的。

* **box**:

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

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

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

  使用 `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 Transmission Format）是 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/blob/main/creator/3d-modeling/3d-models.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` 被设置为 *true*，这些设置会向下影响所有层级中的所有子级。这样可以节省你大量逐个把每个子实体标记为不可见或可见的繁琐工作。

```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` 并设置为 *true*.
{% 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('mesh is LOADING')
				break
			case LoadingState.FINISHED:
				console.log('mesh is FINISHED')
				// 执行自定义逻辑
				break
			case LoadingState.FINISHED_WITH_ERROR:
				console.log('mesh is FINISHED BUT MAY HAVE PROBLEMS')
				break
			case LoadingState.UNKNOWN:
				console.log('mesh is in an UNKNOWN STATE')
				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` 字段允许你指定一种允许的类型。每种类型都支持一组不同的参数。在上面的示例中， `box` 类型支持一个 `uvs` 字段。

的支持值如下： `$case` 是以下这些：

* `box`
* `plane`
* `sphere`
* `cylinder`

根据 `$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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
