0

I'm trying to build a Snake Game and I made it so whenever you eat an apple or lose it plays a sound. I want to make it so I can add a way to decrease the volume and increase the volume of the sounds

Here is the Snake Game class (main Class)

import java.io.File;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

@SuppressWarnings("unused")
public class SnakeGame {
    public static int counter = -1;
    public static int minutes = 0;
    public static int extraSpace = 120;
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        GameFrame frame = new GameFrame();
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                counter++;  
                if(counter == 60) {
                    counter = 0;
                    minutes++;
                    
                }
                
                if(counter >= 10) {
                    extraSpace = 125;
                    
                } else {
                    extraSpace = 120;
                    
                }
                
                
            }
            
        }, new Date(), 1000);
        
    }
    
    public static void PlaySound(File Sound) {  // Method that plays the sound
        try {
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(Sound));
            clip.start();
            
        } catch (Exception e) {
            
            
        }
        
    }

}

Here is the JFrame class (The class that builds the frame of the game

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class GameFrame extends JFrame implements ActionListener {
    
    public JButton resetButton;
    GamePanel panel = new GamePanel(); 
    
    GameFrame() {
        
        Font F1 = new Font("Monospaced", Font.PLAIN, 12);
        
        this.add(panel);
        this.setTitle("Snake Game");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        this.pack();
        this.setLocationRelativeTo(null);
        
        resetButton = new JButton();
        resetButton.setText("Click to restart the Game!");
        resetButton.setFont(F1);
        resetButton.setForeground(Color.WHITE);
        resetButton.setBackground(Color.BLACK);
        resetButton.setSize(100, 50);
        resetButton.setLocation(0, 200);
        resetButton.addActionListener(this);
        
        this.add(resetButton, BorderLayout.PAGE_END);
        this.setVisible(true);
        
    }
    
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == resetButton) {
            this.remove(panel); // Closes the Game
            panel = new GamePanel(); // Makes a new a Game
            this.add(panel); // Opens the game
            SwingUtilities.updateComponentTreeUI(this); // Automatically updates the game frame
            panel.requestFocusInWindow(); // Brings back focus to the game from the reset button
            SnakeGame.counter = 0; // Resets Seconds timer when game resets
            SnakeGame.minutes = 0; // Resets Minutes timer when game resets 
            
        }
        
    }
    
}

and here is the GamePanel Class, here is where all the background proccess work, for example it triggers game over when the snake hits itself or bumps in the border

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.Random;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.JPanel;
import javax.swing.Timer;

@SuppressWarnings({"unused", "serial"})
public class GamePanel extends JPanel implements ActionListener {
    
    static final int SCREEN_WIDTH = 600;
    static final int SCREEN_HEIGHT = 625;
    static final int UNIT_SIZE = 25;
    static final int GAME_UNITS = (SCREEN_WIDTH*SCREEN_HEIGHT)/UNIT_SIZE;
    static final int DELAY = 75;
    final int x[] = new int[GAME_UNITS];
    final int y[] = new int[GAME_UNITS];
    int bodyParts = 6;
    int applesEaten = 0;
    public static int highScore;
    int appleX;
    int appleY;
    int timePassed = SnakeGame.counter;
    int timePauseds = 0;
    int timePausedm = 0;
    int timeOvers;
    int timeOverm;
    String GamePause = "Game Over";
    char direction = 'R';
    boolean running = false;
    boolean paused = false;
    Timer timer;
    Random random;
    
    GamePanel() {
        random = new Random();
        this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        this.setBackground(Color.BLACK);
        this.setFocusable(true);
        this.addKeyListener(new MyKeyAdapter());
        start();
        
    }
    
    public void start() {
        newApple();
        running = true;
        timer = new Timer(DELAY,this);
        timer.start();
        
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        draw(g);
        
    }
    
    public void gameOver(Graphics g) {
        g.setColor(Color.WHITE);
        g.setFont(new Font("Ink Free", Font.PLAIN , 45));
        FontMetrics metrics1 = getFontMetrics(g.getFont());
        g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics1.stringWidth("Score: " + applesEaten))/2, g.getFont().getSize());
        
        g.setColor(Color.red);
        g.setFont(new Font("Ink Free", Font.BOLD, 75));
        FontMetrics metrics2 = getFontMetrics(g.getFont());
        g.drawString(GamePause, (SCREEN_WIDTH - metrics2.stringWidth(GamePause))/2, SCREEN_HEIGHT/2);
        
        while(applesEaten > highScore) {
            highScore = applesEaten;
            
        }
        
        if(!running && GamePause == "Game Over") {
            File NBSE = new File("C:\\Users\\2010\\My Files\\Desktop\\Java Projets\\NBSE.wav");
            SnakeGame.PlaySound(NBSE);
            
        }
        
        g.setColor(Color.WHITE);
        g.setFont(new Font("Ink Free", Font.PLAIN , 25));
        FontMetrics metrics3 = getFontMetrics(g.getFont());
        g.drawString("HighScore: " + highScore, (SCREEN_WIDTH - metrics3.stringWidth("HighScore: " + highScore))/2, 115);
        
        if(!running && GamePause == "Game Over") {
            timeOvers = SnakeGame.counter;
            timeOverm = SnakeGame.minutes;
            
        } else {
            timeOvers = 0;
            timeOverm = 0;
            
        }
        
        g.setColor(Color.WHITE);
        g.setFont(new Font("Ink Free", Font.PLAIN, 25));
        FontMetrics metrics4 = getFontMetrics(g.getFont());
        g.drawString("Time Passed: " + timeOverm + ":" + String.format("%02d", timeOvers), (SCREEN_WIDTH - metrics4.stringWidth("Time Passed: " + timeOverm + ":" + String.format("%02d", timeOvers)))/2, 80);
        
        
    }
    
    public void draw(Graphics g) {
        
        if(running) {
            for(int i=0; i > SCREEN_HEIGHT/UNIT_SIZE; i++) {
                g.drawLine(i*UNIT_SIZE, 0, i*UNIT_SIZE, SCREEN_HEIGHT);
                g.drawLine(0, i*UNIT_SIZE, SCREEN_WIDTH, i*UNIT_SIZE);
            
            }
            g.setColor(Color.RED);
            g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_SIZE);
        
            for(int i = 0; i < bodyParts; i++) {
                if(i == 0) {
                    g.setColor(Color.green);
                    g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
                
                } else {
                    g.setColor(new Color(45, 180, 0));
                    g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
                }
                
            }
            g.setColor(Color.WHITE);
            g.setFont(new Font("Ink Free", Font.PLAIN, 45));
            FontMetrics metrics = getFontMetrics(g.getFont());
            g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics.stringWidth("Score: " + applesEaten))/2, g.getFont().getSize());
            
            g.setColor(Color.WHITE);
            g.setFont(new Font("Ink Free", Font.PLAIN, 25));
            FontMetrics metrics1 = getFontMetrics(g.getFont());
            g.drawString("Time Passed: " + SnakeGame.minutes + ":" + String.format("%02d", SnakeGame.counter), (SCREEN_WIDTH - metrics1.stringWidth("Time Passed: " + SnakeGame.minutes + ":" + String.format("%02d", SnakeGame.counter)))/2, 80);
            
        } else {
            gameOver(g);
            
        }
        
    }
    
    public void newApple() {
        appleX = random.nextInt((int)(SCREEN_WIDTH/UNIT_SIZE))*UNIT_SIZE;
        appleY = random.nextInt((int)((SCREEN_HEIGHT - 2)/UNIT_SIZE))*UNIT_SIZE;
        
    }
    
    public void move() {
        for(int i = bodyParts; i > 0; i--) {
            x[i] = x[i-1];
            y[i] = y[i-1];
            
        }
        
        switch(direction) {
        case 'U':
            y[0] = y[0] - UNIT_SIZE;
            break;
            
        case 'D':
            y[0] = y[0] + UNIT_SIZE;
            break;
        
        case 'L':
            x[0] = x[0] - UNIT_SIZE;
            break;
            
        case 'R':
            x[0] = x[0] + UNIT_SIZE;
            break;
            
        }
        
    }
    
    public void checkApple() {
        if((x[0] == appleX) && (y[0] == appleY)) {
            bodyParts++;
            applesEaten++;
            
            File APSE = new File("C:\\Users\\2010\\My Files\\Desktop\\Java Projets\\APSE.wav");
            SnakeGame.PlaySound(APSE);
            
            newApple();
            
        }
        
    }
    
    public void pause() {
        try {
            Thread.sleep(1000);
            
        } catch (InterruptedException e) {
            e.printStackTrace();
            
        }
        
    }
    
    public void resume() {
        timer.stop();
        
    }
    
    public void checkCollisions() {
        // Checks for collisions 
        for(int i = bodyParts; i > 0; i--) {
            if((x[0] == x[i]) && (y[0] == y[i])) {
                running = false;
                
            }
            
        }
        // Checks if head touches left border
        if(x[0] < 0) {
            running = false;
            
        }
        // Checks if head touches right border
        if(x[0] > SCREEN_WIDTH) {
            running = false;
            
        }
        // Checks if head touches top border
        if(y[0] < 0) {
            running = false;
            
        }
        // Checks if head touches bottom border
        if(y[0] > SCREEN_HEIGHT) {
            running = false;
            
        }
        
        if(!running) {
            timer.stop();
            
        }
        
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        
        if(running) {
            move();
            checkApple();
            checkCollisions();
            
        }
        repaint();
        
    }
    
    public class MyKeyAdapter extends KeyAdapter{
        @Override
        public void keyPressed(KeyEvent e) {
            switch(e.getKeyCode()) {
            case KeyEvent.VK_LEFT:
                if(direction != 'R') {
                    direction = 'L';
                    
                }
                break;
            case KeyEvent.VK_RIGHT:
                if(direction != 'L') {
                    direction = 'R';
                    
                }
                break;
            case KeyEvent.VK_UP:
                if(direction != 'D') {
                    direction = 'U';
                    
                }
                break;
            case KeyEvent.VK_DOWN:
                if(direction != 'U') {
                    direction = 'D';
                    
                }
                break;
            case KeyEvent.VK_A:
                if(direction != 'R') {
                    direction = 'L';
                    
                }
                break;
            case KeyEvent.VK_D:
                if(direction != 'L') {
                    direction = 'R';
                    
                }
                break;
            case KeyEvent.VK_W:
                if(direction != 'D') {
                    direction = 'U';
                    
                }
                break;
            case KeyEvent.VK_S:
                if(direction != 'U') {
                    direction = 'D';
                    
                }
                break;  
            case KeyEvent.VK_ESCAPE:
                if(running) {
                    running = false;
                    GamePause = "Paused";
                    timePauseds = SnakeGame.counter;
                    timePausedm = SnakeGame.minutes;
                    
                } else if(!running) {
                    running = true;
                    GamePause = "Game Over";
                    SnakeGame.counter = timePauseds;
                    SnakeGame.minutes = timePausedm;
                    
                }
                break;
            }
            
        }
    }
    
}

I hope I explained this good enough. If I didn't here let me try again. Basically I want to add a way so I can change the volume of the sounds in the game itself not the entire computer.

  • See this [SO post](https://stackoverflow.com/questions/40514910/set-volume-of-java-clip). – DevilsHnd - 退職した Dec 29 '20 at 08:42
  • I disagree with the assessment that this question is a duplicate of the one cited. The latter is specifically about the issue of scaling the values used to control volume, though I do grant that it contains an example of the main method used for volume control as part of the answer. There are very likely better duplicates to cite. For example: https://stackoverflow.com/questions/953598/audio-volume-control-increase-or-decrease-in-java This one has the merit of specifically addressing the question. – Phil Freihofner Dec 31 '20 at 18:43

1 Answers1

0

This link provided by @DevilsHnd and @tgdavies in the comments to your question, which makes use of the FloatControl class, is the main option if you are sticking with core Java. The Java audio tutorials cover it in the section titled Processing Audio with Controls.

This will work for most situations, but has two limitations that may or may not matter. One is that functionality being relied upon is external to Java. I think most computer systems do have a working MASTER_GAIN, but I don't know for sure.

The other drawback is that volume changes are limited as to the "granularity" of when they can occur in that they only occur at buffer-length intervals. If the buffer is 1/10th of a second long, for example 4410 frames at 44100 fps, at best you can get 10 changes in a second. For longer buffers lengths, this can make it hard to smoothly fade an ongoing sound up or down, as the "stair steps" can create an unwanted "zippering" sound.

The end of the Oracle tutorial describes the possibility of "manipulating the audio directly." This is what occurs in a library I wrote, a class called AudioCue, which provides for real-time volume changes. The class functions very much like a Clip but with additional capabilities. All the functionality is executed within Java. Changes are computed at the level of individual frames, with built in smoothing. So if you have a need for real-time fading, you can either use this class, or examine the code for an example of how to manipulate the PCM data directly.

Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41