package main import ( "bytes" _ "embed" "log" "github.com/hajimehoshi/ebiten/v2/audio" "github.com/hajimehoshi/ebiten/v2/audio/mp3" "github.com/hajimehoshi/ebiten/v2/audio/wav" ) const ( SampleRate = 44100 ) //go:embed web/assets/game.mp3 var gameMusicData []byte //go:embed web/assets/jump.wav var jumpSoundData []byte //go:embed web/assets/pickupCoin.wav var coinSoundData []byte //go:embed web/assets/powerUp.wav var powerUpSoundData []byte // AudioSystem verwaltet Musik und Sound-Effekte type AudioSystem struct { audioContext *audio.Context // Musik musicPlayer *audio.Player musicVolume float64 // Sound-Effekte jumpSound []byte coinSound []byte powerUpSound []byte sfxVolume float64 // Mute muted bool } // NewAudioSystem erstellt ein neues Audio-System func NewAudioSystem() *AudioSystem { log.Println("🎵 Initialisiere Audio-System...") ctx := audio.NewContext(SampleRate) as := &AudioSystem{ audioContext: ctx, musicVolume: 0.7, // 70% Standard-Lautstärke sfxVolume: 0.3, // 30% Standard-Lautstärke muted: false, } // Musik laden log.Printf("📀 Lade Musik (%.2f MB)...", float64(len(gameMusicData))/(1024*1024)) as.loadMusic() // Sound-Effekte dekodieren log.Println("🔊 Lade Sound-Effekte...") as.jumpSound = as.loadWav(jumpSoundData) as.coinSound = as.loadWav(coinSoundData) as.powerUpSound = as.loadWav(powerUpSoundData) log.Println("✅ Audio-System bereit") return as } // loadMusic lädt und startet die Hintergrundmusik func (as *AudioSystem) loadMusic() { // MP3 dekodieren stream, err := mp3.DecodeWithSampleRate(SampleRate, bytes.NewReader(gameMusicData)) if err != nil { log.Printf("❌ Fehler beim Laden der Musik: %v", err) return } // Infinite Loop loop := audio.NewInfiniteLoop(stream, stream.Length()) // Player erstellen player, err := as.audioContext.NewPlayer(loop) if err != nil { log.Printf("❌ Fehler beim Erstellen des Music Players: %v", err) return } as.musicPlayer = player as.updateMusicVolume() log.Println("🎵 Musik geladen") } // loadWav lädt eine WAV-Datei und gibt die dekodierten Bytes zurück func (as *AudioSystem) loadWav(data []byte) []byte { stream, err := wav.DecodeWithSampleRate(SampleRate, bytes.NewReader(data)) if err != nil { log.Printf("❌ Fehler beim Laden von WAV: %v", err) return nil } // Stream in Bytes lesen decoded := bytes.NewBuffer(nil) _, err = decoded.ReadFrom(stream) if err != nil { log.Printf("❌ Fehler beim Dekodieren von WAV: %v", err) return nil } return decoded.Bytes() } // PlayMusic startet die Hintergrundmusik func (as *AudioSystem) PlayMusic() { if as.musicPlayer == nil { log.Println("⚠️ Music Player ist nil - Musik kann nicht gestartet werden") return } if as.musicPlayer.IsPlaying() { log.Println("⚠️ Musik läuft bereits") return } as.musicPlayer.Play() log.Printf("▶️ Musik gestartet (Volume: %.2f, Muted: %v)", as.musicVolume, as.muted) } // StopMusic stoppt die Hintergrundmusik func (as *AudioSystem) StopMusic() { if as.musicPlayer != nil && as.musicPlayer.IsPlaying() { as.musicPlayer.Pause() log.Println("⏸️ Musik gestoppt") } } // PlayJump spielt den Jump-Sound ab func (as *AudioSystem) PlayJump() { if as.muted { return } as.playSoundEffect(as.jumpSound, as.sfxVolume) } // PlayCoin spielt den Coin-Pickup-Sound ab func (as *AudioSystem) PlayCoin() { if as.muted { return } as.playSoundEffect(as.coinSound, as.sfxVolume) } // PlayPowerUp spielt den PowerUp-Sound ab func (as *AudioSystem) PlayPowerUp() { if as.muted { return } as.playSoundEffect(as.powerUpSound, as.sfxVolume) } // playSoundEffect spielt einen Sound-Effekt ab func (as *AudioSystem) playSoundEffect(soundData []byte, volume float64) { if soundData == nil { return } player := as.audioContext.NewPlayerFromBytes(soundData) if player == nil { return } player.SetVolume(volume) player.Play() } // SetMusicVolume setzt die Musik-Lautstärke (0.0 - 1.0) func (as *AudioSystem) SetMusicVolume(volume float64) { as.musicVolume = clamp(volume, 0.0, 1.0) as.updateMusicVolume() } // SetSFXVolume setzt die Sound-Effekt-Lautstärke (0.0 - 1.0) func (as *AudioSystem) SetSFXVolume(volume float64) { as.sfxVolume = clamp(volume, 0.0, 1.0) } // ToggleMute schaltet Mute an/aus func (as *AudioSystem) ToggleMute() { as.muted = !as.muted as.updateMusicVolume() log.Printf("🔇 Mute: %v", as.muted) } // SetMuted setzt Mute-Status func (as *AudioSystem) SetMuted(muted bool) { as.muted = muted as.updateMusicVolume() } // IsMuted gibt zurück, ob der Sound gemutet ist func (as *AudioSystem) IsMuted() bool { return as.muted } // GetMusicVolume gibt die aktuelle Musik-Lautstärke zurück func (as *AudioSystem) GetMusicVolume() float64 { return as.musicVolume } // GetSFXVolume gibt die aktuelle SFX-Lautstärke zurück func (as *AudioSystem) GetSFXVolume() float64 { return as.sfxVolume } // updateMusicVolume aktualisiert die Musik-Lautstärke func (as *AudioSystem) updateMusicVolume() { if as.musicPlayer != nil { if as.muted { as.musicPlayer.SetVolume(0) } else { as.musicPlayer.SetVolume(as.musicVolume) } } } // clamp begrenzt einen Wert zwischen min und max func clamp(value, min, max float64) float64 { if value < min { return min } if value > max { return max } return value }