> 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/entity-positioning.md).

# 实体定位

如何设置场景中实体的位置、旋转和缩放

你可以设置 *位置*, *旋转* 和 *缩放* 通过使用 `Transform` 组件。它可用于 3D 空间中的任何实体，影响该实体的渲染位置。这包括基本形状（立方体、球体、平面等）、3D 文本形状、NFT 形状以及 3D 模型（`GltfContainer`).

## 在 Creator Hub 中使用 Scene Editor

当你通过 Scene Editor 向场景中添加一个物品时，它会隐式包含一个 **Transform** 组件。然后，你通过更改实体的 Position、Rotation 或 Scale 来隐式更改该实体的 Transform 组件中的值。你也可以使用 Scene Editor 的 UI 以数值方式输入更精确的值。

## 代码基础

![](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)

```ts
// 创建一个新实体
const ball = engine.addEntity()

// 给这个实体一个形状，使其可见
MeshRenderer.setSphere(ball)

// 给这个实体添加一个 Transform 组件
Transform.create(ball, {
	position: Vector3.create(5, 1, 5),
	scale: Vector3.create(1, 1, 1),
	rotation: Quaternion.Identity(),
})
```

要在一段时间内移动、旋转或缩放场景中的实体，请逐帧增量更改该组件上的值。详见 [移动实体](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/move-entities.md) 以了解更多细节和最佳实践。

{% hint style="warning" %}
**📔 注意**: `Vector3` 和 `Quaternion` 必须通过

> `import { Vector3, Quaternion } from "@dcl/sdk/math"`

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

## 位置

`位置` 是一个 *3D 向量*，它设置了实体中心在三个轴上的位置， *x*, *y*，以及 *到*。见 [几何类型](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/special-types.md) 了解更多详情。

```ts
// 创建一个新实体
const ball = engine.addEntity()

// 使用预定义位置创建 Transform
Transform.create(ball, {
	  position: Vector3.create(5, 1, 5)
})

// 获取可变版本的 Transform
const mutableTransform = Transform.getMutable(ball)

// 使用对象设置位置
mutableTransform.position = { x: 5, y: 1, z: 5 }

// 使用对象设置位置（另一种语法）
mutableTransform.position = Vector3.create(2, 1, 4)

// 逐个设置每个轴
mutableTransform.position.x = 3
mutableTransform.position.y = 1
mutableTransform.position.z = 3
```

设置位置时，请牢记以下注意事项：

* 位置向量中的数字表示 *米* （除非该实体是某个被缩放实体的子实体）。
* 由单个地块组成的场景尺寸为 16m x 16m。场景中心（地面水平）位于 `x:8, y:0, z:8`。如果场景由多个地块组成，则中心会根据其排列方式而变化。
* `x:0, y:0, z:0` 指的是场景基准地块的 *西南* 角，位于地面水平。

  > 提示：在查看场景预览时，场景的 (0,0,0) 点会显示一个指南针，并标注各轴作为参考。

  > 注意：你可以通过编辑 `base` 属性来更改场景的基准地块。 *scene.json*.
* 为了更好地定位自己，请使用你的 *left* 手：
  * 你的食指（向前指）是 *到* 轴
  * 你的中指（向侧边指）是 *x* 轴
  * 你的拇指（向上指）是 *y* 轴。
* 如果一个实体是另一个实体的子实体，那么 `x:0, y:0, z:0` 指的是其父实体的中心，无论它位于场景中的什么位置。
* 你场景中的每个实体在任何时候都必须位于其所占地块的边界内。如果实体离开这些边界，就会报错。

  > 提示：在预览模式查看场景时，越界的实体会以 *red*.
* 你的场景在高度上也有限制。组成场景的地块越多，允许建造得越高。详见 [场景限制](/creator/content-creator-zh/chang-jing-sdk7/you-hua/scene-limitations.md) 了解更多详情。

## 旋转

`旋转` 存储为 [*四元数*](https://en.wikipedia.org/wiki/Quaternion)，一种由四个数字组成的系统， *x*, *y*, *到* 和 *w*。这些数字都在 -1 到 1 之间。详见 [几何类型](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/special-types.md) 了解更多详情。

```ts
// 创建一个新实体
const cube = engine.addEntity()

// 使用预定义的 0 旋转创建 Transform
Transform.create(cube, {
	  rotation: Quaternion.Identity()
})

// 获取可变版本的 Transform
const mutableTransform = Transform.getMutable(cube)

// 使用对象设置旋转，从欧拉角
mutableTransform.rotation = Quaternion.fromEulerDegrees(0, 90, 0)

// 使用对象设置旋转
mutableTransform.rotation = { x: 0.1, y: 0.5, z: 0.5, w: 0 }

// 逐个设置每个轴
mutableTransform.rotation.x = 0
mutableTransform.rotation.y = 1
mutableTransform.rotation.z = 0.3
mutableTransform.rotation.w = 0
```

你也可以使用 [*欧拉* 角](https://en.wikipedia.org/wiki/Euler_angles)，这是更常见的 *x*, *y* 和 *到* 表示法，数字范围从 0 到 360，大多数人都比较熟悉。要使用欧拉角，请使用以下表示法之一：

```ts
// 使用欧拉角中的预定义旋转创建 Transform
Transform.create(cube, {
	  rotation: Quaternion.fromEulerDegrees(0, 90, 0)
})

// 获取可变版本的 Transform
const mutableTransform = Transform.getMutable(cube)

// 使用对象设置旋转，从欧拉角
mutableTransform.rotation = Quaternion.fromEulerDegrees(0, 90, 0)
```

当使用一个 *3D 向量* 来表示欧拉角时， *x*, *y* 和 *到* 表示该轴上的旋转，以度为单位。完整旋转一圈需要 360 度。

当你获取实体的旋转时，默认返回的是四元数。若要获取以欧拉角表示的旋转，请使用 `Quaternion.toEulerAngles()`:

```ts
// 获取只读版本的 Transform
const transform = Transform.get(cube)

// 获取以欧拉角表示的旋转
const eulerAngle = Quaternion.toEulerAngles(transform.rotation)
```

## 获取实体的全局位置和旋转

该 `getWorldPosition` 和 `getWorldRotation` 函数会返回实体的全局位置和旋转。这意味着它返回的是玩家看到的实体位置或旋转，忽略任何父级层级关系。

* `getWorldPosition(engine, entity: Entity): Vector3Type`: 此函数会返回实体的世界位置，会考虑所有父实体的位置；如果实体本身有父实体，则也会考虑其父实体的位置，返回 `{x: 0, y: 0, z: 0}` 如果该实体没有 Transform。

```ts
const worldPos = getWorldPosition(engine, childEntity)
console.log(`World position: ${worldPos.x}, ${worldPos.y}, ${worldPos.z}`)
```

* `getWorldRotation(engine, entity: Entity): QuaternionType`: 此函数会返回实体的世界旋转，会考虑所有父实体的旋转；如果实体本身有父实体，也会一并考虑。它返回一个 `Quaternion` 类型，返回单位四元数 `{x: 0, y: 0, z: 0, w: 1}` 如果该实体没有 Transform。

```ts
const worldRot = getWorldRotation(engine, childEntity)
console.log(`World rotation: ${worldRot.x}, ${worldRot.y}, ${worldRot.z}, ${worldRot.w}`)
```

{% hint style="info" %}
**注意：** 全局位置和全局旋转是相对于场景内部坐标而言的，而不是相对于 Genesis City。
{% endhint %}

## 面向玩家

添加一个 *广告牌* 组件添加到实体上，使其始终旋转以面向玩家。

Billboard 是 90 年代 3D 游戏中常用的一种技术，当时大多数实体都是始终面向玩家的 2D 平面。同样的思路也可用于旋转 3D 模型。

```ts
// 创建一个新实体
const cube = engine.addEntity()

// 给实体添加一个可见形状
MeshRenderer.setBox(cube)

// 使用预定义位置创建 Transform
Transform.create(cube, {
	  position: Vector3.create(5, 1, 5)
})

// 给实体添加一个 Billboard 组件
Billboard.create(cube, {})
```

你可以使用以下参数配置 Billboard 的行为：

* `billboardMode`: 使用 `BillboardMode` 中的值来设置哪些旋转轴会自动转向玩家：
  * `BillboardMode.BM_ALL`: 实体会在所有旋转轴上转向玩家。如果玩家位于实体上方很高处，实体也会朝上。
  * `BillboardMode.BM_NONE`: 实体完全不会旋转。
  * `BillboardMode.BM_X`: 实体只会在其 *x* 旋转轴上旋转。
  * `BillboardMode.BM_Y`: 实体只会在其 *y* 旋转轴上旋转。它只会左右旋转，不会上下旋转。如果玩家位于实体上方或下方，它会保持与地面垂直。
  * `BillboardMode.BM_Z`: 实体只会在其 *到* 旋转轴上旋转。
* `targetEntity`:（可选）实体会转向面向的目标实体，而不是玩家的摄像机。如果引用的实体不存在（例如尚未创建或已被移除），Billboard 会停止旋转，并保持最后的朝向，直到该实体再次存在。将其设置为 `engine.CameraEntity` 等同于保持未设置。

```ts
// 平面 billboard
const perpendicularPlane = engine.addEntity()

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

MeshRenderer.setPlane(perpendicularPlane)

Billboard.create(perpendicularPlane, {
	billboardMode: BillboardMode.BM_Y,
})

// 文本标签
const textLabel = engine.addEntity()

Transform.create(textLabel, {
	position: Vector3.create(6, 1, 6),
})

TextShape.create(textLabel, {
	text: 'This text is always readable',
})

Billboard.create(textLabel)

// 面向另一个实体而不是玩家的标牌
const sphere = engine.addEntity()

Transform.create(sphere, {
	position: Vector3.create(4, 2, 4),
})

MeshRenderer.setSphere(sphere)

const sign = engine.addEntity()

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

TextShape.create(sign, {
	text: 'Watching the sphere',
})

Billboard.create(sign, {
	targetEntity: sphere,
})
```

{% hint style="info" %}
**💡 提示**: Billboard 非常适合添加到 *text* 实体上，因为它能让它们始终保持可读。
{% endhint %}

该 `旋转` 实体的值 `Transform` 组件不会随着 Billboard 跟随玩家而改变。

如果一个实体同时具有 `广告牌` 组件和 `Transform` 组件，使用 `旋转` 值，玩家会看到实体作为 Billboard 旋转。如果 Billboard 不影响所有轴，其余轴将根据 `Transform` 组件。

{% hint style="warning" %}
**📔 注意**: 如果同一时间有多个玩家在场，每个玩家都会看到 Billboard 模式的实体朝向自己。Billboard 旋转是为每个玩家本地计算的，不会影响其他人看到的内容。这不适用于带有 `targetEntity`: 因为目标位置是场景的一部分，所以所有玩家都会看到这些 Billboard 朝向相同的方向。
{% endhint %}

## 面向一组坐标

要让实体 A 看向实体 B：

```
1）从实体 B 的位置中减去实体 A 的位置，得到描述它们之间距离的向量。
2）对该向量进行归一化，使其长度为 1，同时保持其方向不变。
3）使用 `Quaternion.lookRotation` 获取一个四元数旋转，用来描述朝该方向旋转。
4）将该四元数设置为实体 A 的旋转
```

```ts
export function turn(entity: Entity, target: Vector3.ReadonlyVector3) {
	const transform = Transform.getMutable(entity)
	const difference = Vector3.subtract(target, transform.position)
	const normalizedDifference = Vector3.normalize(difference)
	transform.rotation = Quaternion.lookRotation(normalizedDifference)
}
```

## 缩放

`缩放` 也是一个 *3D 向量*，存储为一个 `Vector3` 对象，包括 *x*, *y* 和 *到* 轴上的缩放因子。实体的形状会相应缩放，无论是基本体还是 3D 模型。

默认缩放为 1，因此设置大于 1 的值可拉伸实体，小于 1 的值可缩小实体。

```ts
// 创建一个新实体
const ball = engine.addEntity()

// 使用预定义位置创建 Transform
Transform.create(ball, {
	  scale: Vector3.create(5, 5, 5)
})

// 获取可变版本的 Transform
const mutableTransform = Transform.getMutable(ball)

// 使用 Vector3 设置缩放

mutableTransform.scale = Vector3.create(2, 2, 2)

// 使用对象设置位置
mutableTransform.scale = { x: 5, y: 1, z: 5 }

// 逐个设置每个轴
mutableTransform.scale.x = 3
mutableTransform.scale.y = 3
mutableTransform.scale.z = 2
```

## 继承父级变换

当一个实体嵌套在另一个实体中时，子实体会继承父实体的组件。这意味着，如果父实体发生位置、缩放或旋转变化，其子实体也会受到影响。子实体的 position、rotation 和 scale 值不会覆盖父实体的值，而是会进行叠加。

你可以通过设置子实体上的 `parent` 字段，将一个实体指定为另一个实体的父实体。 `Transform` 组件。

如果父实体被缩放，它的所有子实体的位置值也会一起缩放。

```ts
// 创建实体
const parentEntity = engine.addEntity()
const childEntity = engine.addEntity()

// 为父实体创建 Transform
Transform.create(parentEntity, {
	position: Vector3.create(3, 1, 1),
	scale: Vector3.create(0.5, 0.5, 0.5),
})

// 为子实体创建 Transform，并将其设为子实体
Transform.create(childEntity, {
	position: Vector3.create(0, 1, 0),
	parent: parentEntity,
})
```

在这个例子中，由于父实体具有 0.5 的缩放值，子实体也会缩小到 0.5。子实体的位置也会相对于其父实体。我们必须将父实体的位置与子实体的位置相加。在这种情况下，由于父实体缩小为原来的一半，子实体的变换也会按比例缩小。按绝对值计算，子实体位于 `{ x: 3, y: 1.5, z: 1 }`。如果父实体有一个 `旋转`，这也会影响子实体的最终位置，因为它会改变子实体偏移的轴。

如果子实体的 Transform 上没有 `位置` ，默认值是 `0,0,0`，这会使其保持在与父实体相同的位置。

你可以使用一个没有形状组件的不可见实体作为父实体，来包裹一组其他实体。这个实体不会在渲染场景中可见，但可用于对其子实体分组并对它们全部应用变换。

{% hint style="warning" %}
**📔 注意**: 避免在层级中创建循环，即某个实体最终成为它自己的祖先。SDK 会检测这些循环并记录警告，标明涉及的实体。在你修正父子关系之前，受影响的实体不会被正确定位。
{% endhint %}

## 将实体附加到头像

有三种方法可以将实体附加到玩家身上：

* 将其设为以下对象的子实体： **Avatar 实体**
* 将其设为以下对象的子实体： **Camera 实体**
* 使用 **AvatarAttach 组件**

将实体附加到头像的最简单方法，是将父级设置为 [保留实体](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/entities-components.md#reserved-entities) `engine.PlayerEntity`。然后该实体就会随玩家的位置一起移动。

```ts
let childEntity = engine.addEntity()

MeshRenderer.setCylinder(childEntity)

Transform.create(childEntity, {
	scale: Vector3.create(0.2, 0.2, 0.2),
	position: Vector3.create(0, 0.4, 0),
	parent: engine.PlayerEntity,
})
```

你也可以将实体设置为 [保留实体](/creator/content-creator-zh/chang-jing-sdk7/jia-gou/entities-components.md#reserved-entities) `engine.CameraEntity`。在第一人称中使用摄像机实体时，附加的实体会跟随摄像机的移动。这非常适合让某些东西始终保持在视野中，例如让枪械的 3D 模型始终可见，即使摄像机朝上也是如此。

```ts
let childEntity = engine.addEntity()

MeshRenderer.setCylinder(childEntity)

Transform.create(childEntity, {
	scale: Vector3.create(0.2, 0.2, 0.2),
	position: Vector3.create(0, 0.4, 0),
	parent: engine.CameraEntity,
})
```

要将物体附加到头像的某根骨骼上，并让它随头像动画一起移动，请向实体添加一个 `AvatarAttach` 组件。

你可以在头像上选择不同的锚点，这些锚点大多连接到玩家的骨骼架构并跟随玩家动画。例如，使用右手锚点时，当头像挥手或跑步时摆动手臂，附加的实体也会一起移动，就像玩家手里拿着该实体一样。

```ts
// 附加到主玩家，如果未设置 avatarId，则默认使用 engine.PlayerEntity
AvatarAttach.create(myEntity, {
	anchorPointId: AvatarAnchorPointType.AAPT_NAME_TAG,
})

// 通过 ID 附加到玩家
AvatarAttach.create(myEntity, {
	avatarId: '0xAAAAAAAAAAAAAAAAA',
	anchorPointId: AvatarAnchorPointType.AAPT_NAME_TAG,
})
```

在创建一个 `AvatarAttach` 组件，请传入一个包含以下数据的对象：

* `avatarId`: *可选* 要附加到的玩家 ID。对于使用 Ethereum 钱包连接的玩家，这与玩家的 Ethereum 地址相同。如果未指定，则会将实体附加到本地玩家的头像上。
* `anchorPointId`：要将实体附加到头像骨架上的哪个锚点，使用枚举中的值 `AvatarAnchorPointType`.

{% hint style="warning" %}
**📔 注意**：如果你希望场景中的所有玩家都看到一个物体附加在同一个玩家身上，例如都能看到玩家 A 捡起了一个物体并将其拿在左手上，那么你必须为 `avatarId`提供一个值。如果未指定，那么所有玩家都会看到物体附加在自己的头像上。
{% endhint %}

下面的示例会将一个实体附加到某个特定头像上，供所有其他玩家看到它附加在同一个头像上。

```ts
import { getPlayer } from '@dcl/sdk/src/players'
import { AvatarAnchorPointType, AvatarAttach, engine, Entity } from '@dcl/sdk/ecs'
import { syncEntity } from '@dcl/sdk/network'

async function attachToPlayer(){

 let userData = await getPlayer()
 console.log(userData)

 if (!userData || !userData.wearables) return

  let entity = engine.addEntity()

  AvatarAttach.create(entity, {
    avatarId: userData.userId,
    anchorPointId: AvatarAnchorPointType.AAPT_RIGHT_HAND,
  })

  // 其他组件

  syncEntity(entity, [AvatarAttach.componentId])

}
```

以下锚点可用于 `AvatarAnchorPointType` 枚举：

* `AAPT_RIGHT_HAND`: 固定在玩家右手上
* `AAPT_LEFT_HAND`: 固定在玩家左手上
* `AAPT_HEAD`: 固定在玩家头部中心。
* `AAPT_NECK`: 固定在玩家颈部底部。
* `AAPT_SPINE`: 固定在脊柱上部。
* `AAPT_SPINE1`: 固定在脊柱中部。
* `AAPT_SPINE2`: 固定在脊柱下部。
* `AAPT_HIP`: 固定在髋骨上。
* `AAPT_LEFT_SHOULDER`: 固定在左肩上。
* `AAPT_LEFT_ARM`: 固定在左上臂骨上，位于肩部高度。
* `AAPT_LEFT_FOREARM`: 固定在左前臂骨上。
* `AAPT_LEFT_HAND_INDEX`: 固定在左手食指尖端。
* `AAPT_RIGHT_SHOULDER`: 固定在右肩上。
* `AAPT_RIGHT_ARM`: 固定在右上臂骨上，位于肩部高度。
* `AAPT_RIGHT_FOREARM`: 固定在右前臂骨上。
* `AAPT_RIGHT_HAND_INDEX`: 固定在右手食指尖端。
* `AAPT_LEFT_UP_LEG`: 固定在左腿的大腿骨上。
* `AAPT_LEFT_LEG`: 固定在左腿的小腿骨上。
* `AAPT_LEFT_FOOT`: 固定在左脚踝上。
* `AAPT_LEFT_TOE_BASE`: 固定在左脚趾尖端。
* `AAPT_RIGHT_UP_LEG`: 固定在右腿的大腿骨上。
* `AAPT_RIGHT_LEG`: 固定在右腿的小腿骨上。
* `AAPT_RIGHT_FOOT`: 固定在右脚踝上。
* `AAPT_RIGHT_TOE_BASE`: 固定在右脚趾尖端。
* `.AAPT_NAME_TAG`: 悬浮在玩家姓名标签正上方，不受玩家动画影响。

  > 注意：姓名标签的高度会根据玩家身上穿戴物的高度动态调整。因此，戴着高帽子的玩家，其姓名标签会比其他人略高一些。
* `AAPT_POSITION` *已弃用*: 玩家整体位置。这显示在比玩家脚下高 0.8 的位置。

  >

{% hint style="warning" %}
\> \*\*📔 注\*\*：\`AAPT\_POSITION\` 已弃用。若要跟随玩家的整体位置，最好将该实体设为 Avatar Entity 的子实体。示例见本节开头。 >
{% endhint %}

{% hint style="info" %}
**💡 提示**: 要使用这些值，请编写 `AvatarAnchorPointType.` 并且 VS Code 会在下拉列表中显示完整的选项列表。
{% endhint %}

![](https://2460066822-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-206945bba419a58cd6fe54c557cc6e5d75e1bb86%2Favatar-attach-points.png?alt=media)

实体渲染由场景中每个实例本地决定。将实体附加到某个玩家身上，并不会让正在查看该玩家的其他玩家看到它。如果实体附加到默认本地玩家身上，每个玩家都会将该实体视为附加在自己的头像上。

{% hint style="warning" %}
**📔 注意**: 附加到头像上的实体必须保持在场景边界内，才能被渲染。如果玩家走出你的场景，任何附加的实体都会停止渲染，直到玩家重新走回来。智能穿戴设备没有这个限制。
{% endhint %}

该 `AvatarAttach` 该组件会覆盖中的值 `Transform` 组件。你放入的任何值都 `Transform` 会被替换为实体相对于玩家 Transform 的相对位置；这些值会随着玩家移动和动画播放而逐帧更新。

如果你需要让实体相对于头像上的锚点带有偏移位置，或采用不同的旋转或缩放，请通过父实体来实现。

1. 创建一个不可见实体，只包含一个 `Transform` 和一个 `AvatarAttach` 组件。它的 `Transform` 值会随着玩家移动而被覆盖
2. 将你想附加的实体设为该父实体的子实体。它的 `Transform` 值可以描述相对于锚点的偏移量。

```ts
// 创建父实体
const parentEntity = engine.addEntity()

// 将父实体附加到玩家
AvatarAttach.create(parentEntity, {
	anchorPointId: AvatarAnchorPointType.AAPT_NAME_TAG,
})

// 创建子实体
let childEntity = engine.addEntity()

MeshRenderer.setCylinder(childEntity)

Transform.create(childEntity, {
	scale: Vector3.create(0.2, 0.2, 0.2),
	position: Vector3.create(0, 0.4, 0),
	parent: parentEntity,
})
```

{% hint style="warning" %}
**📔 注意**: 如果附加的实体有碰撞体，这些碰撞体可能会阻挡玩家移动或引起抖动效果。你可能需要禁用附加实体碰撞体的物理层。参见 [碰撞层](/creator/content-creator-zh/chang-jing-sdk7/3d-nei-rong-ji-chu/colliders.md#collision-layers)
{% endhint %}

{% hint style="warning" %}
**📔 注意**: 如果你有一个子实体，并且想知道它的全局位置和/或旋转，可以使用 `getWorldPosition` 和 `getWorldRotation` 函数。你可以在 [获取全局位置和旋转](#getting-global-position-and-rotation-of-an-entity) 部分。
{% endhint %}

### 附加到其他玩家

你可以使用 `AvatarAttach` 组件将实体附加到另一位玩家。为此，你必须知道该玩家的 id。

要将实体附加到另一位玩家的头像上，你必须在字段中提供用户 ID `avatarId`. 有 [多种不同的方法](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/user-data.md#get-player-data) 来获取这些数据。

{% hint style="warning" %}
**📔 注意**: 对于通过 Ethereum 钱包连接的玩家，他们的 `userId` 与其 Ethereum 地址相同。
{% endhint %}

获取 `userId` 所有其他附近玩家的，通过 `getPlayer()`

```ts
executeTask(async () => {
	for (const [entity, data] of engine.getEntitiesWith(PlayerIdentityData)) {
		console.log('玩家 ID：', data.address)
	}
})
```

将其与 `AvatarAttach`一起使用，你可以使用以下代码为场景中每个其他玩家的头顶添加一个悬浮立方体：

```ts
executeTask(async () => {
        for (const [entity, data] of engine.getEntitiesWith(PlayerIdentityData)) {
            const myEntity = engine.addEntity()
            MeshRenderer.setBox(myEntity)
            AvatarAttach.create(myEntity, {
                anchorPointId: AvatarAnchorPointType.AAPT_LEFT_HAND,
                avatarId: data.address,
            })
        }
    })
```

了解获取其他用户 ID 的其他方式，参见 [获取玩家数据](/creator/content-creator-zh/chang-jing-sdk7/jiao-hu-xing/user-data.md#get-player-data).

## 场景边界

场景中的所有实体都必须位于场景边界内，因为边界之外是属于其他玩家的土地地块。

如果在运行预览时，你的模型有任何部分超出这些限制，那么这些超出的部分将被裁切且不会被渲染，无论是在预览中还是在已发布的场景中。

场景中实体的位置在它们移动时会持续检查，如果实体离开场景然后返回，它将被移除，随后再次正常渲染。

场景地面上的网格显示了场景的边界，默认范围在 *x* 和 *到* 轴上从 0 到 16，并且在 *y* 轴上最多到 20。你可以自由地将实体放置在地下，即在 *y* 轴。

{% hint style="info" %}
**💡 提示**：如果你的场景需要更多地块，你可以在项目的 `scene.json` 文件中添加它们。参见 [场景元数据](/creator/content-creator-zh/chang-jing-sdk7/xiang-mu-lei-xing/scene-metadata.md) 中的说明。添加后，你应该会看到网格扩展以覆盖额外的地块。
{% endhint %}


---

# 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/entity-positioning.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.
