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

```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*、以及 *z*。参见 [几何体类型](/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*.
* 为了更好地确定方向，请使用你的 *左侧* 手：
  * 你的食指（指向前方）是 *z* 轴
  * 你的中指（指向侧面）是 *x* 轴
  * 你的拇指（指向上方）是 *y* 轴。
* 如果一个实体是另一个实体的子级，那么 `x:0, y:0, z:0` 指的是其父实体的中心，无论父实体位于场景中的何处。
* 场景中的每个实体在任何时候都必须位于其所占地块的边界内。如果实体离开这些边界，就会报错。

  > 提示：在预览模式下查看场景时，越界的实体会以 *红色*.
* 你的场景高度也受限制。无论占用多少地块，每个场景的最大高度都是 330 米。放置在 200 米以上的实体可能会出现多人同步问题。另请参见 [场景限制](/creator/content-creator-zh/chang-jing-sdk7/you-hua/scene-limitations.md) 了解更多详情。

## 旋转

`旋转` 存储为一个 [*四元数*](https://en.wikipedia.org/wiki/Quaternion)，一个由四个数字组成的系统， *x*, *y*, *z* 和 *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
```

你也可以使用以下方式设置 rotation 字段： [*欧拉* 角](https://en.wikipedia.org/wiki/Euler_angles)，这是更常见的 *x*, *y* 和 *z* ，使用从 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* 和 *z* 它表示该轴上的旋转，以度为单位。完整旋转一圈需要 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`：实体只会在其 *z* 旋转轴上旋转。
* `targetEntity`：（可选）billboard 旋转面向的实体，而不是玩家的摄像机。如果所引用的实体不存在（例如尚未创建或已被移除），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: '这段文字始终清晰可读',
})

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: '观察球体',
})

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* 和 *z* 轴上的缩放因子。实体的形状会相应缩放，无论它是基本形状还是 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
```

## 继承父级变换

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

你可以通过设置 `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。子实体的位置也会相对于其父实体。我们需要把父实体的位置与子实体的位置相加。在这种情况下，由于父实体被缩小到原来的一半，子实体的变换也会按比例缩小。按绝对坐标看，子实体位于 `{ x: 3, y: 1.5, z: 1 }`。如果父实体具有一个 `旋转`，这也会影响子实体的最终位置，因为它改变了子实体偏移的轴。

如果子实体没有 `位置` 其 Transform 上的 `0,0,0`，则默认值为该项，这会使它与父实体处于相同位置。

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

{% hint style="warning" %}
**📔 注意**注意：避免在层级中创建循环，即一个实体最终成为自身的祖先。SDK 会检测这些循环，并记录一条警告，指出相关实体。在你修正父子关系之前，受影响的实体不会被正确放置。
{% endhint %}

## 将实体附加到头像

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

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

将实体附加到 avatar 的最简单方法，是将父级设置为 [保留实体](/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,
})
```

要将一个对象附加到 avatar 的某根骨骼上，并让它随 avatar 的动画一起移动，请添加一个 `AvatarAttach` 组件到该实体。

你可以在 avatar 上选择不同的锚点，其中大多数锚点都连接到玩家的骨架并跟随玩家的动画。例如，使用右手锚点时，当 avatar 挥手或在奔跑时摆动手臂，附加的实体也会移动，就像玩家正把它拿在手里一样。

```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 地址相同。如果未指定，则会将实体附加到本地玩家的 avatar。
* `anchorPointId`：要将实体附加到 avatar 骨架上的哪个锚点，使用枚举中的值 `AvatarAnchorPointType`.

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

以下示例会将一个实体附加到特定的 avatar 上，让所有其他玩家都看到它附加在同一个 avatar 上。

```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 实体的子实体。示例请参见本节开头。 >
{% 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\&token=bd194d22-a304-4254-b739-b25cdf372497)

实体渲染在场景的每个实例中都是本地决定的。将实体附加到某个玩家身上，不会让看到该玩家的其他玩家也看到它。如果一个实体附加到默认本地玩家，每个玩家都会将该实体体验为附加在自己的化身上。

{% 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" %}
**📔 注意**: 对于那些通过以太坊钱包连接的玩家，他们的 `userId` 与其以太坊地址相同。
{% endhint %}

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

```ts
executeTask(async () => {
	for (const [entity, data] of engine.getEntitiesWith(PlayerIdentityData)) {
		console.log('Player 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).

## 场景边界

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

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

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

场景地面上的网格显示了场景的边界，默认情况下范围从 0 到 16，在 *x* 和 *z* 轴上，并在 *y* 轴上低于 0，并在 *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 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/entity-positioning.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.
