audio-video について
audio-videoは、Decentralandのシーンで音響や動画を扱うためのスキルです。エンティティの設定、実行時制御、動画マテリアルの割り当て、メディアイベントの処理を説明します。
主な機能
- 01ローカル音源と空間オーディオの設定
- 02オーディオストリームの設定
- 03メッシュ面への動画の割り当て
- 04再生と音量の実行時制御
- 05メディア再生状態の監視
活用シーン
- 01シーンへのクリック音追加
- 02BGMやラジオストリームの作成
- 03画面やモデル面での動画再生
- 04音声・動画状態の変化への対応
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dcl-regenesislabs/ opendcl --skill audio-videoインストール後、Claude Code で/audio-videoを入力して読み込まれたか確認します。
同梱ファイルreferences/media-reference.md
詳細
Decentraland のオーディオとビデオ
定義の分割
AudioSource(ローカルのオーディオファイル)、AudioStream(ストリーミング URL)、VideoPlayerはすべてmain-entities.tsでサポートされています。ストリーミング/再生設定とともに、スピーカー/ラジオ/スクリーンのエンティティをここで完全に宣言します。- 実行時の音量/再生/一時停止の切り替えは、
getMutableを使ってsrc/index.tsで行います。
メディアコンポーネントの選び方
| 必要なもの | コンポーネント | 主な違い |
|---|---|---|
| ファイルからの効果音(クリック、爆発、足音) | AudioSource | ローカルファイル、空間オーディオ、1 回のみ再生またはループ再生 |
| BGM またはラジオストリーム | AudioStream | 外部 URL、非空間オーディオ、連続再生 |
| サーフェス上のビデオ(スクリーン、看板) | VideoPlayer + Material.Texture.Video | 表示にはメッシュが必要 |
選択の流れ:
- ローカルのオーディオファイルですか? →
AudioSource - ストリーミング URL(ラジオ、ライブオーディオ)ですか? →
AudioStream - ビデオコンテンツですか? → 平面/メッシュ上の
VideoPlayer
オーディオソース(効果音と音楽)
main-entities.ts でスピーカーを宣言します。
// main-entities.ts
import type { Scene } from '@dcl/sdk/scene-types'
export const scene = {
speaker: {
components: {
Transform: { position: { x: 8, y: 1, z: 8 } },
AudioSource: {
audioClipUrl: 'sounds/music.mp3',
playing: true,
loop: true,
volume: 0.5, // 0 to 1
pitch: 1.0 // Playback speed (0.5 = half speed, 2.0 = double)
}
}
}
} satisfies Scene
対応フォーマット
.mp3(推奨).ogg.wav
空間オーディオと非空間オーディオ
AudioSource はデフォルトで空間オーディオです(音源から離れるほど音量が小さくなります)。BGM/ラジオ/位置に依存しない効果音には、global: true を設定します。
// main-entities.ts
bg_music: {
components: {
Transform: { position: { x: 0, y: 0, z: 0 } }, // ignored when global
AudioSource: {
audioClipUrl: 'sounds/bg.mp3',
playing: true,
loop: true,
volume: 0.5,
global: true // heard everywhere in the scene at constant volume
}
}
}
ファイルの整理
project/
├── sounds/
│ ├── click.mp3
│ ├── background-music.mp3
│ └── explosion.ogg
├── src/
│ └── index.ts
└── scene.json
再生/停止/切り替え(実行時、src/index.ts 内)
import { engine, AudioSource } from '@dcl/sdk/ecs'
export function main() {
const speaker = engine.getEntityOrNullByName('speaker')
if (!speaker) return
AudioSource.getMutable(speaker).playing = true // play
AudioSource.getMutable(speaker).playing = false // stop
// toggle
const audio = AudioSource.getMutable(speaker)
audio.playing = !audio.playing
}
クリック時に再生
静的エンティティ(ボタンのメッシュとクリック時の効果音用スピーカー)は main-entities.ts に記述します。PointerEvents とクリックハンドラーは実行時に設定するため、src/index.ts に記述します。
// main-entities.ts
sfx_button: {
components: {
Transform: { position: { x: 8, y: 1, z: 8 } },
MeshRenderer: { mesh: { $case: 'box', box: { uvs: [] } } }
}
},
click_sfx: {
components: {
Transform: { position: { x: 8, y: 1, z: 8 } },
AudioSource: {
audioClipUrl: 'sounds/click.mp3',
playing: false,
loop: false,
volume: 0.8
}
}
}
// src/index.ts
import { engine, AudioSource, pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'
export function main() {
const button = engine.getEntityOrNullByName('sfx_button')
const sfx = engine.getEntityOrNullByName('click_sfx')
if (!button || !sfx) return
pointerEventsSystem.onPointerDown(
{ entity: button, opts: { button: InputAction.IA_POINTER, hoverText: 'Play sound' } },
() => {
// Reset and play
const audio = AudioSource.getMutable(sfx)
audio.playing = false
audio.playing = true
}
)
}
オーディオストリーミング
AudioStream は main-entities.ts でサポートされています。ストリーミング設定とともにラジオエンティティを 1 か所で宣言します。
// main-entities.ts
radio: {
components: {
Transform: { position: { x: 8, y: 1, z: 8 } },
GltfContainer: { src: 'models/radio.glb' },
AudioStream: {
url: 'https://example.com/stream.mp3',
playing: true,
volume: 0.3
}
}
}
実行時の再生/音量の切り替えには、AudioSource と同じ getMutable パターンを使います。
ビデオプレーヤー
VideoPlayer、MeshRenderer、スクリーンの Transform はすべて main-entities.ts に記述します。Material 内のビデオ テクスチャのバインドには名前ではなく実行時の Entity ID が必要です。ビルド時に名前で解決されるのは Transform.parent だけです。そのため、Material は実行時に src/index.ts で設定します。
// main-entities.ts
video_screen: {
components: {
Transform: {
position: { x: 8, y: 3, z: 15.9 },
scale: { x: 8, y: 4.5, z: 1 } // 16:9 ratio
},
MeshRenderer: { mesh: { $case: 'plane', plane: { uvs: [] } } },
VideoPlayer: {
src: 'https://example.com/video.mp4',
playing: true,
loop: true,
volume: 0.5,
playbackRate: 1.0,
position: 0 // start time in seconds
}
}
}
// src/index.ts
import { engine, Material } from '@dcl/sdk/ecs'
export function main() {
const screen = engine.getEntityOrNullByName('video_screen')
if (!screen) return
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })
// Basic material — better performance than PBR for video surfaces
Material.setBasicMaterial(screen, { texture: videoTexture })
}
ビデオコントロール
// Play
VideoPlayer.getMutable(screen).playing = true
// Pause
VideoPlayer.getMutable(screen).playing = false
// Change volume
VideoPlayer.getMutable(screen).volume = 0.8
// Change source
VideoPlayer.getMutable(screen).src = 'https://example.com/other.mp4'
拡張ビデオマテリアル(PBR)
より明るく、発光するビデオスクリーンにするには:
import { Color3 } from '@dcl/sdk/math'
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })
Material.setPbrMaterial(screen, {
texture: videoTexture,
roughness: 1.0,
specularIntensity: 0,
metallic: 0,
emissiveTexture: videoTexture,
emissiveIntensity: 0.6,
emissiveColor: Color3.White()
})
GLTF サーフェス上のビデオ(曲面スクリーン、テレビ、モニター)
「スクリーン」がモデルの一部である場合(リビングルームのシーンにあるテレビや、曲面のアリーナディスプレイなど)は、GLTF をそのまま使い、実行時に GltfNodeModifiers でスクリーンのマテリアルをビデオテクスチャに上書きします。
// main-entities.ts — declare the TV model
tv: {
components: {
Transform: { position: { x: 8, y: 1.5, z: 8 } },
GltfContainer: { src: 'models/tv.glb' },
VideoPlayer: { src: 'https://example.com/show.mp4', playing: true, loop: true }
}
}
// src/index.ts — bind the video texture to the screen sub-mesh by path
import { engine, Material, GltfNodeModifiers } from '@dcl/sdk/ecs'
export function main() {
const tv = engine.getEntityOrNullByName('tv')
if (!tv) return
const videoTexture = Material.Texture.Video({ videoPlayerEntity: tv })
GltfNodeModifiers.createOrReplace(tv, {
modifiers: [
{
path: 'TV/Screen', // GLTF node path to the screen sub-mesh
material: {
material: {
$case: 'unlit',
unlit: { texture: videoTexture }
}
}
}
]
})
}
モデルのすべてのノードにビデオマテリアルを適用するには、path: ''(空)を使います。モデル全体がスクリーンの場合(例:Blender から書き出した平面の看板メッシュ)に便利です。
ビデオイベント
ビデオの再生状態を監視します。
import { videoEventsSystem, VideoState } from '@dcl/sdk/ecs'
videoEventsSystem.registerVideoEventsEntity(screen, (videoEvent) => {
switch (videoEvent.state) {
case VideoState.VS_PLAYING:
console.log('Video started playing')
break
case VideoState.VS_PAUSED:
console.log('Video paused')
break
case VideoState.VS_READY:
console.log('Video ready to play')
break
case VideoState.VS_ERROR:
console.log('Video error occurred')
break
}
})
空間オーディオ
Decentraland のオーディオはデフォルトで空間オーディオです。プレイヤーがオーディオソースのエンティティに近づくと音量が大きくなり、離れると小さくなります。位置はエンティティの Transform によって決まります。
オーディオを非空間オーディオ(どこでも同じ音量)にする組み込みのフラグはありません。音量を低く保ち、シーンの中央に配置してください。
無料のオーディオファイル
プレースホルダーのサウンドファイル参照を作成する前に、必ずオーディオカタログを確認してください。Creator Hub のアセットパックに含まれる 50 種類の無料サウンドが収録されています。
音楽トラック(アンビエント、ダンス、メディーバル、SF など)、環境音(鳥、街、工場など)、インタラクション音(ボタン、ドア、レバー、宝箱)、効果音(爆発、サイレン、鐘)、ゲームメカニクスのサウンド(勝利/敗北、回復、リスポーン、ダメージ)については、{baseDir}/../../context/audio-catalog.md を読んでください。
カタログのサウンドを使うには:
# Download from catalog
mkdir -p sounds
curl -o sounds/ambient_1.mp3 "https://builder-items.decentraland.org/contents/bafybeic4faewxkdqx67dloyw57ikgaeibc2e2dbx34hwjubl3gfvs2r4su"
// Reference in code — must be a local file path
AudioSource.create(entity, { audioClipUrl: 'sounds/ambient_1.mp3', playing: true, loop: true })
オーディオの提案方法
- オーディオカタログファイルを読み込みます
- ユーザーの説明やテーマに合うサウンドを検索します
- ダウンロードコマンドとともに、具体的なサウンドを提案します
- 選択したサウンドをシーンの
sounds/ディレクトリにダウンロードします - コード内ではローカルパスで参照します
重要:
AudioSourceはローカルファイルでのみ動作します。audioClipUrlフィールドに外部 URL を使用しないでください。必ずオーディオを先にsounds/にダウンロードしてください。
動画の状態のポーリング
プログラムから動画の再生状態を確認します。
import { videoEventsSystem, VideoState } from '@dcl/sdk/ecs'
engine.addSystem(() => {
const state = videoEventsSystem.getVideoState(videoEntity)
if (state) {
console.log('Video state:', state.state) // VideoState.VS_PLAYING, VS_PAUSED, etc.
console.log('Current time:', state.currentOffset)
}
})
オーディオ再生イベント
AudioEvent コンポーネントを使用して、オーディオの状態変化を検出します。
import { AudioEvent } from '@dcl/sdk/ecs'
engine.addSystem(() => {
const event = AudioEvent.getOrNull(audioEntity)
if (event) {
console.log('Audio state:', event.state) // playing, paused, finished
}
})
外部メディアの権限
外部のオーディオ/動画 URL を使用するには、scene.json に ALLOW_MEDIA_HOSTNAMES 権限が必要です。
{
"requiredPermissions": ["ALLOW_MEDIA_HOSTNAMES"],
"allowedMediaHostnames": ["stream.example.com", "cdn.example.com"]
}
複数の動画サーフェス
同じ videoPlayerEntity を参照することで、1 つの VideoPlayer を複数の画面で共有できます。
Material.setPbrMaterial(screen1, {
texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})
Material.setPbrMaterial(screen2, {
texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})
動画の制限とヒント
- 同時再生できる動画数:プレビューでは 1 本、Explorer では 5 本、シーン全体では最大 10 本です
- 距離に応じた制御:帯域幅を節約するため、プレイヤーが遠くにいるときは動画を一時停止します
- 対応形式:ライブストリーミングには
.mp4(H.264)、.webm、HLS(.m3u8)を使用できます - ライブストリーミング:クライアント間で最も安定して動作する HLS(
.m3u8)URL を使用します
コンポーネントのフィールドの詳細、対応形式、高度な使用パターンについては、{baseDir}/references/media-reference.md を参照してください。
重要な注意事項
- オーディオファイルはプロジェクトのディレクトリ内に配置してください(プロジェクトルートからの相対パスを使用します)
- 動画には HTTPS URL が必要です。HTTP は動作しません
- オーディオを再生するには、プレイヤーがシーンをクリックして操作する必要があります(ブラウザーの自動再生ポリシー)
- オーディオファイルは小さくしてください。大きなファイルはシーンの読み込み時間を長くします
- 音楽には
.mp3、効果音には.oggを使用してください(ファイルサイズを小さくできます) - ライブ動画ストリーミングには、可能な限り HLS(.m3u8)URL を使用してください