Skip to content

Adjust the sound volume while playing #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.dinosaur.dinosaurexploder.model.*;
import com.dinosaur.dinosaurexploder.view.DinosaurGUI;
import javafx.scene.input.KeyCode;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import static com.almasb.fxgl.dsl.FXGL.*;
Expand All @@ -20,7 +22,7 @@ public class DinosaurController {
private Entity score;
private Entity life;
private int lives = 3;

private MediaPlayer inGameSound;

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.

/**
* Summary :
* Detecting the player damage to decrease the lives and checking if the game is over
Expand Down Expand Up @@ -66,13 +68,12 @@ public void initInput() {
*/
public void initGame() {
getGameWorld().addEntityFactory(new GameEntityFactory());

spawn("background", 0, 0);

player = spawn("player", getAppCenter().getX() - 45, getAppHeight()-200);

FXGL.play(GameConstants.BACKGROUND_SOUND);

SoundController.getInstance().playInGameSound(GameConstants.BACKGROUND_SOUND, 1.0);
/*
* At each second that passes, we have 2 out of 3 chances of spawning a green
* dinosaur
Expand All @@ -83,30 +84,33 @@ public void initGame() {
spawn("greenDino", random(0, getAppWidth() - 80), -50);
}, seconds(0.75));

score = spawn("Score", getAppCenter().getX() -270, getAppCenter().getY() - 320);
life = spawn("Life", getAppCenter().getX() - 260, getAppCenter().getY() - 250);
score = spawn("Score", getAppCenter().getX() -270, getAppCenter().getY() - 320);
life = spawn("Life", getAppCenter().getX() - 260, getAppCenter().getY() - 250);
}
/**
* Summary :
* Detect the collision between the game elements.
*/
public void initPhysics() {
onCollisionBegin(EntityType.PROJECTILE, EntityType.GREENDINO, (projectile, greendino) -> {
FXGL.play(GameConstants.ENEMY_EXPLODE_SOUND);
SoundController.getInstance().playSoundEffect(GameConstants.ENEMY_EXPLODE_SOUND);

projectile.removeFromWorld();
greendino.removeFromWorld();
score.getComponent(ScoreComponent.class).incrementScore(1);
});

onCollisionBegin(EntityType.ENEMYPROJECTILE, EntityType.PLAYER, (projectile, player) -> {
FXGL.play(GameConstants.PLAYER_HIT_SOUND);
SoundController.getInstance().playSoundEffect(GameConstants.PLAYER_HIT_SOUND);

projectile.removeFromWorld();
System.out.println("You got hit !\n");
damagePlayer();
});

onCollisionBegin(EntityType.PLAYER, EntityType.GREENDINO, (player, greendino) -> {
FXGL.play(GameConstants.PLAYER_HIT_SOUND);
SoundController.getInstance().playSoundEffect(GameConstants.PLAYER_HIT_SOUND);

greendino.removeFromWorld();
System.out.println("You touched a dino !");
damagePlayer();
Expand All @@ -122,7 +126,9 @@ public void gameOver(){
getGameController().startNewGame();
} else {
getGameController().gotoMainMenu();
SoundController.getInstance().playInGameSound(GameConstants.MAINMENU_SOUND, 1.0);
}
});
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package com.dinosaur.dinosaurexploder.controller;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

public class SoundController {
private static SoundController instance;

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.

private MediaPlayer inGameSound;
private BorderPane root;
private static double volume = 1.0;

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().

private static double prevVolume = 1.0;
private static double sfxVolume = 1.0;

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.

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());

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;
}

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) {

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.

// 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);

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.

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.

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)
{

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.

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) {
volumeSlider.setValue(volume);
volumeSlider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
Platform.runLater(() -> {

volume = newValue.doubleValue();

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.

inGameSound.setVolume(volume);
volumeLabel.setText(String.format("%.0f%%", volume * 100));
System.out.println("Volume label updated to: " + volumeLabel.getText());
if(volume == 0.00)
{
imageViewPlaying.setImage(mute);
viewImage = mute;
}
else
{
imageViewPlaying.setImage(audioOn);
viewImage = audioOn;
}
});
}
});


imageViewPlaying.setOnMouseClicked(mouseEvent -> {
if (volume == 0.00){
imageViewPlaying.setImage(audioOn);
volume = prevVolume;
volumeSlider.setValue(volume);
viewImage = audioOn;
} else {
imageViewPlaying.setImage(mute);
prevVolume = volume;
volume = 0.00;
volumeSlider.setValue(volume);
viewImage = mute;
}
});

return volume;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
* This holds every constant in the the PROJECT
*/
public class GameConstants {

/*
* CONSTANTS FOR IMAGES
*/
* CONSTANTS FOR IMAGES
*/
public static final String BACKGROUND_IMAGEPATH = "/assets/textures/background.png";
public static final String SPACESHIP_IMAGEPATH = "assets/textures/spaceship.png";
public static final String SPACESHIP_IMAGEFILE = "spaceship.png";
Expand All @@ -23,22 +23,22 @@ public class GameConstants {
public static final String GREENDINO_IMAGEFILE = "greenDino.png";
public static final String HEART_IMAGEPATH = "assets/textures/life.png";
/*
*CONSTANTS FOR FONTS
*/
*CONSTANTS FOR FONTS
*/
public static final String ARCADECLASSIC_FONTNAME = "ArcadeClassic";
/*
* SOUNDS
*/
public static final String ENEMYSHOOT_SOUND = "enemyShoot.wav";
public static final String SHOOT_SOUND = "shoot.wav";
* SOUNDS
*/
public static final String ENEMYSHOOT_SOUND = "/assets/sounds/enemyShoot.wav";

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.

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 ="gameBackground.wav";
public static final String ENEMY_EXPLODE_SOUND = "enemyExplode.wav";
public static final String PLAYER_HIT_SOUND = "playerHit.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";

public static final String GAME_NAME = "Dinosaur Exploder";

public static final List<Language> AVAILABLE_LANGUAGES = List.of(Language.ENGLISH, Language.GERMAN );

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.almasb.fxgl.entity.SpawnData;
import com.almasb.fxgl.entity.component.Component;
import com.almasb.fxgl.time.LocalTimer;
import com.dinosaur.dinosaurexploder.controller.SoundController;
import javafx.geometry.Point2D;
import javafx.util.Duration;

Expand Down Expand Up @@ -39,12 +40,12 @@ public void onUpdate(double ptf) {
*/
@Override
public void shoot() {
FXGL.play(GameConstants.ENEMYSHOOT_SOUND);
SoundController.getInstance().playSoundEffect(GameConstants.ENEMYSHOOT_SOUND);
Point2D center = entity.getCenter();
Vec2 direction = Vec2.fromAngle(entity.getRotation() +90);
spawn("basicEnemyProjectile",
new SpawnData(center.getX() + 50 +3, center.getY())
.put("direction", direction.toPoint2D() )
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.almasb.fxgl.entity.SpawnData;
import com.almasb.fxgl.entity.component.Component;
import com.almasb.fxgl.texture.Texture;
import com.dinosaur.dinosaurexploder.controller.SoundController;
import com.dinosaur.dinosaurexploder.view.DinosaurGUI;

import javafx.geometry.Point2D;
Expand All @@ -16,7 +17,7 @@

public class PlayerComponent extends Component implements Player{
private Image spcshpImg = new Image(GameConstants.SPACESHIP_IMAGEPATH);

int movementSpeed = 8;
//entity is not initialized anywhere because it is linked in the factory
/**
Expand Down Expand Up @@ -72,28 +73,28 @@ public void moveLeft(){
* This method is overriding the superclass method to the shooting from the player and spawning of the new bullet
*/
public void shoot(){
FXGL.play(GameConstants.SHOOT_SOUND);
SoundController.getInstance().playSoundEffect(GameConstants.SHOOT_SOUND);
Point2D center = entity.getCenter();
Vec2 direction = Vec2.fromAngle(entity.getRotation() -90);
Image projImg = new Image(GameConstants.BASE_PROJECTILE_IMAGEPATH);

spawn("basicProjectile",
new SpawnData(center.getX() - (projImg.getWidth()/2) +3, center.getY() - spcshpImg.getHeight()/2)
.put("direction", direction.toPoint2D() )
);
}



private void spawnMovementAnimation() {

FXGL.entityBuilder()
.at(getEntity().getCenter().subtract(spcshpImg.getWidth() / 2, spcshpImg.getHeight() / 2))
.view(new Texture(spcshpImg))
.with(new ExpireCleanComponent(Duration.seconds(0.15)).animateOpacity())
.buildAndAttach();
}
}



}
Loading