Implemented sound - first rev.

This commit is contained in:
Urban Modig
2025-09-07 16:59:57 +02:00
parent fd85d8cd0f
commit c65787dfc9
30 changed files with 90 additions and 9 deletions

View File

@ -0,0 +1,5 @@
package se.urmo.game.sound;
public enum SoundEffect {
START, SIREN, MUNCH1, MUNCH2, FRUIT, GHOST_EATEN, EXTRA_LIFE, DEATH
}

View File

@ -0,0 +1,62 @@
package se.urmo.game.sound;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import java.util.EnumMap;
import java.util.Map;
public class SoundManager {
private final Map<SoundEffect, Clip> clips = new EnumMap<>(SoundEffect.class);
private SoundEffect lastMunch = SoundEffect.MUNCH1;
public SoundManager() {
load(SoundEffect.START, "/sounds/start.wav");
load(SoundEffect.SIREN, "/sounds/siren0.wav");
load(SoundEffect.MUNCH1, "/sounds/eat_dot_0_fast.wav");
load(SoundEffect.MUNCH2, "/sounds/eat_dot_1_fast.wav");
load(SoundEffect.FRUIT, "/sounds/eat_fruit.wav");
load(SoundEffect.GHOST_EATEN, "/sounds/eat_ghost.wav");
load(SoundEffect.EXTRA_LIFE, "/sounds/extend.wav");
load(SoundEffect.DEATH, "/sounds/death_0.wav");
}
private void load(SoundEffect id, String path) {
try (AudioInputStream ais = AudioSystem.getAudioInputStream(getClass().getResource(path))) {
Clip clip = AudioSystem.getClip();
clip.open(ais);
clips.put(id, clip);
} catch (Exception e) {
throw new RuntimeException("Failed to load sound: " + path, e);
}
}
public void play(SoundEffect id) {
Clip clip = clips.get(id);
if (clip == null) return;
if (clip.isRunning()) clip.stop();
clip.setFramePosition(0);
clip.start();
}
public void loop(SoundEffect id) {
Clip clip = clips.get(id);
if (clip == null) return;
if (!clip.isRunning()) {
clip.setFramePosition(0);
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
}
public void stop(SoundEffect id) {
Clip clip = clips.get(id);
if (clip != null && clip.isRunning()) clip.stop();
}
// For dot munch alternation
public void playMunch() {
lastMunch = (lastMunch == SoundEffect.MUNCH1 ? SoundEffect.MUNCH2 : SoundEffect.MUNCH1);
play(lastMunch);
}
}

View File

@ -16,6 +16,8 @@ import se.urmo.game.main.ScorePopupManager;
import se.urmo.game.map.GameMap;
import se.urmo.game.map.MapTile;
import se.urmo.game.map.TileType;
import se.urmo.game.sound.SoundEffect;
import se.urmo.game.sound.SoundManager;
import se.urmo.game.util.Direction;
import se.urmo.game.util.GameFonts;
import se.urmo.game.util.GameStateType;
@ -36,12 +38,12 @@ public class PlayingState implements GameState {
private final FruitManager fruitManager;
private final LevelManager levelManager;
private final AnimationManager animationManager;
private PacMan pacman;
private final PacMan pacman;
@Getter
private GameMap map;
private final GameMap map;
// Durations (tune to taste)
private static final int READY_MS = 1500;
private static final int READY_MS = 3000;
private static final int LEVEL_COMPLETE_MS = 1500;
private static final int LIFE_LOST_MS = 2000;
@ -51,13 +53,14 @@ public class PlayingState implements GameState {
private int dotsEaten = 0;
// Phase + timers
private RoundPhase phase = RoundPhase.PLAYING;
private RoundPhase phase = RoundPhase.READY;
private long phaseStartMs = System.currentTimeMillis();
private boolean deathInProgress;
private int frightMultiplier;
private final ScorePopupManager scorePopups = new ScorePopupManager();
private final Font scorePopupFont = GameFonts.arcade(16F); // or your arcade font
private final Font scorePopupFont = GameFonts.arcade(16F);
private final SoundManager sound = new SoundManager();
public PlayingState(GameStateManager gameStateManager, GameOverState gameOverState) {
this.gameStateManager = gameStateManager;
@ -69,6 +72,7 @@ public class PlayingState implements GameState {
this.animationManager.register(pacman);
this.ghostManager = new GhostManager(new GhostCollisionChecker(map), animationManager, levelManager);
this.fruitManager = new FruitManager(levelManager);
sound.play(SoundEffect.START);
}
@Override
@ -82,6 +86,7 @@ public class PlayingState implements GameState {
}
}
case PLAYING -> {
sound.loop(SoundEffect.SIREN);
animationManager.updateAll();
pacman.update();
ghostManager.update(pacman, map);
@ -137,9 +142,9 @@ public class PlayingState implements GameState {
boolean wasRemoved = map.removeTileImage(pacmanScreenPos);
if (wasRemoved && tile.getTileType() == TileType.LARGE_PELLET) {
ghostManager.setFrightMode();
frightMultiplier = 1;
}
if (wasRemoved) {
sound.playMunch();
dotsEaten++;
fruitManager.dotEaten(dotsEaten);
score += tile.getTileType().getScore();
@ -228,12 +233,14 @@ public class PlayingState implements GameState {
if (ghost.isEaten()) return;
if (ghost.isFrightened()) {
log.debug("Pacman eats ghost");
sound.play(SoundEffect.GHOST_EATEN);
int pts = 200 * (1 << (ghostManager.getGhosts().size() - ghostManager.isFrightened()));
score += pts;
scorePopups.spawn(pts);
ghost.setMode(GhostMode.EATEN);
} else {
log.debug("Pacman loses a life");
sound.play(SoundEffect.DEATH);
ghostManager.setFrozen(true);
pacman.setState(PacMan.PacmanState.DYING);
deathInProgress = true;

View File

@ -5,7 +5,14 @@
</encoder>
</appender>
<root level="DEBUG">
<!--root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</root -->
<root level="OFF"/>
<!-- Enable entire package -->
<logger name="se.urmo.myapp" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
</configuration>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.