remove sound
This commit is contained in:
parent
f1b511be15
commit
7cc0950a84
@ -16,7 +16,7 @@ This is a client-side JavaScript application that runs directly in the browser:
|
|||||||
The project uses an **Entity-Component-System (ECS)** pattern with event-driven communication:
|
The project uses an **Entity-Component-System (ECS)** pattern with event-driven communication:
|
||||||
|
|
||||||
- **Entities**: `Player` and `Puck` classes represent game objects
|
- **Entities**: `Player` and `Puck` classes represent game objects
|
||||||
- **Systems**: Modular systems handle specific concerns (rendering, physics, AI, rules, audio)
|
- **Systems**: Modular systems handle specific concerns (rendering, physics, AI, rules)
|
||||||
- **Game Engine**: Central orchestrator that manages all systems and entities
|
- **Game Engine**: Central orchestrator that manages all systems and entities
|
||||||
- **Game State**: Centralized state management for scores, time, penalties, etc.
|
- **Game State**: Centralized state management for scores, time, penalties, etc.
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ The application loads scripts in a specific order due to class dependencies:
|
|||||||
|
|
||||||
1. **Utilities**: `vector.js`, `physics.js` (foundational math/physics)
|
1. **Utilities**: `vector.js`, `physics.js` (foundational math/physics)
|
||||||
2. **Entities**: `player.js`, `puck.js` (game objects)
|
2. **Entities**: `player.js`, `puck.js` (game objects)
|
||||||
3. **Systems**: `renderer.js`, `physics-system.js`, `ai-system.js`, `rules-system.js`, `audio-system.js`
|
3. **Systems**: `renderer.js`, `physics-system.js`, `ai-system.js`, `rules-system.js`
|
||||||
4. **Engine**: `game-state.js`, `game-engine.js`, `main.js` (orchestration)
|
4. **Engine**: `game-state.js`, `game-engine.js`, `main.js` (orchestration)
|
||||||
|
|
||||||
### Key Classes and Responsibilities
|
### Key Classes and Responsibilities
|
||||||
@ -46,7 +46,6 @@ The engine runs at 60 FPS using `requestAnimationFrame` with these phases:
|
|||||||
3. AI decision making
|
3. AI decision making
|
||||||
4. Rules enforcement
|
4. Rules enforcement
|
||||||
5. Rendering
|
5. Rendering
|
||||||
6. Audio updates
|
|
||||||
|
|
||||||
### Hockey-Specific Features
|
### Hockey-Specific Features
|
||||||
- **12 players** (6 per team) with distinct roles: Center (C), Left/Right Wing (LW/RW), Left/Right Defense (LD/RD), Goalie (G)
|
- **12 players** (6 per team) with distinct roles: Center (C), Left/Right Wing (LW/RW), Left/Right Defense (LD/RD), Goalie (G)
|
||||||
|
|||||||
@ -35,7 +35,6 @@
|
|||||||
<div id="controls">
|
<div id="controls">
|
||||||
<button id="play-pause">Play/Pause</button>
|
<button id="play-pause">Play/Pause</button>
|
||||||
<button id="speed-control">Speed: 1x</button>
|
<button id="speed-control">Speed: 1x</button>
|
||||||
<button id="sound-toggle">Sound: ON</button>
|
|
||||||
<button id="reset-game">Reset</button>
|
<button id="reset-game">Reset</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -48,7 +47,6 @@
|
|||||||
<script src="src/systems/physics-system.js"></script>
|
<script src="src/systems/physics-system.js"></script>
|
||||||
<script src="src/systems/ai-system.js"></script>
|
<script src="src/systems/ai-system.js"></script>
|
||||||
<script src="src/systems/rules-system.js"></script>
|
<script src="src/systems/rules-system.js"></script>
|
||||||
<script src="src/systems/audio-system.js"></script>
|
|
||||||
<script src="src/engine/game-state.js"></script>
|
<script src="src/engine/game-state.js"></script>
|
||||||
<script src="src/engine/game-engine.js"></script>
|
<script src="src/engine/game-engine.js"></script>
|
||||||
<script src="src/engine/main.js"></script>
|
<script src="src/engine/main.js"></script>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ class GameEngine {
|
|||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.gameState = new GameState();
|
this.gameState = new GameState();
|
||||||
this.renderer = new Renderer(canvas);
|
this.renderer = new Renderer(canvas);
|
||||||
this.audioSystem = new AudioSystem();
|
|
||||||
|
|
||||||
this.players = [];
|
this.players = [];
|
||||||
this.puck = new Puck(500, 300);
|
this.puck = new Puck(500, 300);
|
||||||
@ -75,7 +74,6 @@ class GameEngine {
|
|||||||
startTime: Date.now()
|
startTime: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.audioSystem.onGoal(data.team);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.startFaceoff();
|
this.startFaceoff();
|
||||||
@ -90,7 +88,6 @@ class GameEngine {
|
|||||||
startTime: Date.now()
|
startTime: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.audioSystem.onSave();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gameState.on('penalty', (data) => {
|
this.gameState.on('penalty', (data) => {
|
||||||
@ -99,11 +96,9 @@ class GameEngine {
|
|||||||
penalizedPlayer.state.penaltyTime = data.duration;
|
penalizedPlayer.state.penaltyTime = data.duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.audioSystem.onPenalty();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gameState.on('period-end', () => {
|
this.gameState.on('period-end', () => {
|
||||||
this.audioSystem.onPeriodEnd();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gameState.on('faceoff-start', (data) => {
|
this.gameState.on('faceoff-start', (data) => {
|
||||||
@ -132,10 +127,6 @@ class GameEngine {
|
|||||||
e.target.textContent = `Speed: ${speeds[nextIndex]}x`;
|
e.target.textContent = `Speed: ${speeds[nextIndex]}x`;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('sound-toggle').addEventListener('click', (e) => {
|
|
||||||
const enabled = this.audioSystem.toggle();
|
|
||||||
e.target.textContent = `Sound: ${enabled ? 'ON' : 'OFF'}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('reset-game').addEventListener('click', () => {
|
document.getElementById('reset-game').addEventListener('click', () => {
|
||||||
this.resetGame();
|
this.resetGame();
|
||||||
|
|||||||
@ -1,245 +0,0 @@
|
|||||||
class AudioSystem {
|
|
||||||
constructor() {
|
|
||||||
this.audioContext = null;
|
|
||||||
this.sounds = {};
|
|
||||||
this.volume = 0.7;
|
|
||||||
this.enabled = true;
|
|
||||||
this.initialized = false;
|
|
||||||
|
|
||||||
this.initializeAudio();
|
|
||||||
}
|
|
||||||
|
|
||||||
async initializeAudio() {
|
|
||||||
try {
|
|
||||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
||||||
this.generateSounds();
|
|
||||||
this.initialized = true;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Audio initialization failed:', error);
|
|
||||||
this.enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generateSounds() {
|
|
||||||
this.sounds = {
|
|
||||||
puckHit: this.createPuckHitSound(),
|
|
||||||
stick: this.createStickSound(),
|
|
||||||
goal: this.createGoalSound(),
|
|
||||||
save: this.createSaveSound(),
|
|
||||||
whistle: this.createWhistleSound(),
|
|
||||||
crowd: this.createCrowdSound(),
|
|
||||||
skate: this.createSkateSound(),
|
|
||||||
board: this.createBoardSound()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
createPuckHitSound() {
|
|
||||||
return this.createPercussiveSound(150, 0.1, 'sawtooth');
|
|
||||||
}
|
|
||||||
|
|
||||||
createStickSound() {
|
|
||||||
return this.createPercussiveSound(200, 0.05, 'square');
|
|
||||||
}
|
|
||||||
|
|
||||||
createGoalSound() {
|
|
||||||
return this.createMelodicSound([400, 500, 600], 0.8, 'sine');
|
|
||||||
}
|
|
||||||
|
|
||||||
createSaveSound() {
|
|
||||||
return this.createPercussiveSound(100, 0.3, 'triangle');
|
|
||||||
}
|
|
||||||
|
|
||||||
createWhistleSound() {
|
|
||||||
return this.createMelodicSound([800, 900], 0.5, 'sine');
|
|
||||||
}
|
|
||||||
|
|
||||||
createCrowdSound() {
|
|
||||||
return this.createNoiseSound(0.2, 2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
createSkateSound() {
|
|
||||||
return this.createNoiseSound(0.1, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
createBoardSound() {
|
|
||||||
return this.createPercussiveSound(80, 0.2, 'triangle');
|
|
||||||
}
|
|
||||||
|
|
||||||
createPercussiveSound(frequency, duration, waveType = 'sine') {
|
|
||||||
return () => {
|
|
||||||
if (!this.enabled || !this.audioContext) return;
|
|
||||||
|
|
||||||
const oscillator = this.audioContext.createOscillator();
|
|
||||||
const gainNode = this.audioContext.createGain();
|
|
||||||
|
|
||||||
oscillator.connect(gainNode);
|
|
||||||
gainNode.connect(this.audioContext.destination);
|
|
||||||
|
|
||||||
oscillator.type = waveType;
|
|
||||||
oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime);
|
|
||||||
oscillator.frequency.exponentialRampToValueAtTime(
|
|
||||||
frequency * 0.5,
|
|
||||||
this.audioContext.currentTime + duration
|
|
||||||
);
|
|
||||||
|
|
||||||
gainNode.gain.setValueAtTime(this.volume * 0.3, this.audioContext.currentTime);
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(
|
|
||||||
0.001,
|
|
||||||
this.audioContext.currentTime + duration
|
|
||||||
);
|
|
||||||
|
|
||||||
oscillator.start(this.audioContext.currentTime);
|
|
||||||
oscillator.stop(this.audioContext.currentTime + duration);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
createMelodicSound(frequencies, duration, waveType = 'sine') {
|
|
||||||
return () => {
|
|
||||||
if (!this.enabled || !this.audioContext) return;
|
|
||||||
|
|
||||||
frequencies.forEach((freq, index) => {
|
|
||||||
const delay = index * 0.1;
|
|
||||||
const oscillator = this.audioContext.createOscillator();
|
|
||||||
const gainNode = this.audioContext.createGain();
|
|
||||||
|
|
||||||
oscillator.connect(gainNode);
|
|
||||||
gainNode.connect(this.audioContext.destination);
|
|
||||||
|
|
||||||
oscillator.type = waveType;
|
|
||||||
oscillator.frequency.setValueAtTime(freq, this.audioContext.currentTime + delay);
|
|
||||||
|
|
||||||
gainNode.gain.setValueAtTime(0, this.audioContext.currentTime + delay);
|
|
||||||
gainNode.gain.linearRampToValueAtTime(
|
|
||||||
this.volume * 0.2,
|
|
||||||
this.audioContext.currentTime + delay + 0.05
|
|
||||||
);
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(
|
|
||||||
0.001,
|
|
||||||
this.audioContext.currentTime + delay + duration
|
|
||||||
);
|
|
||||||
|
|
||||||
oscillator.start(this.audioContext.currentTime + delay);
|
|
||||||
oscillator.stop(this.audioContext.currentTime + delay + duration);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
createNoiseSound(volume, duration) {
|
|
||||||
return () => {
|
|
||||||
if (!this.enabled || !this.audioContext) return;
|
|
||||||
|
|
||||||
const bufferSize = this.audioContext.sampleRate * duration;
|
|
||||||
const buffer = this.audioContext.createBuffer(1, bufferSize, this.audioContext.sampleRate);
|
|
||||||
const data = buffer.getChannelData(0);
|
|
||||||
|
|
||||||
for (let i = 0; i < bufferSize; i++) {
|
|
||||||
data[i] = (Math.random() * 2 - 1) * volume * this.volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
const source = this.audioContext.createBufferSource();
|
|
||||||
const gainNode = this.audioContext.createGain();
|
|
||||||
const filter = this.audioContext.createBiquadFilter();
|
|
||||||
|
|
||||||
source.buffer = buffer;
|
|
||||||
source.connect(filter);
|
|
||||||
filter.connect(gainNode);
|
|
||||||
gainNode.connect(this.audioContext.destination);
|
|
||||||
|
|
||||||
filter.type = 'lowpass';
|
|
||||||
filter.frequency.setValueAtTime(2000, this.audioContext.currentTime);
|
|
||||||
|
|
||||||
gainNode.gain.setValueAtTime(1, this.audioContext.currentTime);
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration);
|
|
||||||
|
|
||||||
source.start(this.audioContext.currentTime);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
playSound(soundName, volume = 1) {
|
|
||||||
if (!this.enabled || !this.sounds[soundName]) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.audioContext.state === 'suspended') {
|
|
||||||
this.audioContext.resume();
|
|
||||||
}
|
|
||||||
this.sounds[soundName]();
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Failed to play sound:', soundName, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPuckHit(velocity) {
|
|
||||||
const intensity = Math.min(1, velocity / 500);
|
|
||||||
if (intensity > 0.1) {
|
|
||||||
this.playSound('puckHit', intensity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onStickContact() {
|
|
||||||
this.playSound('stick');
|
|
||||||
}
|
|
||||||
|
|
||||||
onGoal(team) {
|
|
||||||
this.playSound('goal');
|
|
||||||
setTimeout(() => this.playSound('crowd'), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSave() {
|
|
||||||
this.playSound('save');
|
|
||||||
}
|
|
||||||
|
|
||||||
onPenalty() {
|
|
||||||
this.playSound('whistle');
|
|
||||||
}
|
|
||||||
|
|
||||||
onPeriodEnd() {
|
|
||||||
this.playSound('whistle');
|
|
||||||
}
|
|
||||||
|
|
||||||
onBoardCollision(velocity) {
|
|
||||||
const intensity = Math.min(1, velocity / 300);
|
|
||||||
if (intensity > 0.2) {
|
|
||||||
this.playSound('board', intensity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPlayerMovement(speed) {
|
|
||||||
if (speed > 100 && Math.random() < 0.01) {
|
|
||||||
this.playSound('skate', 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setVolume(volume) {
|
|
||||||
this.volume = Math.max(0, Math.min(1, volume));
|
|
||||||
}
|
|
||||||
|
|
||||||
setEnabled(enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
this.enabled = !this.enabled;
|
|
||||||
return this.enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
resume() {
|
|
||||||
if (this.audioContext && this.audioContext.state === 'suspended') {
|
|
||||||
return this.audioContext.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend() {
|
|
||||||
if (this.audioContext && this.audioContext.state === 'running') {
|
|
||||||
return this.audioContext.suspend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getState() {
|
|
||||||
return {
|
|
||||||
enabled: this.enabled,
|
|
||||||
volume: this.volume,
|
|
||||||
initialized: this.initialized,
|
|
||||||
contextState: this.audioContext ? this.audioContext.state : 'not-initialized'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user