隆隆的雷声

This commit is contained in:
2026-03-26 13:08:33 +08:00
parent c8dfb03362
commit 299d9d4d82
2 changed files with 68 additions and 0 deletions

Binary file not shown.

View File

@@ -10,6 +10,7 @@ import { TerrainGenerator } from './TerrainGenerator.js';
import { VegetationSystem } from './VegetationSystem.js';
const RAIN_AUDIO_URL = '/audio/rain-calming.mp3';
const THUNDER_AUDIO_URL = '/audio/thunder-distant.mp3';
export class OceanScene {
constructor(container) {
@@ -41,6 +42,9 @@ export class OceanScene {
this.horizonFog = null;
this.skyHazeBand = null;
this.rainAudio = null;
this.thunderAudioPool = [];
this.thunderAudioIndex = 0;
this.scheduledThunder = [];
this.params = {
elevation: 2,
@@ -192,6 +196,13 @@ export class OceanScene {
this.rainAudio.preload = 'auto';
this.rainAudio.volume = this.params.rainAudioVolume;
this.rainAudio.crossOrigin = 'anonymous';
this.thunderAudioPool = Array.from({ length: 3 }, () => {
const audio = new Audio(THUNDER_AUDIO_URL);
audio.preload = 'auto';
audio.crossOrigin = 'anonymous';
return audio;
});
}
createRainShader() {
@@ -1108,7 +1119,10 @@ export class OceanScene {
this.lightningFlash = 0;
this.lightningBurstEnd = 0;
this.nextLightningAt = 0;
this.lightningPulseSchedule = [];
this.scheduledThunder = [];
this.applyLightningState(0);
this.stopThunderAudio();
}
}
@@ -1152,6 +1166,59 @@ export class OceanScene {
const size = THREE.MathUtils.randFloat(720, 1480);
this.lightningCloudGlow.scale.set(size, size * THREE.MathUtils.randFloat(0.72, 1.08), 1);
}
this.scheduleThunderBurst(flashX, flashY, flashZ);
}
scheduleThunderBurst(flashX, flashY, flashZ) {
const distanceNorm = THREE.MathUtils.clamp(
(Math.abs(flashX) / 1600) * 0.35 + (Math.abs(flashZ) / 1800) * 0.65,
0,
1
);
const delay = THREE.MathUtils.lerp(0.65, 2.4, distanceNorm) + Math.random() * 0.45;
const volume = this.params.lightningIntensity * THREE.MathUtils.lerp(0.9, 0.42, distanceNorm);
this.scheduledThunder.push({
playAt: this.lightningBurstEnd + delay,
volume,
playbackRate: THREE.MathUtils.randFloat(0.94, 1.03)
});
}
stopThunderAudio() {
for (const audio of this.thunderAudioPool) {
audio.pause();
audio.currentTime = 0;
}
}
playThunder(volume, playbackRate) {
if (!this.params.lightningEnabled || this.thunderAudioPool.length === 0) return;
const audio = this.thunderAudioPool[this.thunderAudioIndex % this.thunderAudioPool.length];
this.thunderAudioIndex += 1;
audio.pause();
audio.currentTime = 0;
audio.volume = THREE.MathUtils.clamp(volume, 0, 1);
audio.playbackRate = playbackRate;
const playPromise = audio.play();
if (playPromise?.catch) {
playPromise.catch(() => {});
}
}
updateThunder(time) {
if (!this.params.lightningEnabled || this.scheduledThunder.length === 0) return;
const pending = [];
for (const thunder of this.scheduledThunder) {
if (time >= thunder.playAt) {
this.playThunder(thunder.volume, thunder.playbackRate);
} else {
pending.push(thunder);
}
}
this.scheduledThunder = pending;
}
updateLightning(time) {
@@ -1293,6 +1360,7 @@ export class OceanScene {
const time = this.clock.getElapsedTime();
this.updateLightning(time);
this.updateThunder(time);
if (this.water) {
this.water.material.uniforms['time'].value += 1.0 / 60.0;