added background music

This commit is contained in:
Alx-Ai 2025-06-05 08:02:33 +00:00
parent 30849a36e4
commit ad3af94f72
6 changed files with 125 additions and 12 deletions

Binary file not shown.

View file

@ -0,0 +1,69 @@
/**
* Background audio management for streaming mode
*/
let backgroundAudio: HTMLAudioElement | null = null;
let isAudioInitialized = false;
/**
* Initialize background audio for streaming
* Only loads in streaming mode to avoid unnecessary downloads
*/
export function initializeBackgroundAudio(): void {
const isStreamingMode = import.meta.env.VITE_STREAMING_MODE === 'True' || import.meta.env.VITE_STREAMING_MODE === 'true';
if (!isStreamingMode || isAudioInitialized) {
return;
}
isAudioInitialized = true;
// Create audio element
backgroundAudio = new Audio();
backgroundAudio.loop = true;
backgroundAudio.volume = 0.4; // 40% volume as requested
// For now, we'll use a placeholder - you should download and convert the wave file
// to a smaller MP3 format (aim for < 10MB) and place it in public/sounds/
backgroundAudio.src = './sounds/background_ambience.mp3';
// Handle audio loading
backgroundAudio.addEventListener('canplaythrough', () => {
console.log('Background audio loaded and ready to play');
});
backgroundAudio.addEventListener('error', (e) => {
console.error('Failed to load background audio:', e);
});
}
/**
* Start playing background audio
* Will only work after user interaction due to browser policies
*/
export function startBackgroundAudio(): void {
if (backgroundAudio && backgroundAudio.paused) {
backgroundAudio.play().catch(err => {
console.log('Background audio autoplay blocked, will retry on user interaction:', err);
});
}
}
/**
* Stop background audio
*/
export function stopBackgroundAudio(): void {
if (backgroundAudio && !backgroundAudio.paused) {
backgroundAudio.pause();
}
}
/**
* Set background audio volume
* @param volume - Volume level from 0 to 1
*/
export function setBackgroundAudioVolume(volume: number): void {
if (backgroundAudio) {
backgroundAudio.volume = Math.max(0, Math.min(1, volume));
}
}

View file

@ -404,7 +404,15 @@ function animateMessageWords(message: string, contentSpanId: string, targetPower
setTimeout(addNextWord, delay);
// Scroll to ensure newest content is visible
messagesContainer.scrollTop = messagesContainer.scrollHeight;
// Use requestAnimationFrame to batch DOM updates in streaming mode
const isStreamingModeForScroll = import.meta.env.VITE_STREAMING_MODE === 'True' || import.meta.env.VITE_STREAMING_MODE === 'true';
if (isStreamingModeForScroll) {
requestAnimationFrame(() => {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
});
} else {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
};
// Start animation

View file

@ -376,6 +376,9 @@ class GameState {
if (mapView === null) {
throw Error("Cannot find mapView element, unable to continue.")
}
const isStreamingMode = import.meta.env.VITE_STREAMING_MODE === 'True' || import.meta.env.VITE_STREAMING_MODE === 'true';
this.scene.background = new THREE.Color(0x87CEEB);
// Camera
@ -386,14 +389,27 @@ class GameState {
3000
);
this.camera.position.set(0, 800, 900); // MODIFIED: Increased z-value to account for map shift
this.renderer = new THREE.WebGLRenderer({ antialias: true });
// Renderer with streaming optimizations
this.renderer = new THREE.WebGLRenderer({
antialias: !isStreamingMode, // Disable antialiasing in streaming mode
powerPreference: "high-performance",
preserveDrawingBuffer: isStreamingMode // Required for streaming
});
this.renderer.setSize(mapView.clientWidth, mapView.clientHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
// Force lower pixel ratio for streaming to reduce GPU load
if (isStreamingMode) {
this.renderer.setPixelRatio(1);
} else {
this.renderer.setPixelRatio(window.devicePixelRatio);
}
mapView.appendChild(this.renderer.domElement);
// Controls
// Controls with streaming optimizations
this.camControls = new OrbitControls(this.camera, this.renderer.domElement);
this.camControls.enableDamping = true;
this.camControls.enableDamping = !isStreamingMode; // Disable damping for immediate response
this.camControls.dampingFactor = 0.05;
this.camControls.screenSpacePanning = true;
this.camControls.minDistance = 100;

View file

@ -14,6 +14,7 @@ import { closeTwoPowerConversation, showTwoPowerConversation } from "./component
import { PowerENUM } from "./types/map";
import { debugMenuInstance } from "./debug/debugMenu";
import { sineWave } from "./utils/timing";
import { initializeBackgroundAudio, startBackgroundAudio } from "./backgroundAudio";
//TODO: Create a function that finds a suitable unit location within a given polygon, for placing units better
// Currently the location for label, unit, and SC are all the same manually picked location
@ -24,6 +25,9 @@ const isStreamingMode = import.meta.env.VITE_STREAMING_MODE === 'True' || import
function initScene() {
gameState.createThreeScene()
// Initialize background audio for streaming mode
initializeBackgroundAudio();
// Enable audio on first user interaction (to comply with browser autoplay policies)
let audioEnabled = false;
const enableAudio = () => {
@ -35,6 +39,11 @@ function initScene() {
silentAudio.volume = 0;
silentAudio.play().catch(() => {});
// Start background audio in streaming mode
if (isStreamingMode) {
startBackgroundAudio();
}
// Remove the listener after first interaction
document.removeEventListener('click', enableAudio);
document.removeEventListener('keydown', enableAudio);
@ -78,8 +87,10 @@ function initScene() {
}
if (isStreamingMode) {
setTimeout(() => {
togglePlayback()
}, 5000) // Increased delay to 5 seconds for Chrome to stabilize
togglePlayback();
// Try to start background audio when auto-starting
startBackgroundAudio();
}, 10000) // Increased delay to 10 seconds for Chrome to fully load in Docker
}
})
}).catch(err => {
@ -191,20 +202,25 @@ function animate(currentTime: number = 0) {
}, config.effectivePlaybackSpeed);
}
// Update any pulsing or wave animations on supply centers or units
if (gameState.scene.userData.animatedObjects) {
// In streaming mode, reduce animation frequency
const isStreamingMode = import.meta.env.VITE_STREAMING_MODE === 'True' || import.meta.env.VITE_STREAMING_MODE === 'true';
const frameSkip = isStreamingMode ? 2 : 1; // Skip every other frame in streaming
if (gameState.scene.userData.animatedObjects && (Math.floor(currentTime / 16.67) % frameSkip === 0)) {
gameState.scene.userData.animatedObjects.forEach(obj => {
if (obj.userData.pulseAnimation) {
const anim = obj.userData.pulseAnimation;
// Use delta time for consistent animation speed regardless of frame rate
anim.time += anim.speed * deltaTime;
anim.time += anim.speed * deltaTime * frameSkip; // Compensate for skipped frames
if (obj.userData.glowMesh) {
const pulseValue = sineWave(config.animation.supplyPulseFrequency, anim.time, anim.intensity, 0.5);
obj.userData.glowMesh.material.opacity = 0.2 + (pulseValue * 0.3);
const scale = 1 + (pulseValue * 0.1);
obj.userData.glowMesh.scale.set(scale, scale, scale);
}
// Subtle bobbing up/down
obj.position.y = 2 + sineWave(config.animation.supplyPulseFrequency, anim.time, 0.5);
// Subtle bobbing up/down - reduce in streaming mode
const bobAmount = isStreamingMode ? 0.25 : 0.5;
obj.position.y = 2 + sineWave(config.animation.supplyPulseFrequency, anim.time, bobAmount);
}
});
}

View file

@ -31,8 +31,12 @@ while true; do
--use-fake-ui-for-media-stream \
--enable-usermedia-screen-capturing \
--enable-gpu \
--use-gl=swiftshader \
--use-gl=angle \
--use-angle=gl \
--disable-gpu-vsync \
--disable-gpu-sandbox \
--enable-accelerated-2d-canvas \
--enable-accelerated-video-decode=false \
--force-device-scale-factor=1 \
--disable-web-security \
--disable-features=VizDisplayCompositor \