audio-video
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAudio and Video in Decentraland
Decentraland 中的音频与视频
Audio Source (Sound Effects & Music)
音频源(音效与音乐)
Play audio clips from files:
typescript
import { engine, Transform, AudioSource } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const speaker = engine.addEntity()
Transform.create(speaker, { position: Vector3.create(8, 1, 8) })
AudioSource.create(speaker, {
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)
})从文件播放音频片段:
typescript
import { engine, Transform, AudioSource } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const speaker = engine.addEntity()
Transform.create(speaker, { position: Vector3.create(8, 1, 8) })
AudioSource.create(speaker, {
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)
})Supported Formats
支持的格式
- (recommended)
.mp3 .ogg.wav
- (推荐)
.mp3 .ogg.wav
File Organization
文件组织结构
project/
├── sounds/
│ ├── click.mp3
│ ├── background-music.mp3
│ └── explosion.ogg
├── src/
│ └── index.ts
└── scene.jsonproject/
├── sounds/
│ ├── click.mp3
│ ├── background-music.mp3
│ └── explosion.ogg
├── src/
│ └── index.ts
└── scene.jsonPlay/Stop/Toggle
播放/停止/切换
typescript
// Play
AudioSource.getMutable(speaker).playing = true
// Stop
AudioSource.getMutable(speaker).playing = false
// Toggle
const audio = AudioSource.getMutable(speaker)
audio.playing = !audio.playingtypescript
// Play
AudioSource.getMutable(speaker).playing = true
// Stop
AudioSource.getMutable(speaker).playing = false
// Toggle
const audio = AudioSource.getMutable(speaker)
audio.playing = !audio.playingPlay on Click
点击播放
typescript
import { pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'
const button = engine.addEntity()
// ... set up transform and mesh ...
const audioEntity = engine.addEntity()
Transform.create(audioEntity, { position: Vector3.create(8, 1, 8) })
AudioSource.create(audioEntity, {
audioClipUrl: 'sounds/click.mp3',
playing: false,
loop: false,
volume: 0.8
})
pointerEventsSystem.onPointerDown(
{ entity: button, opts: { button: InputAction.IA_POINTER, hoverText: 'Play sound' } },
() => {
// Reset and play
const audio = AudioSource.getMutable(audioEntity)
audio.playing = false
audio.playing = true
}
)typescript
import { pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'
const button = engine.addEntity()
// ... set up transform and mesh ...
const audioEntity = engine.addEntity()
Transform.create(audioEntity, { position: Vector3.create(8, 1, 8) })
AudioSource.create(audioEntity, {
audioClipUrl: 'sounds/click.mp3',
playing: false,
loop: false,
volume: 0.8
})
pointerEventsSystem.onPointerDown(
{ entity: button, opts: { button: InputAction.IA_POINTER, hoverText: 'Play sound' } },
() => {
// Reset and play
const audio = AudioSource.getMutable(audioEntity)
audio.playing = false
audio.playing = true
}
)Audio Streaming
音频流
Stream audio from a URL (radio, live streams):
typescript
import { engine, Transform, AudioStream } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const radio = engine.addEntity()
Transform.create(radio, { position: Vector3.create(8, 1, 8) })
AudioStream.create(radio, {
url: 'https://example.com/stream.mp3',
playing: true,
volume: 0.3
})从URL流式传输音频(电台、直播流):
typescript
import { engine, Transform, AudioStream } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
const radio = engine.addEntity()
Transform.create(radio, { position: Vector3.create(8, 1, 8) })
AudioStream.create(radio, {
url: 'https://example.com/stream.mp3',
playing: true,
volume: 0.3
})Video Player
视频播放器
Play video on a surface:
typescript
import { engine, Transform, VideoPlayer, Material, MeshRenderer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
// Create a screen
const screen = engine.addEntity()
Transform.create(screen, {
position: Vector3.create(8, 3, 15.9),
scale: Vector3.create(8, 4.5, 1) // 16:9 ratio
})
MeshRenderer.setPlane(screen)
// Add video player
VideoPlayer.create(screen, {
src: 'https://example.com/video.mp4',
playing: true,
loop: true,
volume: 0.5,
playbackRate: 1.0,
position: 0 // Start time in seconds
})
// Create video texture
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })
// Basic material (recommended — better performance)
Material.setBasicMaterial(screen, {
texture: videoTexture
})在平面上播放视频:
typescript
import { engine, Transform, VideoPlayer, Material, MeshRenderer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
// Create a screen
const screen = engine.addEntity()
Transform.create(screen, {
position: Vector3.create(8, 3, 15.9),
scale: Vector3.create(8, 4.5, 1) // 16:9 ratio
})
MeshRenderer.setPlane(screen)
// Add video player
VideoPlayer.create(screen, {
src: 'https://example.com/video.mp4',
playing: true,
loop: true,
volume: 0.5,
playbackRate: 1.0,
position: 0 // Start time in seconds
})
// Create video texture
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })
// Basic material (recommended — better performance)
Material.setBasicMaterial(screen, {
texture: videoTexture
})Video Controls
视频控制
typescript
// 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'typescript
// 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'Enhanced Video Material (PBR)
增强型视频材质(PBR)
For a brighter, emissive video screen:
typescript
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()
})创建更明亮、自发光的视频屏幕:
typescript
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()
})Video Events
视频事件
Monitor video playback state:
typescript
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
}
})监控视频播放状态:
typescript
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
}
})Spatial Audio
空间音频
Audio in Decentraland is spatial by default — it gets louder as the player approaches the audio source entity and quieter as they move away. The position is determined by the entity's .
TransformTo make audio non-spatial (same volume everywhere), there's no built-in flag — keep the volume low and place the audio at the scene center.
Decentraland 中的音频默认是空间化的——玩家靠近音频源实体时声音会变大,远离时则变小。位置由实体的组件决定。
Transform若要使音频非空间化(音量保持一致),目前没有内置开关——可以将音量调低,并将音频源放置在场景中心。
Free Audio Files
免费音频文件
Always check the audio catalog before creating placeholder sound file references. It contains 50 free sounds from the Creator Hub asset packs.
Read for music tracks (ambient, dance, medieval, sci-fi, etc.), ambient sounds (birds, city, factory, etc.), interaction sounds (buttons, doors, levers, chests), sound effects (explosions, sirens, bells), and game mechanic sounds (win/lose, heal, respawn, damage).
{baseDir}/../../context/audio-catalog.mdTo use a catalog sound:
bash
undefined在创建占位符音频文件引用前,请务必查看音频目录。该目录包含来自创作者中心资源包的50个免费音频文件。
阅读获取各类音乐曲目(氛围音、舞曲、中世纪风格、科幻风格等)、环境音效(鸟鸣、城市、工厂等)、交互音效(按钮、门、杠杆、宝箱)、特效音(爆炸、警报、钟声)以及游戏机制音效(胜利/失败、治疗、重生、受伤)。
{baseDir}/../../context/audio-catalog.md使用目录中的音频文件:
bash
undefinedDownload from catalog
Download from catalog
mkdir -p sounds
curl -o sounds/ambient_1.mp3 "https://builder-items.decentraland.org/contents/bafybeic4faewxkdqx67dloyw57ikgaeibc2e2dbx34hwjubl3gfvs2r4su"
```typescript
// Reference in code — must be a local file path
AudioSource.create(entity, { audioClipUrl: 'sounds/ambient_1.mp3', playing: true, loop: true })mkdir -p sounds
curl -o sounds/ambient_1.mp3 "https://builder-items.decentraland.org/contents/bafybeic4faewxkdqx67dloyw57ikgaeibc2e2dbx34hwjubl3gfvs2r4su"
```typescript
// Reference in code — must be a local file path
AudioSource.create(entity, { audioClipUrl: 'sounds/ambient_1.mp3', playing: true, loop: true })How to suggest audio
如何推荐音频
- Read the audio catalog file
- Search for sounds matching the user's description/theme
- Suggest specific sounds with download commands
- Download selected sounds into the scene's directory
sounds/ - Reference them in code with local paths
Important:only works with local files. Never use external URLs for theAudioSourcefield. Always download audio intoaudioClipUrlfirst.sounds/
- 阅读音频目录文件
- 搜索符合用户描述/主题的音频
- 推荐具体的音频文件并提供下载命令
- 将选中的音频下载到场景的目录中
sounds/ - 在代码中使用本地路径引用这些音频
重要提示:仅支持本地文件。请勿在AudioSource字段中使用外部URL。请务必先将音频下载到audioClipUrl目录中。sounds/
Video State Polling
视频状态轮询
Check video playback state programmatically:
typescript
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)
}
})通过编程方式检查视频播放状态:
typescript
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)
}
})Audio Playback Events
音频播放事件
Use the component to detect audio state changes:
AudioEventtypescript
import { AudioEvent } from '@dcl/sdk/ecs'
engine.addSystem(() => {
const event = AudioEvent.getOrNull(audioEntity)
if (event) {
console.log('Audio state:', event.state) // playing, paused, finished
}
})使用组件检测音频状态变化:
AudioEventtypescript
import { AudioEvent } from '@dcl/sdk/ecs'
engine.addSystem(() => {
const event = AudioEvent.getOrNull(audioEntity)
if (event) {
console.log('Audio state:', event.state) // playing, paused, finished
}
})Permission for External Media
外部媒体权限
External audio/video URLs require the permission in scene.json:
ALLOW_MEDIA_HOSTNAMESjson
{
"requiredPermissions": ["ALLOW_MEDIA_HOSTNAMES"],
"allowedMediaHostnames": ["stream.example.com", "cdn.example.com"]
}外部音频/视频URL需要在scene.json中配置权限:
ALLOW_MEDIA_HOSTNAMESjson
{
"requiredPermissions": ["ALLOW_MEDIA_HOSTNAMES"],
"allowedMediaHostnames": ["stream.example.com", "cdn.example.com"]
}Multiple Video Surfaces
多视频屏幕
Share one VideoPlayer across multiple screens by referencing the same :
videoPlayerEntitytypescript
Material.setPbrMaterial(screen1, {
texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})
Material.setPbrMaterial(screen2, {
texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})通过引用同一个,在多个屏幕上共享一个VideoPlayer:
videoPlayerEntitytypescript
Material.setPbrMaterial(screen1, {
texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})
Material.setPbrMaterial(screen2, {
texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})Video Limits & Tips
视频限制与技巧
- Simultaneous videos: 1 in preview, 5 in Explorer, 10 max across the scene
- Distance-based control: Pause video when player is far away to save bandwidth
- Supported formats: (H.264),
.mp4, HLS (.webm) for live streaming.m3u8 - Live streaming: Use HLS () URLs — most reliable across clients
.m3u8
- 同时播放的视频数量:预览环境中为1个,Explorer中为5个,场景中最多10个
- 基于距离的控制:玩家远离时暂停视频以节省带宽
- 支持的格式:(H.264)、
.mp4、用于直播的HLS(.webm).m3u8 - 直播流:使用HLS()URL——在各客户端中兼容性最佳
.m3u8
Important Notes
重要注意事项
- Audio files must be in the project's directory (relative paths from project root)
- Video requires HTTPS URLs — HTTP won't work
- Players must interact with the scene (click) before audio can play (browser autoplay policy)
- Keep audio files small — large files increase scene load time
- Use for music and
.mp3for sound effects (smaller file sizes).ogg - For live video streaming, use HLS (.m3u8) URLs when possible
- 音频文件必须位于项目目录中(相对于项目根目录的相对路径)
- 视频需要使用HTTPS URL——HTTP无法正常工作
- 玩家必须与场景交互(点击)后才能播放音频(浏览器自动播放策略限制)
- 音频文件体积请保持较小——大文件会增加场景加载时间
- 音乐推荐使用格式,音效推荐使用
.mp3格式(文件体积更小).ogg - 若进行视频直播,请尽可能使用HLS(.m3u8)URL