mcpField
P
player-avatar

player-avatar

github.com/dcl-regenesislabs開発1737 回インストール21 週間前に更新
リポジトリ

player-avatar について

Decentraland SDK7 のプレイヤーとアバター API を扱うシーン開発者向けのリファレンススキルです。プレイヤーデータ、NPC、アタッチメント、エモート、移動制御を説明します。

主な機能

  1. 01プレイヤーの位置とプロフィール取得
  2. 02NPC アバターとウェアラブルの設定
  3. 03アバターのアンカーポイントへのオブジェクト装着
  4. 04標準およびカスタムエモートの実行
  5. 05アバターエリアと移動制限の設定
  6. 06制限付きアクションによるテレポート

活用シーン

  1. 01プレイヤーと NPC の距離に応じたシーン処理
  2. 02アバターに手持ちや頭部のオブジェクトを追加
  3. 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> を使用します。

値列挙型場所
0AAPT_POSITIONアバターの足元(非推奨 — parent: engine.PlayerEntity を推奨)
1AAPT_NAME_TAGネームタグの上
2AAPT_LEFT_HAND左手
3AAPT_RIGHT_HAND右手
4AAPT_HEAD頭のボーン
5AAPT_NECK首
6AAPT_SPINE背骨の付け根
7AAPT_SPINE1背骨の中央
8AAPT_SPINE2背骨の上部
9AAPT_HIP腰
10AAPT_LEFT_SHOULDER左肩
11AAPT_LEFT_ARM左上腕
12AAPT_LEFT_FOREARM左前腕
13AAPT_LEFT_HAND_INDEX左手の人差し指
14AAPT_RIGHT_SHOULDER右肩
15AAPT_RIGHT_ARM右上腕
16AAPT_RIGHT_FOREARM右前腕
17AAPT_RIGHT_HAND_INDEX右手の人差し指
18AAPT_LEFT_UP_LEG左太もも
19AAPT_LEFT_LEG左ふくらはぎ
20AAPT_LEFT_FOOT左足
21AAPT_LEFT_TOE_BASE左足のつま先
22AAPT_RIGHT_UP_LEG右太もも
23AAPT_RIGHT_LEG右ふくらはぎ
24AAPT_RIGHT_FOOT右足
25AAPT_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_HAND
  • AvatarAnchorPointType.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 を参照してください。

コメント