Änderung: Optimierung der Audio-Integration und Verbesserung der Benutzerinteraktion im Taxi-Spiel

Änderungen:
- Reduzierung der Buffergröße im MotorSound für geringere Latenz.
- Einführung eines AudioUnlockHandlers zur Aktivierung des AudioContext bei Benutzerinteraktionen.
- Anpassung der Initialisierung und Steuerung des MotorSounds, um eine bessere Benutzererfahrung zu gewährleisten.
- Verbesserung der Konsolenausgaben zur besseren Nachverfolgbarkeit während der Audio-Initialisierung.

Diese Anpassungen verbessern die Audio-Performance und die Benutzerinteraktion im Taxi-Minispiel erheblich.
This commit is contained in:
Torsten Schulz (local)
2025-09-17 13:04:51 +02:00
parent abfa6f4b8d
commit 96491902e3
2 changed files with 51 additions and 24 deletions

View File

@@ -19,7 +19,8 @@ class MotorSound {
async init() {
// Verwende ScriptProcessor für bessere Kompatibilität
console.log('Initialisiere MotorSound mit ScriptProcessor');
this.workletNode = this.context.createScriptProcessor(1024);
// Kleinere Buffergröße für geringere Latenz
this.workletNode = this.context.createScriptProcessor(256);
this.workletNode.onaudioprocess = this.process.bind(this);
// Filter-Kette für motorähnlicheren Klang
@@ -34,7 +35,7 @@ class MotorSound {
this.lowPass.Q.value = 0.5;
this.gainNode = this.context.createGain();
this.gainNode.gain.value = 0.25; // Leiser starten
this.gainNode.gain.setValueAtTime(0.25, this.context.currentTime);
// Verbindung: script -> lowshelf -> lowpass -> gain
this.workletNode.connect(this.lowShelf);

View File

@@ -227,7 +227,8 @@ export default {
obstacles: [],
keys: {},
motorSound: null,
audioContext: null
audioContext: null,
audioUnlockHandler: null
}
},
async mounted() {
@@ -238,6 +239,7 @@ export default {
this.loadMaps();
this.setupEventListeners();
await this.initializeMotorSound();
this.setupAudioUnlockHandlers();
},
beforeUnmount() {
this.cleanup();
@@ -307,10 +309,25 @@ export default {
document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp);
},
setupAudioUnlockHandlers() {
this.audioUnlockHandler = async () => {
await this.initAudioOnUserInteraction();
if (this.audioContext && this.audioContext.state === 'suspended') {
await this.audioContext.resume().catch(() => {});
}
document.removeEventListener('pointerdown', this.audioUnlockHandler, { capture: true });
document.removeEventListener('touchstart', this.audioUnlockHandler, { capture: true });
document.removeEventListener('keydown', this.audioUnlockHandler, { capture: true });
this.audioUnlockHandler = null;
};
document.addEventListener('pointerdown', this.audioUnlockHandler, { capture: true, passive: true });
document.addEventListener('touchstart', this.audioUnlockHandler, { capture: true, passive: true });
document.addEventListener('keydown', this.audioUnlockHandler, { capture: true });
},
async initializeMotorSound() {
// AudioContext wird erst bei erster Benutzerinteraktion erstellt
console.log('MotorSound wird bei erster Benutzerinteraktion initialisiert');
if (import.meta.env?.DEV) console.log('[Audio] Lazy-Init: MotorSound wird bei erster Benutzerinteraktion initialisiert');
},
async initAudioOnUserInteraction() {
@@ -318,17 +335,21 @@ export default {
try {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
if (import.meta.env?.DEV) console.log('[Audio] init: Context erstellt. state=', this.audioContext.state, 'rate=', this.audioContext.sampleRate);
// Autoplay-Policy: Context sofort im User-Gesture fortsetzen
if (this.audioContext.state === 'suspended') {
if (import.meta.env?.DEV) console.log('[Audio] init: resume() versuchen');
await this.audioContext.resume();
if (import.meta.env?.DEV) console.log('[Audio] init: state nach resume=', this.audioContext.state);
}
const generator = new NoiseGenerator();
this.motorSound = new MotorSound(this.audioContext, generator);
await this.motorSound.init();
console.log('MotorSound initialisiert');
if (import.meta.env?.DEV) console.log('[Audio] init: MotorSound initialisiert');
// Falls bereits Bewegung anliegt, Sound direkt starten
const speedKmh = this.taxi.speed * 5;
if (speedKmh > 0) {
if (import.meta.env?.DEV) console.log('[Audio] init: direkter Start. speedKmh=', speedKmh);
this.motorSound.start();
// Sofort Parameter setzen
const speedFactor = Math.min(speedKmh / 120, 1);
@@ -338,7 +359,7 @@ export default {
this.motorSound.setVolume(volume);
}
} catch (error) {
console.warn('MotorSound konnte nicht initialisiert werden:', error);
console.warn('[Audio] init: Fehler', error);
}
},
@@ -347,15 +368,11 @@ export default {
const speedKmh = this.taxi.speed * 5; // Geschwindigkeit in km/h
const isMoving = speedKmh > 0;
if (import.meta.env?.DEV) console.log('[Audio] update:', { speedKmh, isMoving, ctx: this.audioContext?.state, playing: this.motorSound.isPlaying });
// Sicherstellen, dass der AudioContext läuft
if (this.audioContext && this.audioContext.state === 'suspended') {
this.audioContext.resume().catch(() => {});
}
if (isMoving && !this.motorSound.isPlaying) {
this.motorSound.start();
} else if (!isMoving && this.motorSound.isPlaying) {
// Start NICHT hier (kein User-Gesture). Nur Stop, wenn still.
if (!isMoving && this.motorSound.isPlaying) {
if (import.meta.env?.DEV) console.log('[Audio] update: motorSound.stop()');
this.motorSound.stop();
}
@@ -364,6 +381,7 @@ export default {
const speedFactor = Math.min(speedKmh / 120, 1); // 0-1 basierend auf 0-120 km/h
const motorSpeed = 0.3 + (speedFactor * 0.3); // 0.3 bis 1.5
const volume = 0.1 + (speedFactor * 0.4); // 0.1 bis 0.5
if (import.meta.env?.DEV) console.log('[Audio] update: params', { motorSpeed, volume });
this.motorSound.setSpeed(motorSpeed);
this.motorSound.setVolume(volume);
@@ -382,6 +400,12 @@ export default {
}
document.removeEventListener('keydown', this.handleKeyDown);
document.removeEventListener('keyup', this.handleKeyUp);
if (this.audioUnlockHandler) {
document.removeEventListener('pointerdown', this.audioUnlockHandler, { capture: true });
document.removeEventListener('touchstart', this.audioUnlockHandler, { capture: true });
document.removeEventListener('keydown', this.audioUnlockHandler, { capture: true });
this.audioUnlockHandler = null;
}
},
generateLevel() {
@@ -982,19 +1006,17 @@ export default {
async handleCanvasClick(event) {
// AudioContext bei erster Benutzerinteraktion initialisieren
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: start, hasCtx=', !!this.audioContext);
await this.initAudioOnUserInteraction();
if (this.audioContext && this.audioContext.state === 'suspended') {
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: resume()');
await this.audioContext.resume().catch(() => {});
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: state=', this.audioContext.state);
}
// Motor ggf. direkt starten
// Motor ggf. direkt starten (Klick ist User-Geste)
if (this.motorSound && !this.motorSound.isPlaying) {
if (import.meta.env?.DEV) console.log('[Audio] CanvasClick: motorSound.start()');
this.motorSound.start();
const speedKmh = this.taxi.speed * 5;
const speedFactor = Math.min(speedKmh / 120, 1);
const motorSpeed = 0.3 + (speedFactor * 0.3);
const volume = 0.1 + (speedFactor * 0.4);
this.motorSound.setSpeed(motorSpeed);
this.motorSound.setVolume(volume);
}
},
@@ -1002,14 +1024,18 @@ export default {
this.keys[event.key] = true;
// AudioContext bei erster Benutzerinteraktion initialisieren
if (import.meta.env?.DEV) console.log('[Audio] KeyDown: key=', event.key, 'hasCtx=', !!this.audioContext);
await this.initAudioOnUserInteraction();
if (this.audioContext && this.audioContext.state === 'suspended') {
if (import.meta.env?.DEV) console.log('[Audio] KeyDown: resume()');
await this.audioContext.resume().catch(() => {});
}
// Motor ggf. direkt starten
if (this.motorSound && !this.motorSound.isPlaying) {
// Bei Beschleunigungs-Key Motor starten (User-Geste garantiert)
if ((event.key === 'ArrowUp' || event.key === 'w' || event.key === 'W') && this.motorSound && !this.motorSound.isPlaying) {
if (import.meta.env?.DEV) console.log('[Audio] KeyDown: motorSound.start()');
this.motorSound.start();
const speedKmh = this.taxi.speed * 5;
// Direkt Parameter setzen, um hörbares Feedback ohne Verzögerung zu bekommen
const speedKmh = Math.max(5, this.taxi.speed * 5);
const speedFactor = Math.min(speedKmh / 120, 1);
const motorSpeed = 0.3 + (speedFactor * 0.3);
const volume = 0.1 + (speedFactor * 0.4);