2D Game Engine

Audio (AudioManager)

Cztery kanały, efektywna głośność, SoundHandle, mute i kolejka autoplay. Jak grać dźwięki bez problemów z blokadą przeglądarki.

Kanały i głośność#

AudioChannel = "master" | "music" | "sfx" | "ui"

Domyślne głośności: { master: 1, music: 0.7, sfx: 1, ui: 0.9 }.

Efektywna głośność dźwięku to iloczyn:

text
effectiveVolume = master × channel × volume
ts
const audio = this.getAudio()

audio.setMasterVolume(0.8)
audio.setVolume('music', 0.5)
audio.setVolume('sfx', 1)

Odtwarzanie#

ts
const handle = this.playSound('pickup', { channel: 'sfx', volume: 0.9 })
const music = this.playMusic('theme', { loop: true, volume: 0.6 })

this.stopMusic()
this.stopSounds()
this.stopSounds('game')

PlaySoundOptions = { channel?, volume?, loop?, rate?, owner? }.

SoundHandle#

ts
handle.id
handle.assetId
handle.channel
handle.owner
handle.loop
handle.rate
handle.volume
handle.effectiveVolume
handle.isPlaying
handle.statusName
handle.setVolume(0.5)
handle.stop()

Przykład: wyciszanie muzyki w dialogu#

ts
class DialogScene extends Scene {
  private music: SoundHandle | null = null

  create() {
    this.music = this.playMusic('dialog-theme', { loop: true })
    this.music?.setVolume(0.3)
  }

  close() {
    this.music?.stop()
    this.getEvents().emit('dialog:closed')
  }
}

API#

ts
audio.play(assetId, options?): SoundHandle | null
audio.playMusic(assetId, options?): SoundHandle | null
audio.stopMusic(); audio.getMusic()
audio.stop(handle): boolean; audio.stopAsset(assetId): number
audio.stopOwner(owner): number; audio.stopAll()
audio.pauseAll(); audio.resumeAll(); audio.isPaused()

audio.setVolume(channel, value); audio.setMasterVolume(value)
audio.getVolume(channel?); audio.getVolumes()
audio.setMuted(bool); audio.mute(); audio.unmute(); audio.toggleMute(); audio.isMuted()

audio.isUnlocked(); audio.unlock(): boolean; audio.onUnlock(listener)
audio.getHandleCount(); audio.getPendingCount(); audio.getState(): AudioState

Blokada autoplay i unlock#

Przeglądarki blokują audio bez gestu użytkownika. Żądania odtwarzania trafiają wtedy do kolejki pending, a unlock() odtwarza je po kolei i emituje audio:unlocked:

ts
class MenuScene extends Scene {
  create() {
    this.playMusic('theme', { loop: true })

    const start = this.createText('start', { x: 640, y: 500, text: 'Kliknij, aby zacząć' })
    start.setRender({ type: 'text', text: 'Kliknij, aby zacząć', interactive: true })

    this.getEvents().on('audio:unlocked', () => {
      this.getLogger().info('Audio odblokowane')
    })
  }

  update() {
    if (this.getInput().getPointer().isDown) {
      this.getAudio().unlock()
      this.getEvents().emit('request:scene', { to: 'game' })
    }
  }
}

Wskazówka

Nie walcz z blokadą autoplay — kolejka pending jest po to, żeby muzyka wystartowała dokładnie przy pierwszym kliknięciu, bez podwójnego odtwarzania i bez błędów w konsoli.

Mute i pauza#

ts
audio.toggleMute()

engine.pause()
engine.resume()

engine.pause() woła audio.pauseAll(), resume() — audio.resumeAll(). Każdy dźwięk ma właściciela (scenę), więc shutdown() sceny zatrzymuje tylko jej dźwięki przez stopOwner.

Stan audio#

ts
const state = audio.getState()

AudioState zawiera kanały, głośności, mute, unlock i liczby uchwytów — gotowe do nakładki debug i raportu diagnostycznego.