player-avatar について
Decentraland SDK7 のプレイヤーとアバター API を扱うシーン開発者向けのリファレンススキルです。プレイヤーデータ、NPC、アタッチメント、エモート、移動制御を説明します。
主な機能
- 01プレイヤーの位置とプロフィール取得
- 02NPC アバターとウェアラブルの設定
- 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 のプレイヤーとアバターシステム
定義ファイルの分担
AvatarShape(NPC と事前配置されたアバターに使用するコンポーネント)は 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 からアバターの完全なプロフィールを取得する
getPlayer() はプレイヤーのローカルビューを返します。アバターの完全なデータ(ウェアラブルの一覧、ボディシェイプ、肌・髪・目の色)を取得するには、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 }オブジェクトです。さらに一段階展開しないでください。 - フェッチは認証なしで行われます。プレイヤーの署名済み ID が必要なエンドポイントには、通常の
fetchではなく@dcl/sdk/networkのsignedFetchを使用してください。
アバターへのアタッチ
プレイヤーのアバターに 3D オブジェクトをアタッチします:
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 | アバターの足元(非推奨 — 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 アバター
AvatarShape を使って、アバター型の 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: []
})
マネキン(ウェアラブルのみ表示)
全身のアバターではなく、ウェアラブルだけを表示します:
AvatarShape.create(mannequin, {
id: 'mannequin-1',
name: 'Display',
wearables: [
'urn:decentraland:matic:collections-v2:0x90e5cb2d673699be8f28d339c818a0b60144c494:0'
],
show_only_wearables: true
})
NPC アバターは静的です。アバターモデルを表示しますが、自ら移動したりアニメーションしたりすることはありません。移動させるには、Animator または Tween と組み合わせてください。
アバター修飾エリア
領域内でのアバターの見た目や動作を変更します:
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]
})
アバターの移動設定
プレイヤーの移動速度とジャンプの高さを調整します。
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')
})
アバター変更リスナー
アバターの変更にリアルタイムで対応します。
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— アバターの足元の位置AvatarAnchorPointType.AAPT_NAME_TAG— ネームタグの上AvatarAnchorPointType.AAPT_LEFT_HAND/AvatarAnchorPointType.AAPT_RIGHT_HANDAvatarAnchorPointType.AAPT_HEAD— 頭のボーンAvatarAnchorPointType.AAPT_NECK— 首のボーン
アバターアイテムを表示する前にプレイヤーのウォレットを確認する必要がありますか?
getPlayer()とisGuestを使用したウォレットの確認方法については、nft-blockchain スキルを参照してください。
ベストプラクティス
- プレイヤーのデータを読み取る前に、必ず
Transform.has(engine.PlayerEntity)を確認してください。初回フレームではまだ準備できていない場合があります。 - ウォレットに依存する機能を使用する前に、
getPlayer()を使用してisGuestを確認してください。 AvatarAttachを使用するには、対象のプレイヤーが同じシーンにいる必要があります。プレイヤーがシーンを離れると、アタッチメントは消えます。- カスタムエモートファイルには、
_emote.glbという命名規則を使用してください。 - プライベートルームやパズルエリアでは、
AMT_HIDE_AVATARSを指定したAvatarModifierAreaを使用してください。 - 特定のプレイヤー(シーンの所有者など)を表示したままにする場合は、モディファイアエリアに
excludeIdsを追加してください。 - プレイヤーの移動に
Transform.getMutable(engine.PlayerEntity)を絶対に使用しないでください。 機能しません。必ず~system/RestrictedActionsのmovePlayerToを使用してください。 Transform.get(engine.PlayerEntity)は、位置の読み取りに限り使用できます。
コンポーネントのフィールドの詳細については、{baseDir}/../../context/components-reference.md を参照してください。
AvatarShape の全フィールド、ウェアラブルの URN、アンカーポイント、エモート名、イベントコールバックについては、{baseDir}/references/avatar-apis.md を参照してください。