Skip to content

Conversation

arvi18
Copy link

@arvi18 arvi18 commented Apr 15, 2025

👉 Checklist

Please make sure to check off the following before submitting:

  • I have reviewed my submission thoroughly.
  • I have tested my code (if submission is related to coding) and run the game before pushing (to make sure the project compile).
  • I have run the JUnit tests (if submission is related to coding).
  • I have read the Code of Conduct and Contribution Guidelines.

✍ Description of the Pull Request

Please concisely describe the changes you have made.

  • Added Volume Control for Background Music and Added Sound Effects Volume Control

🔗 Issue link

Issue reference number and link goes here e.g. 'Fixes/Closes #<issue_number>'.

  • This Pull Request fixes the issue : 'Closes #'
  • This Pull Request does not fix an issue.

Thanks for taking the time to fill out this Pull Request! ❤️ Thanks for contributing to this project 🦖

@arvi18
Copy link
Author

arvi18 commented Apr 28, 2025

/codehelper hello

private static SoundController instance;
private MediaPlayer inGameSound;
private BorderPane root;
private static double volume = 1.0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: Consider initializing the inGameSound field in the constructor rather than having it as null initially. This would prevent potential null pointer exceptions if methods like stopInGameSound() are called before playInGameSound().

{
inGameSound.stop();
}
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding a check for null before accessing the resource to prevent potential NullPointerException if the resource is not found:

if (getClass().getResource(musicResource) == null) {
    System.err.println("Resource not found: " + musicResource);
    return;
}

sfxVolumeLabel.setText(String.format("%.0f%%", sfxVolume * 100));
System.out.println("VFX Volume label updated to: " + sfxVolumeLabel.getText());
if(sfxVolume == 0.00)
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best Practice: Consider using a logger instead of System.out.println() for better control over logging output.

private BorderPane root;
private static double volume = 1.0;
private static double prevVolume = 1.0;
private static double sfxVolume = 1.0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: The prevVolume and prevSfxVolume fields are static but could be instance variables since they're specific to each instance of SoundController. Since this is a singleton, it might not cause issues, but it's better practice to make them instance variables.

sfxPlayer.play();
}
public double adjustInGameSFX(Slider sfxVolumeSlider, Label sfxVolumeLabel, Image sfxMute, Image sfxAudioOn, ImageView sfxImageViewPlaying) {
sfxVolumeSlider.setValue(sfxVolume);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding a null check for the sfxImageViewPlaying parameter before accessing it to prevent potential NullPointerException.

sfxPlayer.play();
}
public double adjustInGameSFX(Slider sfxVolumeSlider, Label sfxVolumeLabel, Image sfxMute, Image sfxAudioOn, ImageView sfxImageViewPlaying) {
sfxVolumeSlider.setValue(sfxVolume);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design Improvement: The adjustInGameSFX and adjustVolume methods are quite long and do multiple things. Consider breaking them down into smaller, more focused methods for better maintainability.

public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
Platform.runLater(() -> {

volume = newValue.doubleValue();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: Consider adding a null check for the inGameSound before setting its volume to prevent potential NullPointerException.

import javafx.scene.media.MediaPlayer;

public class SoundController {
private static SoundController instance;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding JavaDoc to the SoundController class and its public methods to better document their purpose and usage.

@@ -20,7 +22,7 @@ public class DinosaurController {
private Entity score;
private Entity life;
private int lives = 3;

private MediaPlayer inGameSound;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: The inGameSound field is now declared in both DinosaurController and SoundController. Since SoundController manages the sound, consider removing the duplicate field from DinosaurController.

public Image getViewImage() {
return viewImage;
}
public void playSoundEffect(String soundResource) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider adding proper error handling when playing sound effects. If the sound file is missing, the current implementation might throw an exception.

public PauseMenu() {
super(MenuType.GAME_MENU);
DinosaurApp.initLanguages();

DinosaurApp.initLanguages();

PauseButton btnBack = new PauseButton(getLocalizationService().getLocalizedString("Pause.1"),() -> fireResume());

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Style: The comment //need2ComeBack seems like a TODO item. Consider using a proper TODO comment format like // TODO: [description] or removing it if no longer needed.

//These are for tracking how many times the pauseMenu has been opened. Helps in getting the mute/unmute image from SoundController
//For first time, this helps loading the unmuted icon and if count increases more than 1, it changes the icon depending on the controller
//At this point of time, I really don't have other proper method, It works so why not, But if you know any other solution please let me know and contribute
private int count = 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design Concern: The comment about tracking how many times the pause menu has been opened suggests a workaround solution. Consider refactoring this approach to a more robust design that doesn't rely on counting menu opens.


ControlButton btnControls = new ControlButton(getLocalizationService().getLocalizedString("Pause.3"));


Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resource Management: Consider using try-with-resources for handling input streams to ensure they are properly closed after use.


//These are for tracking how many times the pauseMenu has been opened. Helps in getting the mute/unmute image from SoundController
//For first time, this helps loading the unmuted icon and if count increases more than 1, it changes the icon depending on the controller
//At this point of time, I really don't have other proper method, It works so why not, But if you know any other solution please let me know and contribute
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: The count and sfxCount variables are used to track menu opens, but this approach seems fragile. Consider using a more direct approach to check the current state from SoundController instead.

public static final String SHOOT_SOUND = "shoot.wav";
* SOUNDS
*/
public static final String ENEMYSHOOT_SOUND = "/assets/sounds/enemyShoot.wav";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency: The sound path constants in GameConstants have been updated to include the /assets prefix, which is good for consistency, but ensure all code that uses these constants has been updated accordingly.

@visz11
Copy link
Collaborator

visz11 commented Jul 23, 2025

/refacto-test

Copy link

refacto-test bot commented Jul 23, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

@visz11
Copy link
Collaborator

visz11 commented Jul 23, 2025

/refacto-test

Copy link

refacto-test bot commented Jul 23, 2025

PR already reviewed at the latest commit: 4befef9.
Please try again with new changes.

@visz11
Copy link
Collaborator

visz11 commented Aug 7, 2025

/refacto-test

Copy link

refacto-test bot commented Aug 7, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

@visz11
Copy link
Collaborator

visz11 commented Aug 20, 2025

/refacto-test

Copy link

refacto-test bot commented Aug 20, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

Copy link

refacto-test bot commented Aug 20, 2025

Code Review: Sound Controller Implementation

👍 Well Done
Singleton Pattern Implementation

SoundController uses singleton pattern effectively for centralized sound management across the application.

Separate Volume Controls

Implemented distinct controls for music and sound effects, improving user experience.

📌 Files Processed
  • src/main/java/com/dinosaur/dinosaurexploder/controller/SoundController.java
  • src/main/java/com/dinosaur/dinosaurexploder/view/PauseMenu.java
  • src/main/java/com/dinosaur/dinosaurexploder/model/GameConstants.java
  • src/main/java/com/dinosaur/dinosaurexploder/model/PlayerComponent.java
  • src/main/java/com/dinosaur/dinosaurexploder/controller/DinosaurController.java
  • src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java
  • .idea/misc.xml
  • src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java
  • src/main/resources/Styles/styles.css
  • src/main/java/module-info.java
📝 Additional Comments
src/main/java/com/dinosaur/dinosaurexploder/controller/SoundController.java (1)
Sound Effect Pooling

Creating new Media and MediaPlayer objects for each sound effect is inefficient. Frequent sound effects (like shooting) could benefit from object pooling to reduce allocation overhead.

private Map<String, MediaPlayer> soundEffectPool = new HashMap<>();

public void playSoundEffect(String soundResource) {
    MediaPlayer sfxPlayer = soundEffectPool.get(soundResource);
    if (sfxPlayer == null) {
        Media media = new Media(getClass().getResource(soundResource).toExternalForm());
        sfxPlayer = new MediaPlayer(media);
        soundEffectPool.put(soundResource, sfxPlayer);
    }
    sfxPlayer.setVolume(sfxVolume);
    sfxPlayer.stop();
    sfxPlayer.play();
}

Standards:

  • ISO-IEC-25010-Performance-Resource-Utilization
  • Object-Pooling-Pattern
src/main/java/com/dinosaur/dinosaurexploder/model/GameConstants.java (1)
Resource Path Inconsistency

Sound resource paths are inconsistently defined compared to image paths in the same file. Some image paths use 'assets/textures/' format without leading slash, while all sound paths use '/assets/sounds/' with leading slash, potentially causing resource loading issues.

public static final String ENEMYSHOOT_SOUND = "assets/sounds/enemyShoot.wav";
public static final String SHOOT_SOUND = "assets/sounds/shoot.wav";
public static final String MAINMENU_SOUND = "assets/sounds/mainMenu.wav";
public static final String BACKGROUND_SOUND ="assets/sounds/gameBackground.wav";
public static final String ENEMY_EXPLODE_SOUND = "assets/sounds/enemyExplode.wav";
public static final String PLAYER_HIT_SOUND = "assets/sounds/playerHit.wav";

Standards:

  • Logic-Verification-Consistency
  • Business-Rule-Path-Standardization
src/main/java/com/dinosaur/dinosaurexploder/view/PauseMenu.java (1)
Resource Leak Risk

Input streams are not closed after use. Resource leaks can lead to memory issues under load. Potential DoS vector if application runs for extended periods.

try (InputStream muteButton = getClass().getClassLoader().getResourceAsStream("assets/textures/silent.png");
     InputStream soundButton = getClass().getClassLoader().getResourceAsStream("assets/textures/playing.png");
     InputStream sfxMuteButton = getClass().getClassLoader().getResourceAsStream("assets/textures/silent.png");
     InputStream sfxSoundButton = getClass().getClassLoader().getResourceAsStream("assets/textures/playing.png")) {
    if (muteButton == null || soundButton == null || sfxMuteButton == null || sfxSoundButton == null) {
        throw new FileNotFoundException("Resource not found: mute or play icon");
    }
    // Initialize images for mute and play
    mute = new Image(muteButton);
    audioOn = new Image(soundButton);
    sfxMute = new Image(sfxMuteButton);
    sfxAudioOn = new Image(sfxSoundButton);
}

Standards:

  • CWE-772
  • OWASP-A06

Comment on lines +78 to +83
// Use the controlled volume
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
MediaPlayer sfxPlayer = new MediaPlayer(media);
sfxPlayer.setVolume(sfxVolume);
sfxPlayer.play();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resource Leak Risk in Sound Effects

MediaPlayer instances are created without disposal, causing resource leaks. Each sound effect creates a new MediaPlayer that's never released, potentially exhausting system resources during gameplay.

Suggested change
// Use the controlled volume
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
MediaPlayer sfxPlayer = new MediaPlayer(media);
sfxPlayer.setVolume(sfxVolume);
sfxPlayer.play();
}
public void playSoundEffect(String soundResource) {
// Use the controlled volume
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
MediaPlayer sfxPlayer = new MediaPlayer(media);
sfxPlayer.setVolume(sfxVolume);
sfxPlayer.setOnEndOfMedia(() -> {
sfxPlayer.dispose();
});
sfxPlayer.play();
}
Standards
  • ISO-IEC-25010-Reliability-Maturity
  • SRE-Resource-Management

Comment on lines +37 to +41
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Error Handling for Media Loading

No exception handling for resource loading failures. If audio file is missing or corrupted, NullPointerException will occur, potentially crashing the application.

Suggested change
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
try {
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
} catch (Exception e) {
System.err.println("Failed to load or play sound: " + musicResource);
e.printStackTrace();
}
Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • SRE-Error-Handling

Platform.runLater(() -> {

volume = newValue.doubleValue();
inGameSound.setVolume(volume);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential NullPointerException Risk

The adjustVolume method accesses inGameSound without null check. If called before playInGameSound initializes inGameSound, this will cause NullPointerException, breaking volume adjustment functionality.

Suggested change
inGameSound.setVolume(volume);
volume = newValue.doubleValue();
if (inGameSound != null) {
inGameSound.setVolume(volume);
}
volumeLabel.setText(String.format("%.0f%%", volume * 100));
Standards
  • Logic-Verification-Null-Safety
  • Algorithm-Correctness-Defensive-Programming

Comment on lines +89 to +103
Platform.runLater(() -> {
sfxVolume = newValue.doubleValue();
sfxVolumeLabel.setText(String.format("%.0f%%", sfxVolume * 100));
System.out.println("VFX Volume label updated to: " + sfxVolumeLabel.getText());
if(sfxVolume == 0.00)
{
sfxImageViewPlaying.setImage(sfxMute);
viewSfxImage = sfxMute;
}
else
{
sfxImageViewPlaying.setImage(sfxAudioOn);
viewSfxImage = sfxAudioOn;
}
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI Thread Blocking

Using Platform.runLater inside a listener that's already on the JavaFX thread creates unnecessary thread scheduling overhead. This pattern can cause UI stuttering under high frequency slider changes.

Suggested change
Platform.runLater(() -> {
sfxVolume = newValue.doubleValue();
sfxVolumeLabel.setText(String.format("%.0f%%", sfxVolume * 100));
System.out.println("VFX Volume label updated to: " + sfxVolumeLabel.getText());
if(sfxVolume == 0.00)
{
sfxImageViewPlaying.setImage(sfxMute);
viewSfxImage = sfxMute;
}
else
{
sfxImageViewPlaying.setImage(sfxAudioOn);
viewSfxImage = sfxAudioOn;
}
});
sfxVolume = newValue.doubleValue();
sfxVolumeLabel.setText(String.format("%.0f%%", sfxVolume * 100));
System.out.println("VFX Volume label updated to: " + sfxVolumeLabel.getText());
if(sfxVolume == 0.00) {
sfxImageViewPlaying.setImage(sfxMute);
viewSfxImage = sfxMute;
} else {
sfxImageViewPlaying.setImage(sfxAudioOn);
viewSfxImage = sfxAudioOn;
}
Standards
  • ISO-IEC-25010-Performance-Time-Behaviour
  • JavaFX-Thread-Model-Best-Practices

Comment on lines +37 to +79
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
}

public void stopInGameSound() {
if (inGameSound != null) {
inGameSound.stop();
}
}

public void muteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(true);
}
}

public void unmuteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(false);
}
}

public boolean isMuted() {
return inGameSound != null && inGameSound.isMute();
}
public double getVolume() {
return volume;
}
public double getSfxVolume() {
return sfxVolume;
}
public Image getViewSfxImage() {
return viewSfxImage;
}
public Image getViewImage() {
return viewImage;
}
public void playSoundEffect(String soundResource) {
// Use the controlled volume
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Null Resource Check

Missing null check before calling toExternalForm() on resource. If resource not found, NullPointerException occurs. Attacker could exploit by manipulating resource paths.

Suggested change
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
}
public void stopInGameSound() {
if (inGameSound != null) {
inGameSound.stop();
}
}
public void muteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(true);
}
}
public void unmuteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(false);
}
}
public boolean isMuted() {
return inGameSound != null && inGameSound.isMute();
}
public double getVolume() {
return volume;
}
public double getSfxVolume() {
return sfxVolume;
}
public Image getViewSfxImage() {
return viewSfxImage;
}
public Image getViewImage() {
return viewImage;
}
public void playSoundEffect(String soundResource) {
// Use the controlled volume
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
java.net.URL resourceUrl = getClass().getResource(musicResource);
if (resourceUrl == null) {
System.err.println("Resource not found: " + musicResource);
return;
}
Media media = new Media(resourceUrl.toExternalForm());
Standards
  • CWE-476
  • OWASP-A04

Comment on lines +13 to +124
public static SoundController getInstance() {
if (instance == null) {
instance = new SoundController();
}
return instance;
}

public void playInGameSound(String musicResource, double volumeValue) {
if(inGameSound != null)
{
inGameSound.stop();
}
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
}

public void stopInGameSound() {
if (inGameSound != null) {
inGameSound.stop();
}
}

public void muteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(true);
}
}

public void unmuteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(false);
}
}

public boolean isMuted() {
return inGameSound != null && inGameSound.isMute();
}
public double getVolume() {
return volume;
}
public double getSfxVolume() {
return sfxVolume;
}
public Image getViewSfxImage() {
return viewSfxImage;
}
public Image getViewImage() {
return viewImage;
}
public void playSoundEffect(String soundResource) {
// Use the controlled volume
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
MediaPlayer sfxPlayer = new MediaPlayer(media);
sfxPlayer.setVolume(sfxVolume);
sfxPlayer.play();
}
public double adjustInGameSFX(Slider sfxVolumeSlider, Label sfxVolumeLabel, Image sfxMute, Image sfxAudioOn, ImageView sfxImageViewPlaying) {
sfxVolumeSlider.setValue(sfxVolume);
sfxVolumeSlider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
Platform.runLater(() -> {
sfxVolume = newValue.doubleValue();
sfxVolumeLabel.setText(String.format("%.0f%%", sfxVolume * 100));
System.out.println("VFX Volume label updated to: " + sfxVolumeLabel.getText());
if(sfxVolume == 0.00)
{
sfxImageViewPlaying.setImage(sfxMute);
viewSfxImage = sfxMute;
}
else
{
sfxImageViewPlaying.setImage(sfxAudioOn);
viewSfxImage = sfxAudioOn;
}
});
}

});

sfxImageViewPlaying.setOnMouseClicked(mouseEvent -> {
if (sfxVolume == 0.00){
sfxImageViewPlaying.setImage(sfxAudioOn);
sfxVolume = prevSfxVolume;
sfxVolumeSlider.setValue(sfxVolume);
viewSfxImage = sfxAudioOn;
} else {
sfxImageViewPlaying.setImage(sfxMute);
prevSfxVolume = sfxVolume;
sfxVolume = 0.00;
sfxVolumeSlider.setValue(sfxVolume);
viewSfxImage = sfxMute;
}
});
return sfxVolume;
}
public double adjustVolume(Slider volumeSlider, Label volumeLabel, Image mute, Image audioOn, ImageView imageViewPlaying) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

God Class Issue

SoundController violates SRP by handling sound playback, UI interaction, and volume control. This creates tight coupling between UI and audio logic, making changes difficult and testing nearly impossible.

Suggested change
public class SoundController {
private static SoundController instance;
private MediaPlayer inGameSound;
private BorderPane root;
private static double volume = 1.0;
private static double prevVolume = 1.0;
private static double sfxVolume = 1.0;
private static double prevSfxVolume = 1.0;
private static Image viewImage;
private static Image viewSfxImage;
private SoundController() {}
public static SoundController getInstance() {
if (instance == null) {
instance = new SoundController();
}
return instance;
}
public void playInGameSound(String musicResource, double volumeValue) {
if(inGameSound != null)
{
inGameSound.stop();
}
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
}
public void stopInGameSound() {
if (inGameSound != null) {
inGameSound.stop();
}
}
public void muteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(true);
}
}
public void unmuteInGameSound() {
if (inGameSound != null) {
inGameSound.setMute(false);
}
}
public boolean isMuted() {
return inGameSound != null && inGameSound.isMute();
}
public double getVolume() {
return volume;
}
public double getSfxVolume() {
return sfxVolume;
}
public Image getViewSfxImage() {
return viewSfxImage;
}
public Image getViewImage() {
return viewImage;
}
public void playSoundEffect(String soundResource) {
// Use the controlled volume
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
MediaPlayer sfxPlayer = new MediaPlayer(media);
sfxPlayer.setVolume(sfxVolume);
sfxPlayer.play();
}
public double adjustInGameSFX(Slider sfxVolumeSlider, Label sfxVolumeLabel, Image sfxMute, Image sfxAudioOn, ImageView sfxImageViewPlaying) {
sfxVolumeSlider.setValue(sfxVolume);
sfxVolumeSlider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
Platform.runLater(() -> {
sfxVolume = newValue.doubleValue();
sfxVolumeLabel.setText(String.format("%.0f%%", sfxVolume * 100));
System.out.println("VFX Volume label updated to: " + sfxVolumeLabel.getText());
if(sfxVolume == 0.00)
{
sfxImageViewPlaying.setImage(sfxMute);
viewSfxImage = sfxMute;
}
else
{
sfxImageViewPlaying.setImage(sfxAudioOn);
viewSfxImage = sfxAudioOn;
}
});
}
});
sfxImageViewPlaying.setOnMouseClicked(mouseEvent -> {
if (sfxVolume == 0.00){
sfxImageViewPlaying.setImage(sfxAudioOn);
sfxVolume = prevSfxVolume;
sfxVolumeSlider.setValue(sfxVolume);
viewSfxImage = sfxAudioOn;
} else {
sfxImageViewPlaying.setImage(sfxMute);
prevSfxVolume = sfxVolume;
sfxVolume = 0.00;
sfxVolumeSlider.setValue(sfxVolume);
viewSfxImage = sfxMute;
}
});
return sfxVolume;
}
public double adjustVolume(Slider volumeSlider, Label volumeLabel, Image mute, Image audioOn, ImageView imageViewPlaying) {
// SoundController.java - Core audio functionality
public class SoundController {
private static SoundController instance;
private MediaPlayer inGameSound;
private static double volume = 1.0;
private static double sfxVolume = 1.0;
private SoundController() {}
public static SoundController getInstance() {
if (instance == null) {
instance = new SoundController();
}
return instance;
}
public void playInGameSound(String musicResource, double volumeValue) {
if(inGameSound != null) {
inGameSound.stop();
}
Media media = new Media(getClass().getResource(musicResource).toExternalForm());
inGameSound = new MediaPlayer(media);
inGameSound.setCycleCount(MediaPlayer.INDEFINITE);
inGameSound.setVolume(volume);
inGameSound.play();
}
public void playSoundEffect(String soundResource) {
Media media = new Media(getClass().getResource(soundResource).toExternalForm());
MediaPlayer sfxPlayer = new MediaPlayer(media);
sfxPlayer.setVolume(sfxVolume);
sfxPlayer.play();
}
// Volume getters and setters
public double getVolume() { return volume; }
public void setVolume(double value) { volume = value; if(inGameSound != null) inGameSound.setVolume(volume); }
public double getSfxVolume() { return sfxVolume; }
public void setSfxVolume(double value) { sfxVolume = value; }
}
Standards
  • SOLID-SRP
  • Clean-Code-Class-Organization
  • Refactoring-Extract-Class

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants