Background music thread dying at game over rather than pausing - java

I'm working on a snake clone project and am having trouble implementing a feature where the background music stops only during my game over screen.
My music plays during the first round of gameplay but won't start up again after I press the reset key for the game. I've debugged as far as to see that the thread I created to loop the music dies at game-over, and I'm not sure how to fix this.
SoundFX backgroundSongLoop = new SoundFX(backgroundSong, true);
public Gameplay() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
//the speed of the snake
timer = new Timer(clockSpeed, this);
backgroundSongLoop.start();
timer.start();
}
public void paint(Graphics g){
//region Snake-Snake Collision
for (int b=1; b<lengthOfSnake; b++){
if(snakeXLength[b]==snakeXLength[0] && snakeYLength[b]==snakeYLength[0]){
backgroundSongLoop.setOn(false);
failedSoundFX.playSound(failSound);
failed=true;
timer.stop(); }
//region restart function
if (failed){
if (e.getKeyCode()==KeyEvent.VK_SPACE) {
backgroundSongLoop.setOn(true);
score = 0;
lengthOfSnake = 3;
moves = 0;
repaint();
timer.start();
}
}
//endregion
public class SoundFX extends Thread{
private final String soundName;
public boolean isOn;
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 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 void run() {
//public void loopSong(String soundName, boolean playing){
File soundFile = new File(soundName);
try{
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(soundFile));
while (isOn){
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
while (!isOn){
clip.stop();
}
} catch (Exception e){
e.printStackTrace();
}
}
}

Two things:
Use clip.setFramePosition(0) to position the clip for restarting at the beginning.
Put the clip.open() method in a constructor and make clip an instance variable. That way, when you wish to restart it, only the following is needed:
public void play() {
clip.setFramePosition(0);
clip.loop();
}
By doing this, you avoid reloading from the file location every time you wish to play the cue. The cue won't even start until all the file operations are done, so this method gives you a much quicker start.
I think if you do this, you can also write a method that calls clip.stop() and just call all of these methods directly on your SoundFX class without needing to extend Thread.

Related

How to use a .mp3 file in java?

I'm using java to try and make an alarm of sorts. I want to input a time and then when it hits zero, play a specific .mp3. The countdown works fine, but when it hits zero and tries to play the mp3, it can't find the file. Advice? I've already searched the site and can't find anything simple enough for me to use (I'm a beginner). Here's what I got so far:
public static void main(String[] args) {
int timer;
File song = new File("C:/Users/N/Documents/Random/Handies/Proj/Song.mp3");
timer = 1;
timer*=1;
while (timer != 0)
{
try
{
Thread.sleep(1000);
} catch (InterruptedException e) {}
timer -= 1;
System.out.println(timer);
}
if (timer == 0)
Play(song);
}
public static void Play(File song)
{
try
{
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(song));
clip.start();
}
catch (Exception exc)
{
exc.printStackTrace(System.out);
}
}
With JavaFX you can easily play the MP3 like this:
public class Test extends javafx.application.Application {
#Override
public void start(Stage primaryStage) throws Exception {
File file = new File("/path/test.mp3");
Media media = new Media(file.toURI().toString());
MediaPlayer mediaPlayer = new MediaPlayer(media);
mediaPlayer.play();
}
}
I usually find it hard using AudioSystem for this kind of thing.

Infinite running of game

I am trying to make a small 2D java game by following this YouTube Guide.
I am not following it 100% but sort of along those lines. When I try to run my program, it opens infinitely and does not stop opening. I have turned it into a thread(??) and added starts and stops but it does not seem to work. Any ideas?
public class game implements Runnable {
private Thread thread;
private boolean running = false;
private BufferStrategy bs;
private Graphics g;
public game(){
}
private void init(){
new frame();
}
private void update(){
}
private void render(){
frame frame = new frame();
bs = frame.getCanvas().getBufferStrategy();
if (bs == null){
frame.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
}
public void run() {
init();
while(running){
update();
render();
}
stop();
}
public synchronized void start(){
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
It runs off a Launcher class like so.
public static void main(String[] args) {
game game = new game();
game.start();
}
I can ive the full code if need be. I have tried asking the creator and looking at his source code but everything seems to be identical (apart from the display deviations I have made to alter my game from his.)
TL:DR Game opens infinite frames and eventually crashes.
Had to make getCanvas() method static and it worked. Simple.

BufferStrategy don't solve flickering

I'm trying to understand how BufferStrategy is working. I've made a simple app that is drawing some Sprite objects over and over again every 60 frames per second. I can see the images on the canvas but they are flickering for some reason. Can you tell me why? If you don't want to read all the code, just focus on the Paint method and the main game loop.
public abstract class Frame extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;
//------------------------Variables------------------------//
private boolean initialized = false;
private boolean fullScreen = false;
public boolean running = true;
private GraphicsDevice vc;
private BufferStrategy strategy;
private Graphics2D g2d;
private int timer = 0;
//------------------------Variables------------------------//
public final void __init__() {
this.addKeyListener(this); //Adding key listener.
this.setVisible(true);
this.setIgnoreRepaint(true);
this.createBufferStrategy(2);
this.strategy = this.getBufferStrategy();
this.setResizable(false);
this.initialized = true; //Initialized.
}
//Create a window.
public final void set_mode(int width, int height, boolean fullScreen) throws NotInitializedException {
//Frame not initialized.
if (!this.initialized) {
throw new NotInitializedException("Frame not initialized!");
} else {
//--------------------Variables--------------------//
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
//--------------------Variables--------------------//
//Setting vc equal to the default graphics device of the system.
this.vc = env.getDefaultScreenDevice();
//Full Screen.
if (fullScreen) {
this.fullScreen = fullScreen;
//Creating the display mode.
DisplayMode mode = new DisplayMode(width, height, 32, DisplayMode.REFRESH_RATE_UNKNOWN);
//If display settings are allow to change display mode.
if (this.vc.isDisplayChangeSupported()) {
this.vc.setDisplayMode(mode); //Change to the new mode.
}
//Set the screen to full screen.
this.vc.setFullScreenWindow(this);
} //Not full screen.
else {
this.setSize(width, height);
this.addWindowListener(new WindowHandler(this));
}
}
}
//This mehod is been called from Sprite.draw() method.
public void paint(Sprite sprite) {
do {
do {
this.g2d = (Graphics2D) this.strategy.getDrawGraphics();
g2d.drawImage(sprite.getImage(), sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight(), null);
this.g2d.dispose();
} while (this.strategy.contentsRestored());
this.strategy.show();
} while (this.strategy.contentsLost());
}
public final int tick(int fps) {
int ms = 1000 / fps;
timer += 1;
//Try to sleep.
try {
Thread.sleep(ms);
} //Thread interrupted.
catch (Exception e) {
System.out.println(e.getMessage());
System.exit(0);
}
return timer;
}
public class MyApp extends Frame {
public static String BG_PATH = "C:/Users/admin/Desktop/game/bg.jpg";
public static String PL_PATH = "C:/Users/admin/Desktop/game/player.png";
public static String EN_PATH = "C:/Users/admin/Desktop/game/enemy.png";
private int speed = 20;
private boolean left = false;
private boolean right = false;
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = true;
} else if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {
this.close();
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
if (arg0.getKeyCode() == KeyEvent.VK_LEFT) {
this.left = false;
} else if (arg0.getKeyCode() == KeyEvent.VK_RIGHT) {
this.right = false;
}
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void onWindowClose() {
// TODO Auto-generated method stub
}
//This method starts the game.
public void startApp() {
this.__init__(); //initialize the frame.
Sprite bg = new Sprite(this, Picture.load(BG_PATH), "bg"); //Create a new sprite obj
this.set_mode(bg.getWidth() - 500, bg.getHeight() - 100, false); //Create the window.
Sprite player = new Sprite(this, Picture.load(PL_PATH), "player");
player.setX(bg.getWidth() / 3);
player.setY(bg.getHeight() / 2);
//Game Main Loop
while (this.running) {
bg.draw();
player.draw();
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
}
}
}
}
You have a few issues that I can clearly spot.
First off, you must understand that drawing in Swing/Awt is not known for it's speed, it's actually known for the exact opposite. The fact is, even though you're telling your game to run at 60fps, it probably can't do it. Thus the flickering. Essentially, your application is caught within a "drawing-data race", and it's always slightly behind. Try something real quick; set Thead.Sleep() to 10 or 30. I feel as though that might solve your problem entirely.
If not, consider the second problem. You're calling this.strategy.show(); inside the player.Draw(); function, when it needs to be the last thing that you do in your draw call. In other words:
//Game Main Loop
while (this.running) {
bg.draw(); // DON'T SWAP BUFFERS!
player.draw(); // DON'T SWAP BUFFERS!
// draw other entities
player.moveHorizontal(left, right, speed); //Dont worry about this line.
this.tick(50);
this.strategy.show(); // This needs to be the very last thing you do. You're swapping buffers here, which only needs to be done once per frame.
}
My guess is you're also swapping your buffer during the bg.Draw(); function as well, and that is actually why your screen is flickering. So those are two things right there. Try lowering the frames per second down to something that Java can actually handle, and don't swap your buffer until the VERY end of your drawing routine.
Some other recommendations:
Use direct variable access instead of getters and setters. There is overhead when calling "player.getX()". When you could just call "player.x".
There is no future in Java 2D game development. Swing/AWT(which you're using) is entirely dead. If you want to make a game, and be serious about it, take the time to learn OpenGL(In your case it would be Lwjgl as a wrapper).

How to delay a board game between moves so a sound can play

In the board game I am designing, each user has a grid with 100 cells. The user clicks a cell in the computer's grid, which makes a sound and changes to a different colour. The computer then automatically clicks a cell in the user's grid, which makes a sound and changes to a different colour.
The user's clicks are dealt with via the MouseListener (and the MouseClicked method). Currently, the last thing the method does is call the computerMove() method. This method executes and performs the computer move before giving changing the current player back to human. The game proceeds when the human makes another mouse click.
Ideally, I'd like to have a pause (perhaps a second) between each player move. However, due to the fact the computerMove method is being called inside the MouseClicked method, this is proving troublesome. By using Thread.sleep and even a TimerTask, the best I can do is to slow the human player's move down. However, as soon as the player makes a mouse click, the computer instantaneously responds.
Does anybody have any suggestions as to how I could implement a delay? Do I need to change how I am calling the methods?
ComputerMove method:
public void computerMove() {
if (currentTurn == computer) {
int xPos = randomGenerator.nextInt(10);
int yPos = randomGenerator.nextInt(10);
LogicCell attackedCell = new LogicCell(xPos, yPos);
playerGridLogic.addCellsThatHaveBeenAttacked(attackedCell);
if (playerGridLogic.getCellsWithShips().contains(attackedCell)) {
playerGrid.getCellArray()[xPos][yPos].setBackground(Color.ORANGE);
hitSound();
}
else {
playerGrid.getCellArray()[xPos][yPos].setBackground(Color.MAGENTA);
missSound();
}
nextTurn();
}
}
Corresponding MouseClick method:
#Override
public void mouseClicked(MouseEvent e) {
if (allComputerShipsPlaced && currentTurn == human) {
ViewCell currentCell = (ViewCell) e.getSource();
xPos = currentCell.getXPos();
yPos = currentCell.getYPos();
LogicCell attackedCell = new LogicCell(xPos, yPos);
computerGridLogic.addCellsThatHaveBeenAttacked(attackedCell);
if (computerGridLogic.getCellsWithShips().contains(attackedCell)) {
cellArray[xPos][yPos].setBackground(Color.ORANGE);
hitSound();
}
else {
cellArray[xPos][yPos].setBackground(Color.MAGENTA);
missSound();
}
nextTurn();
computerMove();
}
}
missSound method() (hitSound is very similar):
public static void missSound() {
try {
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(new File("water-splash.wav")));
clip.start();
}
catch (Exception exc)
{
exc.printStackTrace(System.out);
}
}
Edit: I've tried changing the sound classes to this, but to no avail:
public static void hitSound()
{
try
{
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(new File("bomb-explosion.wav")));
clip.start();
LineListener listener = new LineListener() {
public void update(LineEvent event) {
if (event.getType() != Type.STOP) {
return;
}
try {
queue.take();
} catch (InterruptedException e) {
//ignore this
}
}
};
clip.addLineListener(listener);
}
catch (Exception exc)
{
exc.printStackTrace(System.out);
}
}
You should be able to add a listener and react to the end of the sound.
See this question: how can I wait for a java sound clip to finish playing back?
This works for me:
public class Test {
public static void main(String[] args) {
final Object foo = new Object();
Clip clip;
try {
clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(new File("go.wav")));
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if(event.getType() == LineEvent.Type.STOP){
System.out.println("done playing");
synchronized(foo) {
foo.notify();
}
}
}
});
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
synchronized(foo) {
try {
foo.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I would consider calling computerMove inside nextTurn, and do the waiting inside nextTurn.
You could use Thread.sleep in computerMove() to pause the execution. You can use clip.getMicrosecondsLength to get the length of the sound effect in microseconds. Multiply it with 1000 and pass it to sleep to wait for the sound effect to finish.
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(new File("water-splash.wav")));
clip.start();
int lengthInMilliseconds = clip.getMicrosecondLength() * 1000;
Thread.sleep(lengthInMilliseconds);

Change volume java using jlayer?

I've searched and read another topics but i still can't solve my problems. I had one java main class and one java jframe. I wanna add jslider to change volume in my mp3 player. What should i do?
my_player2_func
class my_player2_func{
static Player player;
static void play() {
try {
FileInputStream fe = new FileInputStream(my_player2_main.str);
player = new Player(fe);
} catch (Exception ex) {
System.out.println(ex);
}
new Thread(new Runnable() {
#Override
public void run() {
try {
player.play();
} catch (Exception ex) {
System.out.println(ex);
}
}
}).start();
}
static void stop() {
if (player != null)
player.close();
} }
my_player2_main
public my_player2_main() {
initComponents();
}
static String str;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
my_player2_func.play();
}
private void jSlider1StateChanged(javax.swing.event.ChangeEvent evt) {//what should i do here?}
JLayer is a decoder. By its very definition it does not know anything about volume, thats the job of the audio hardware that plays the sound.
Your problem stems from the fact that you are using the Player and defaul JavaSoundAudioDevice classes, which were meant as short, simple examples how to play an MP3, not as building blocks for a full fledged audio player.
You will have to copy or modify the source of JavaSoundAudioDevice (part of JLayer source) and hack it to support volume control. Et voila you can control volume.

Categories