隆隆的雷声
This commit is contained in:
BIN
public/audio/thunder-distant.mp3
Normal file
BIN
public/audio/thunder-distant.mp3
Normal file
Binary file not shown.
@@ -10,6 +10,7 @@ import { TerrainGenerator } from './TerrainGenerator.js';
|
|||||||
import { VegetationSystem } from './VegetationSystem.js';
|
import { VegetationSystem } from './VegetationSystem.js';
|
||||||
|
|
||||||
const RAIN_AUDIO_URL = '/audio/rain-calming.mp3';
|
const RAIN_AUDIO_URL = '/audio/rain-calming.mp3';
|
||||||
|
const THUNDER_AUDIO_URL = '/audio/thunder-distant.mp3';
|
||||||
|
|
||||||
export class OceanScene {
|
export class OceanScene {
|
||||||
constructor(container) {
|
constructor(container) {
|
||||||
@@ -41,6 +42,9 @@ export class OceanScene {
|
|||||||
this.horizonFog = null;
|
this.horizonFog = null;
|
||||||
this.skyHazeBand = null;
|
this.skyHazeBand = null;
|
||||||
this.rainAudio = null;
|
this.rainAudio = null;
|
||||||
|
this.thunderAudioPool = [];
|
||||||
|
this.thunderAudioIndex = 0;
|
||||||
|
this.scheduledThunder = [];
|
||||||
|
|
||||||
this.params = {
|
this.params = {
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
@@ -192,6 +196,13 @@ export class OceanScene {
|
|||||||
this.rainAudio.preload = 'auto';
|
this.rainAudio.preload = 'auto';
|
||||||
this.rainAudio.volume = this.params.rainAudioVolume;
|
this.rainAudio.volume = this.params.rainAudioVolume;
|
||||||
this.rainAudio.crossOrigin = 'anonymous';
|
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() {
|
createRainShader() {
|
||||||
@@ -1108,7 +1119,10 @@ export class OceanScene {
|
|||||||
this.lightningFlash = 0;
|
this.lightningFlash = 0;
|
||||||
this.lightningBurstEnd = 0;
|
this.lightningBurstEnd = 0;
|
||||||
this.nextLightningAt = 0;
|
this.nextLightningAt = 0;
|
||||||
|
this.lightningPulseSchedule = [];
|
||||||
|
this.scheduledThunder = [];
|
||||||
this.applyLightningState(0);
|
this.applyLightningState(0);
|
||||||
|
this.stopThunderAudio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1152,6 +1166,59 @@ export class OceanScene {
|
|||||||
const size = THREE.MathUtils.randFloat(720, 1480);
|
const size = THREE.MathUtils.randFloat(720, 1480);
|
||||||
this.lightningCloudGlow.scale.set(size, size * THREE.MathUtils.randFloat(0.72, 1.08), 1);
|
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) {
|
updateLightning(time) {
|
||||||
@@ -1293,6 +1360,7 @@ export class OceanScene {
|
|||||||
|
|
||||||
const time = this.clock.getElapsedTime();
|
const time = this.clock.getElapsedTime();
|
||||||
this.updateLightning(time);
|
this.updateLightning(time);
|
||||||
|
this.updateThunder(time);
|
||||||
|
|
||||||
if (this.water) {
|
if (this.water) {
|
||||||
this.water.material.uniforms['time'].value += 1.0 / 60.0;
|
this.water.material.uniforms['time'].value += 1.0 / 60.0;
|
||||||
|
|||||||
Reference in New Issue
Block a user