#java #multithreading #audio
#java #многопоточность #Аудио
Вопрос:
Я создал простую игру snake, в которой я реализовал звуковые эффекты и немного фоновой музыки.
Я создал класс «SoundFX» для обработки звуков, два экземпляра SoundFX воспроизводят GameOver и appleGet FX, но класс SoundFX также расширяет поток, чтобы зацикливать фоновую музыку во время воспроизведения игры.
Я хочу, чтобы эта музыка останавливалась, когда игрок нажимает «game over», а затем снова запускал игру, как только игрок нажимал клавишу пробела.
Я пытаюсь использовать ожидание и уведомление, чтобы избежать ожидания ожидания фоновой музыки. поток.
Однако я не могу понять, как это сделать, не оставляя мой фоновый музыкальный поток в состоянии ожидания или не нажимая исключение «Поток не является текущим владельцем».
Я совсем новичок в потоках, и, несмотря на учебные пособия YouTube и чтение в «Head First Java», я не могу понять, как применить это к этому контексту.
public class SoundFX extends Thread{
private final String soundName;
private volatile boolean isOn;
private volatile boolean isFailedGame = false;
public SoundFX(String soundName) {
this.soundName = soundName;
}
public SoundFX(String soundName, boolean isOn) {
this.soundName = soundName;
this.isOn = isOn;
}
public void setOn(boolean on) {
isOn = on;
}
public void setFailedGame(boolean failed){
isFailedGame = failed;
}
public void playSound(String soundName){
File soundFile = new File(soundName);
try{
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(soundFile));
clip.start();
} catch (Exception e){
e.printStackTrace();
}
}
@Override
public synchronized void run() {
File soundFile = new File(soundName);
try{
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(soundFile));
while (Thread.currentThread().isAlive()) {
if (!isOn) {
clip.stop();
if(isFailedGame) {
clip.setFramePosition(0);
}
//Thread.onSpinWait();
this.wait();
}
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
} catch (Exception e){
e.printStackTrace();
}
}
}
public class Gameplay extends JPanel implements KeyListener, ActionListener {
//region SoundFX
private String appleGetSound = "/Users/Niklas/Downloads/JavaFontysICT-master/2D-Snake-AwaisMirzaTutorial/AppleGet.wav";
private String failSound = "/Users/Niklas/Downloads/JavaFontysICT-master/2D-Snake-AwaisMirzaTutorial/SnakeFail.wav";
private String backgroundSong = "/Users/Niklas/Downloads/JavaFontysICT-master/2D-Snake-AwaisMirzaTutorial/SnakeSongLoop.wav";
SoundFX appleGetSoundFX = new SoundFX(appleGetSound);
SoundFX failedSoundFX = new SoundFX(failSound);
SoundFX backgroundSongLoop = new SoundFX(backgroundSong, false);
//endregion
private boolean paused = false;
private boolean failed = false;
public Gameplay() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
//the speed of the snake
timer = new Timer(clockSpeed, this);
backgroundSongLoop.start();
timer.start();
}
public synchronized void paint(Graphics g){
//region Default position
if(moves==0){
backgroundSongLoop.setOn(true);
snakeXLength[2] = 50;
snakeXLength[1] = 75;
snakeXLength[0] = 100;
snakeYLength[2] = 100;
snakeYLength[1] = 100;
snakeYLength[0] = 100;
failed=false;
}
//endregion
//region Snake-Snake Collision
for (int b=1; b<lengthOfSnake; b ){
if(snakeXLength[b]==snakeXLength[0] amp;amp; snakeYLength[b]==snakeYLength[0]){
backgroundSongLoop.setOn(false);
backgroundSongLoop.setFailedGame(true);
System.out.println(Thread.currentThread());
failedSoundFX.playSound(failSound);
failed=true;
timer.stop();
//region High score manager
if (score>highscores[2] amp;amp; score<highscores[1]){
highscores[2]=score;
}
if (score>highscores[1] amp;amp; score<highscores[0]){
highscores[2]=highscores[1];
highscores[1]=score;
}
if (score > highscores[0]){
highscores[2]=highscores[1];
highscores[1]=highscores[0];
highscores[0] = score;
}
//endregion
g.setColor(Color.BLACK);
g.fillRect(25, 75, 850, 575);
g.setColor(Color.WHITE);
g.setFont(new Font("arial", Font.BOLD, 50));
g.drawString("GAME OVER", 300, 300);
g.setFont(new Font("arial", Font.BOLD, 20));
g.drawString("Press [Space Bar] to play again", 300, 340);
g.drawString("HIGHSCORES:", 380, 380);
g.drawString("#1: " highscores[0], 380, 400);
if (highscores[1]>0){
g.drawString("#2: " highscores[1], 380, 420);
}
if (highscores[2]>0) {
g.drawString("#3: " highscores[2], 380, 440);
}
}
}
//endregion
g.dispose();
}
@Override
public void keyPressed(KeyEvent e) {
//region restart function
if (failed){
if (e.getKeyCode()==KeyEvent.VK_SPACE) {
backgroundSongLoop.setOn(true);
backgroundSongLoop.setFailedGame(false);
score = 0;
lengthOfSnake = 3;
moves = 0;
repaint();
timer.start();
}
}
//endregion
// region pause function
if (e.getKeyCode()==KeyEvent.VK_ESCAPE amp;amp; !paused amp;amp; !failed){
backgroundSongLoop.setOn(false);
paused=true;
} else if (e.getKeyCode()==KeyEvent.VK_ESCAPE amp;amp; paused amp;amp; !failed){
paused=false;
backgroundSongLoop.setOn(true);
timer.start();
}
//endregion
}
}
Ответ №1:
Я рекомендую удалить Thread
расширение из SoundFX
. Просто создайте Clip
переменную экземпляра в своем SoundFX
классе. Создайте отдельные методы для инициализации (конструктор отлично подходит для этого), воспроизведения и остановки воспроизведения клипа. Метод воспроизведения (для непрерывного цикла) будет следующим:
public void play() {
clip.setFramePosition(0);
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
Этот setFramePosition(0)
метод не является строго необходимым. Это сделано для того, чтобы гарантировать, что если вы остановите свой клип и захотите возобновить воспроизведение, воспроизведение начнется с начала звука.
Когда вы создаете экземпляр своего clip в конструкторе, я рекомендую использовать строку, которая позволяет вам получить a URL
с помощью getResource
метода, и использовать URL
для создания вашего AudioInputStream
. Это избавит вас от множества головных болей, когда придет время запускать вашу программу.
Нет необходимости совмещать открытие звукового файла с его запуском. На самом Clip
деле он был разработан для инициализации, открытия и хранения в памяти до момента, когда потребуется воспроизведение. Он также был разработан для остановки, изменения положения и перезапуска. Если аудиофайл слишком велик для хранения в памяти, SourceDataLine
обычно предпочтительнее a Clip
.