关于 player-avatar
Decentraland SDK7 的玩家与 Avatar 开发参考,面向场景开发者,涵盖玩家状态、NPC、附件、动作和移动控制。
主要功能
- 01读取玩家位置与档案
- 02配置 NPC Avatar 与穿戴物
- 03将物体附加到玩家骨骼点
- 04触发默认及自定义表情动作
- 05设置区域修饰与移动限制
- 06通过受限操作传送玩家
适用场景
- 01按玩家与 NPC 的距离触发场景逻辑
- 02为玩家角色添加手持或头部物件
- 03在对话或过场中限制玩家移动
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dcl-regenesislabs/ opendcl --skill player-avatar安装后在 Claude Code 中输入/player-avatar确认已加载。
附带文件references/avatar-apis.md
详细介绍
Decentraland 中的玩家与 Avatar 系统
编写方式的划分
AvatarShape(用于 NPC 和预先放置的 Avatar 的组件)受 main-entities.ts 支持 — 在此处完整声明 NPC,包括 id、名称、可穿戴物品等:
// main-entities.ts
shopkeeper: {
components: {
Transform: { position: { x: 8, y: 0, z: 8 } },
AvatarShape: {
id: 'shopkeeper-1',
name: 'Shopkeeper',
bodyShape: 'urn:decentraland:off-chain:base-avatars:BaseMale',
wearables: [],
emotes: []
}
}
}
AvatarAttach、AvatarModifierArea、AvatarBase、AvatarEquippedData 不在支持列表中 — 它们按设计用于运行时(绑定到实时玩家,或应用于动态创建的实体)。在 src/index.ts 中添加它们,并将其附加到通过 getEntityOrNullByName 查找的实体(用于静态放置),或运行时创建的实体(用于绑定到玩家的效果)。
预留的 engine.PlayerEntity 由引擎管理,在 main-entities.ts 中没有对应表示。
玩家位置与移动
通过预留的 engine.PlayerEntity 访问玩家的位置:
import { engine, Transform } from '@dcl/sdk/ecs'
function trackPlayer() {
if (!Transform.has(engine.PlayerEntity)) return
const playerTransform = Transform.get(engine.PlayerEntity)
console.log('Player position:', playerTransform.position)
console.log('Player rotation:', playerTransform.rotation)
}
engine.addSystem(trackPlayer)
基于距离的逻辑
import { Vector3 } from '@dcl/sdk/math'
function proximityCheck() {
const playerPos = Transform.get(engine.PlayerEntity).position
const npcPos = Transform.get(npcEntity).position
const distance = Vector3.distance(playerPos, npcPos)
if (distance < 5) {
console.log('Player is near the NPC')
}
}
engine.addSystem(proximityCheck)
玩家资料数据
获取玩家的名称、钱包地址和访客状态:
import { getPlayer } from '@dcl/sdk/src/players'
function main() {
const player = getPlayer()
if (player) {
console.log('Name:', player.name)
console.log('User ID:', player.userId)
console.log('Is guest:', player.isGuest)
}
}
userId— 玩家的 Ethereum 钱包地址(或访客 ID)isGuest— 如果玩家尚未连接钱包,则为true
从 Catalyst 获取完整的 Avatar 资料
getPlayer() 返回玩家的本地视图。要获取完整的 Avatar 数据(可穿戴物品列表、身体形状、皮肤/头发/眼睛颜色),请从 Catalyst 获取:
import { executeTask, signedFetch } from '@dcl/sdk/network'
import { getPlayer } from '@dcl/sdk/players'
executeTask(async () => {
const player = getPlayer()
if (!player || player.isGuest) return
const res = await fetch(`https://peer.decentraland.org/lambdas/profiles/${player.userId}`)
const body = await res.json()
// Response is an array of profiles; the first entry holds the active avatar.
const profile = body?.avatars?.[0]
if (!profile) return
console.log('name:', profile.name)
console.log('wearables:', profile.avatar.wearables)
console.log('skin color:', profile.avatar.skin.color) // { r, g, b } — already unwrapped, NOT { color: { r,g,b } }
})
注意事项:
- 响应格式为
{ avatars: [...] },而非扁平的资料对象。始终读取body.avatars[0]。 - 颜色字段(
skin.color、hair.color、eyes.color)已经是{ r, g, b }对象 — 不要再多解开一层。 - 此 fetch 请求无需身份验证;对于需要玩家签名身份的端点,请使用
@dcl/sdk/network中的signedFetch,而不是普通的fetch。
Avatar 附件
将 3D 对象附加到玩家的 Avatar:
import { engine, Transform, GltfContainer, AvatarAttach, AvatarAnchorPointType } from '@dcl/sdk/ecs'
const hat = engine.addEntity()
GltfContainer.create(hat, { src: 'models/hat.glb' })
// Attach to the local player's avatar
AvatarAttach.create(hat, {
anchorPointId: AvatarAnchorPointType.AAPT_NAME_TAG
})
锚点
附件可跟踪的骨骼/位置的完整枚举。在 main-entities.ts 中使用整数值(左侧)。在 src/index.ts 中使用 AvatarAnchorPointType.<NAME>。
| 值 | 枚举 | 位置 |
|---|---|---|
| 0 | AAPT_POSITION | Avatar 的脚部(已弃用 — 建议使用 parent: engine.PlayerEntity) |
| 1 | AAPT_NAME_TAG | 名牌上方 |
| 2 | AAPT_LEFT_HAND | 左手 |
| 3 | AAPT_RIGHT_HAND | 右手 |
| 4 | AAPT_HEAD | 头部骨骼 |
| 5 | AAPT_NECK | 颈部 |
| 6 | AAPT_SPINE | 脊柱根部 |
| 7 | AAPT_SPINE1 | 脊柱中部 |
| 8 | AAPT_SPINE2 | 脊柱顶部 |
| 9 | AAPT_HIP | 髋部 |
| 10 | AAPT_LEFT_SHOULDER | 左肩 |
| 11 | AAPT_LEFT_ARM | 左上臂 |
| 12 | AAPT_LEFT_FOREARM | 左前臂 |
| 13 | AAPT_LEFT_HAND_INDEX | 左手食指 |
| 14 | AAPT_RIGHT_SHOULDER | 右肩 |
| 15 | AAPT_RIGHT_ARM | 右上臂 |
| 16 | AAPT_RIGHT_FOREARM | 右前臂 |
| 17 | AAPT_RIGHT_HAND_INDEX | 右手食指 |
| 18 | AAPT_LEFT_UP_LEG | 左大腿 |
| 19 | AAPT_LEFT_LEG | 左小腿 |
| 20 | AAPT_LEFT_FOOT | 左脚 |
| 21 | AAPT_LEFT_TOE_BASE | 左脚趾 |
| 22 | AAPT_RIGHT_UP_LEG | 右大腿 |
| 23 | AAPT_RIGHT_LEG | 右小腿 |
| 24 | AAPT_RIGHT_FOOT | 右脚 |
| 25 | AAPT_RIGHT_TOE_BASE | 右脚趾 |
附加到特定玩家
AvatarAttach.create(hat, {
avatarId: '0x123...abc', // Target player's wallet address
anchorPointId: AvatarAnchorPointType.AAPT_RIGHT_HAND
})
触发表情动作
默认表情动作
import { triggerEmote } from '~system/RestrictedActions'
// Play a built-in emote
triggerEmote({ predefinedEmote: 'robot' })
triggerEmote({ predefinedEmote: 'wave' })
triggerEmote({ predefinedEmote: 'clap' })
自定义场景表情动作
import { triggerSceneEmote } from '~system/RestrictedActions'
// Play a custom emote animation (file must end with _emote.glb)
triggerSceneEmote({
src: 'animations/Snowball_Throw_emote.glb',
loop: false
})
注意:
- 表情动作仅在玩家静止站立时播放 — 行走或跳跃会中断播放
- 自定义表情动作文件必须使用
_emote.glb后缀
NPC Avatar
使用 AvatarShape 创建 Avatar 形状的 NPC:
import { engine, Transform, AvatarShape } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const npc = engine.addEntity()
Transform.create(npc, { position: Vector3.create(8, 0, 8) })
AvatarShape.create(npc, {
id: 'npc-1',
name: 'Guard',
bodyShape: 'urn:decentraland:off-chain:base-avatars:BaseMale', // or BaseFemale
wearables: [
'urn:decentraland:off-chain:base-avatars:eyebrows_00',
'urn:decentraland:off-chain:base-avatars:mouth_00',
'urn:decentraland:off-chain:base-avatars:eyes_00',
'urn:decentraland:off-chain:base-avatars:blue_tshirt',
'urn:decentraland:off-chain:base-avatars:brown_pants',
'urn:decentraland:off-chain:base-avatars:classic_shoes',
'urn:decentraland:off-chain:base-avatars:short_hair'
],
hairColor: { r: 0.92, g: 0.76, b: 0.62 }, // RGB values 0-1
skinColor: { r: 0.94, g: 0.85, b: 0.6 }, // RGB values 0-1
emotes: []
})
模特(仅展示可穿戴物品)
仅展示可穿戴物品,不显示完整的 Avatar 身体:
AvatarShape.create(mannequin, {
id: 'mannequin-1',
name: 'Display',
wearables: [
'urn:decentraland:matic:collections-v2:0x90e5cb2d673699be8f28d339c818a0b60144c494:0'
],
show_only_wearables: true
})
NPC Avatar 是静态的 — 它们会显示 Avatar 模型,但不会自行移动或播放动画。可与 Animator 或 Tween 组合以实现移动。
Avatar 修饰区域
修改 Avatar 在某个区域内的外观或行为:
import { engine, Transform, AvatarModifierArea, AvatarModifierType } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const modifierArea = engine.addEntity()
Transform.create(modifierArea, {
position: Vector3.create(8, 1.5, 8),
scale: Vector3.create(4, 3, 4)
})
AvatarModifierArea.create(modifierArea, {
area: { box: Vector3.create(4, 3, 4) },
modifiers: [AvatarModifierType.AMT_HIDE_AVATARS],
excludeIds: ['0x123...abc'] // Optional: exclude specific players
})
可用修饰项
AvatarModifierType.AMT_HIDE_AVATARS // Hide all avatars in the area
AvatarModifierType.AMT_DISABLE_PASSPORTS // Disable clicking on avatars to see profiles
AvatarModifierType.AMT_DISABLE_JUMPING // Prevent jumping in the area
移动限制
// Prevent jumping in a specific area
const constraintArea = engine.addEntity()
Transform.create(constraintArea, {
position: Vector3.create(8, 5, 8),
scale: Vector3.create(6, 10, 6)
})
AvatarModifierArea.create(constraintArea, {
area: { box: Vector3.create(6, 10, 6) },
modifiers: [AvatarModifierType.AMT_DISABLE_JUMPING]
})
Avatar 移动设置
调整玩家的移动速度和跳跃高度:
import { engine, AvatarLocomotionSettings } from '@dcl/sdk/ecs'
// Modify run speed and jump height
AvatarLocomotionSettings.createOrReplace(engine.PlayerEntity, {
runSpeed: 8, // default is ~6
jumpHeight: 3 // default is ~1.5
})
InputModifier — 限制玩家移动
为本地玩家禁用特定的移动模式。适用于过场动画、对话冻结和移动谜题。应用于 engine.PlayerEntity。
import { engine, InputModifier } from '@dcl/sdk/ecs'
InputModifier.createOrReplace(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({
disableWalk: false,
disableJog: false,
disableRun: false,
disableJump: false,
disableDoubleJump: false,
disableGliding: false
})
})
将任意字段设为 true 即可禁用对应模式。禁用期间:
- 重力仍然生效(玩家仍会下落)。
- 摄像头仍可自由旋转。
- 玩家仍可触发指针/接近事件。
- 玩家离开场景时,所有限制都会自动解除。
冻结所有移动
InputModifier.createOrReplace(engine.PlayerEntity, {
mode: InputModifier.Mode.Standard({ disableAll: true })
})
要解除限制:InputModifier.deleteFrom(engine.PlayerEntity),或者使用所有标志均为 false 的参数调用 createOrReplace。
传送玩家
必须使用 ~system/RestrictedActions 中的 movePlayerTo 来移动或传送玩家。 设置 Transform.getMutable(engine.PlayerEntity).position 不起作用——运行时会忽略对玩家变换的直接写入。engine.PlayerEntity 上的 Transform 为只读;engine.CameraEntity 也一样。
movePlayerTo 只能在同一场景内传送玩家。跨场景传送需要获得玩家的明确同意,并使用不同的 API。
import { movePlayerTo } from '~system/RestrictedActions'
// Move player to a position
void movePlayerTo({
newRelativePosition: Vector3.create(8, 0, 8)
})
// Move player with camera direction
void movePlayerTo({
newRelativePosition: Vector3.create(8, 0, 8),
cameraTarget: Vector3.create(8, 1, 12)
})
movePlayerTo 会返回一个 Promise——如果需要串联操作,请使用 await:
import { executeTask } from '@dcl/sdk/ecs'
executeTask(async () => {
await movePlayerTo({ newRelativePosition: Vector3.create(8, 0, 8) })
console.log('teleported, continuing flow')
})
Avatar 变更监听器
实时响应 Avatar 变更:
import { AvatarEmoteCommand, AvatarBase, AvatarEquippedData } from '@dcl/sdk/ecs'
// Detect when any player triggers an emote
AvatarEmoteCommand.onChange(engine.PlayerEntity, (cmd) => {
if (cmd) console.log('Emote played:', cmd.emoteUrn)
})
// Detect avatar appearance changes (wearables, skin color, etc.)
AvatarBase.onChange(engine.PlayerEntity, (base) => {
if (base) console.log('Avatar name:', base.name)
})
// Detect equipment changes
AvatarEquippedData.onChange(engine.PlayerEntity, (equipped) => {
if (equipped) console.log('Wearables changed:', equipped.wearableUrns)
})
其他锚点
除了常用锚点外,完整列表还包括:
AvatarAnchorPointType.AAPT_POSITION— Avatar 脚部位置AvatarAnchorPointType.AAPT_NAME_TAG— 名称标签上方AvatarAnchorPointType.AAPT_LEFT_HAND/AAPT_RIGHT_HANDAvatarAnchorPointType.AAPT_HEAD— 头部骨骼AvatarAnchorPointType.AAPT_NECK— 颈部骨骼
显示 Avatar 道具前需要检查玩家的钱包? 请参阅 nft-blockchain 技能,了解如何使用
getPlayer()和isGuest检查钱包。
最佳实践
- 读取玩家数据前,始终检查
Transform.has(engine.PlayerEntity)——第一帧时数据可能尚未就绪 - 尝试使用依赖钱包的功能前,使用
getPlayer()检查isGuest AvatarAttach要求目标玩家与当前玩家处于同一场景——玩家离开后,附加物会消失- 自定义表情文件必须遵循
_emote.glb命名约定 - 在私人房间或谜题区域中使用
AvatarModifierArea和AMT_HIDE_AVATARS - 如果希望特定玩家(例如场景所有者)保持可见,请在修改器区域中添加
excludeIds - 绝不要使用
Transform.getMutable(engine.PlayerEntity)移动玩家——这不起作用。始终使用~system/RestrictedActions中的movePlayerTo Transform.get(engine.PlayerEntity)仅可用于读取位置
组件字段详情请参阅 {baseDir}/../../context/components-reference.md。
AvatarShape 的完整字段、可穿戴物品 URN、锚点、表情名称和事件回调请参阅 {baseDir}/references/avatar-apis.md。