add basic MVVM setup

This commit is contained in:
Quillraven
2025-06-01 02:01:40 +02:00
parent 1fa51c9b14
commit 90fe7b367d
7 changed files with 196 additions and 77 deletions

View File

@@ -2,6 +2,7 @@ package io.github.com.quillraven.audio;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.math.MathUtils;
import io.github.com.quillraven.asset.AssetService;
import io.github.com.quillraven.asset.MusicAsset;
@@ -10,11 +11,34 @@ public class AudioService {
private final AssetService assetService;
private Music currentMusic;
private MusicAsset currentMusicAsset;
private float musicVolume;
private float soundVolume;
public AudioService(AssetService assetService) {
this.assetService = assetService;
this.currentMusic = null;
this.currentMusicAsset = null;
this.musicVolume = 0.5f;
this.soundVolume = 0.33f;
}
public void setMusicVolume(float volume) {
this.musicVolume = MathUtils.clamp(volume, 0f, 1f);
if (this.currentMusic != null) {
this.currentMusic.setVolume(volume);
}
}
public float getMusicVolume() {
return musicVolume;
}
public void setSoundVolume(float soundVolume) {
this.soundVolume = MathUtils.clamp(soundVolume, 0f, 1f);
}
public float getSoundVolume() {
return soundVolume;
}
public void playMusic(MusicAsset musicAsset) {

View File

@@ -47,7 +47,7 @@ public class GameScreen extends ScreenAdapter {
this.physicWorld = new World(Vector2.Zero, true);
this.physicWorld.setAutoClearForces(false);
this.engine = new Engine();
this.tiledAshleySpawner = new TiledAshleySpawner(this.engine, this.physicWorld);
this.tiledAshleySpawner = new TiledAshleySpawner(this.engine, this.physicWorld, this.game.getAssetService());
ImmutableArray<Entity> controllerEntities = this.engine.getEntitiesFor(Family.all(Controller.class).get());
this.keyboardController = new KeyboardController(GameControllerState.class, controllerEntities);

View File

@@ -1,22 +1,15 @@
package io.github.com.quillraven.screen;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ProgressBar;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.asset.SkinAsset;
import io.github.com.quillraven.ui.model.MenuViewModel;
import io.github.com.quillraven.ui.view.MenuView;
public class MenuScreen extends ScreenAdapter {
@@ -44,7 +37,7 @@ public class MenuScreen extends ScreenAdapter {
this.inputMultiplexer.clear();
this.inputMultiplexer.addProcessor(stage);
menuUI();
this.stage.addActor(new MenuView(stage, skin, new MenuViewModel(game)));
}
@Override
@@ -52,72 +45,6 @@ public class MenuScreen extends ScreenAdapter {
this.stage.clear();
}
private void menuUI() {
Table table = new Table();
table.setFillParent(true);
Image image = new Image(skin, "banner");
table.add(image);
table.row();
Table table1 = new Table();
table1.setBackground(skin.getDrawable("frame"));
table1.padLeft(40.0f);
table1.padRight(40.0f);
table1.padTop(25.0f);
table1.padBottom(20.0f);
TextButton textButton = new TextButton("Start Game", skin);
textButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
game.setScreen(GameScreen.class);
}
});
table1.add(textButton);
table1.row();
Table table2 = new Table();
Label label = new Label("Music Volume", skin);
label.setColor(skin.getColor("sand"));
table2.add(label);
table2.row();
ProgressBar progressBar = new ProgressBar(0.0f, 100.0f, 1.0f, false, skin);
table2.add(progressBar);
table1.add(table2).padTop(10.0f);
table1.row();
table2 = new Table();
label = new Label("Sound Volume", skin);
label.setColor(skin.getColor("sand"));
table2.add(label);
table2.row();
progressBar = new ProgressBar(0.0f, 100.0f, 1.0f, false, skin);
table2.add(progressBar);
table1.add(table2).padTop(10.0f);
table1.row();
textButton = new TextButton("Quit Game", skin);
textButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
Gdx.app.exit();
}
});
table1.add(textButton).padTop(10.0f);
table.add(table1).align(Align.top).expandY().padTop(20f);
table.row();
label = new Label("by Quillraven 2025", skin, "small");
label.setColor(skin.getColor("white"));
table.add(label).padRight(5.0f).padBottom(5f).expand().align(Align.bottomRight);
stage.addActor(table);
}
@Override
public void render(float delta) {
stage.act(delta);

View File

@@ -0,0 +1,38 @@
package io.github.com.quillraven.ui.model;
import com.badlogic.gdx.Gdx;
import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.audio.AudioService;
import io.github.com.quillraven.screen.GameScreen;
public class MenuViewModel extends ViewModel {
public static final String MUSIC_VOLUME_PROPERTY = "musicVolume";
private final AudioService audioService;
public MenuViewModel(GdxGame game) {
super(game);
this.audioService = game.getAudioService();
}
public float getMusicVolume() {
return audioService.getMusicVolume();
}
public void setMusicVolume(float volume) {
this.propertyChangeSupport.firePropertyChange(MUSIC_VOLUME_PROPERTY, getMusicVolume(), volume);
this.audioService.setMusicVolume(volume);
}
public float getSoundVolume() {
return audioService.getSoundVolume();
}
public void startGame() {
game.setScreen(GameScreen.class);
}
public void quitGame() {
Gdx.app.exit();
}
}

View File

@@ -0,0 +1,20 @@
package io.github.com.quillraven.ui.model;
import io.github.com.quillraven.GdxGame;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public abstract class ViewModel {
protected final GdxGame game;
protected final PropertyChangeSupport propertyChangeSupport;
public ViewModel(GdxGame game) {
this.game = game;
this.propertyChangeSupport = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
this.propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
}

View File

@@ -0,0 +1,70 @@
package io.github.com.quillraven.ui.view;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ProgressBar;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.utils.Align;
import io.github.com.quillraven.ui.model.MenuViewModel;
public class MenuView extends View<MenuViewModel> {
public MenuView(Stage stage, Skin skin, MenuViewModel viewModel) {
super(stage, skin, viewModel);
}
@Override
protected void setupUI() {
setFillParent(true);
Image image = new Image(skin, "banner");
add(image).row();
setupMenuContent();
Label label = new Label("by Quillraven 2025", skin, "small");
label.setColor(skin.getColor("white"));
add(label).padRight(5.0f).padBottom(5f).expand().align(Align.bottomRight);
}
private void setupMenuContent() {
Table contentTable = new Table();
contentTable.setBackground(skin.getDrawable("frame"));
contentTable.padLeft(40.0f);
contentTable.padRight(40.0f);
contentTable.padTop(25.0f);
contentTable.padBottom(20.0f);
TextButton textButton = new TextButton("Start Game", skin);
onClick(textButton, ((event, x, y) -> viewModel.startGame()));
contentTable.add(textButton).row();
ProgressBar musicBar = setupVolumeBar(contentTable, "Music Volume", viewModel.getMusicVolume());
viewModel.addPropertyChangeListener(MenuViewModel.MUSIC_VOLUME_PROPERTY, (event) ->
musicBar.setValue((Float) event.getNewValue())
);
setupVolumeBar(contentTable, "Sound Volume", viewModel.getSoundVolume());
textButton = new TextButton("Quit Game", skin);
onClick(textButton, ((event, x, y) -> viewModel.quitGame()));
contentTable.add(textButton).padTop(10.0f);
add(contentTable).align(Align.top).expandY().padTop(20f).row();
}
private ProgressBar setupVolumeBar(Table contentTable, String title, float initialValue) {
Table table = new Table();
Label label = new Label(title, skin);
label.setColor(skin.getColor("sand"));
table.add(label).row();
ProgressBar progressBar = new ProgressBar(0.0f, 1f, 0.05f, false, skin);
progressBar.setValue(initialValue);
table.add(progressBar).fill();
contentTable.add(table).padTop(10.0f).row();
return progressBar;
}
}

View File

@@ -0,0 +1,40 @@
package io.github.com.quillraven.ui.view;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import io.github.com.quillraven.ui.model.ViewModel;
public abstract class View<T extends ViewModel> extends Table {
protected final Stage stage;
protected final Skin skin;
protected final T viewModel;
public View(Stage stage, Skin skin, T viewModel) {
super(skin);
this.stage = stage;
this.skin = skin;
this.viewModel = viewModel;
setupUI();
}
protected abstract void setupUI();
public static void onClick(Actor actor, OnClickConsumer consumer) {
actor.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
consumer.onClick(event, x, y);
}
});
}
@FunctionalInterface
public interface OnClickConsumer {
void onClick(InputEvent event, float x, float y);
}
}