All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 1m50s
238 lines
5.3 KiB
Go
238 lines
5.3 KiB
Go
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
|
|
}
|