finalize main menu with mouse controls

This commit is contained in:
Quillraven
2025-06-01 17:03:00 +02:00
parent 90fe7b367d
commit 7e7ae56b31
17 changed files with 140 additions and 21 deletions

View File

@@ -1,3 +1,4 @@
https://kenmi-art.itch.io/cute-fantasy-rpg
https://yubatake.bandcamp.com/album/jrpg-collection
https://crusenho.itch.io/complete-ui-essential-pack
https://xdeviruchi.itch.io/8-bit-fantasy-adventure-music-pack

BIN
assets/audio/menu.ogg Normal file

Binary file not shown.

BIN
assets/audio/sword_hit.wav Normal file

Binary file not shown.

View File

@@ -28,7 +28,7 @@ bar_frame
index: -1
button
rotate: false
xy: 141, 578
xy: 644, 726
size: 32, 16
split: 5, 5, 8, 6
orig: 32, 16
@@ -42,3 +42,11 @@ frame
orig: 129, 117
offset: 0, 0
index: -1
selection
rotate: false
xy: 141, 569
size: 24, 25
split: 8, 8, 9, 9
orig: 24, 25
offset: 0, 0
index: -1

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
@@ -60,6 +61,14 @@ public class AssetService implements Disposable {
return this.assetManager.get(skinAsset.getPath(), Skin.class);
}
public void queue(SoundAsset soundAsset) {
this.assetManager.load(soundAsset.getPath(), Sound.class);
}
public Sound get(SoundAsset soundAsset) {
return this.assetManager.get(soundAsset.getPath(), Sound.class);
}
public boolean update() {
return this.assetManager.update();
}

View File

@@ -1,7 +1,8 @@
package io.github.com.quillraven.asset;
public enum MusicAsset {
TOWN("town.ogg");
TOWN("town.ogg"),
MENU("menu.ogg");
private final String path;

View File

@@ -0,0 +1,15 @@
package io.github.com.quillraven.asset;
public enum SoundAsset {
SWORD_HIT("sword_hit.wav");
private final String path;
SoundAsset(String musicFile) {
this.path = "audio/" + musicFile;
}
public String getPath() {
return path;
}
}

View File

@@ -5,6 +5,7 @@ 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;
import io.github.com.quillraven.asset.SoundAsset;
public class AudioService {
@@ -57,6 +58,10 @@ public class AudioService {
this.currentMusicAsset = musicAsset;
}
public void playSound(SoundAsset soundAsset) {
this.assetService.get(soundAsset).play(this.soundVolume);
}
public void setMap(TiledMap tiledMap) {
String musicAssetStr = tiledMap.getProperties().get("music", "", String.class);
if (musicAssetStr.isBlank()) {

View File

@@ -6,6 +6,7 @@ import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.asset.AssetService;
import io.github.com.quillraven.asset.AtlasAsset;
import io.github.com.quillraven.asset.SkinAsset;
import io.github.com.quillraven.asset.SoundAsset;
public class LoadingScreen extends ScreenAdapter {
@@ -23,6 +24,9 @@ public class LoadingScreen extends ScreenAdapter {
assetService.queue(atlasAsset);
}
assetService.queue(SkinAsset.DEFAULT);
for (SoundAsset soundAsset : SoundAsset.values()) {
assetService.queue(soundAsset);
}
}
@Override

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Skin;
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.MusicAsset;
import io.github.com.quillraven.asset.SkinAsset;
import io.github.com.quillraven.ui.model.MenuViewModel;
import io.github.com.quillraven.ui.view.MenuView;
@@ -38,6 +39,7 @@ public class MenuScreen extends ScreenAdapter {
this.inputMultiplexer.addProcessor(stage);
this.stage.addActor(new MenuView(stage, skin, new MenuViewModel(game)));
this.game.getAudioService().playMusic(MusicAsset.MENU);
}
@Override

View File

@@ -1,18 +1,21 @@
package io.github.com.quillraven.ui.model;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.TimeUtils;
import io.github.com.quillraven.GdxGame;
import io.github.com.quillraven.asset.SoundAsset;
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;
private long lastSndPlayTime;
public MenuViewModel(GdxGame game) {
super(game);
this.audioService = game.getAudioService();
this.lastSndPlayTime = 0L;
}
public float getMusicVolume() {
@@ -20,7 +23,6 @@ public class MenuViewModel extends ViewModel {
}
public void setMusicVolume(float volume) {
this.propertyChangeSupport.firePropertyChange(MUSIC_VOLUME_PROPERTY, getMusicVolume(), volume);
this.audioService.setMusicVolume(volume);
}
@@ -28,6 +30,14 @@ public class MenuViewModel extends ViewModel {
return audioService.getSoundVolume();
}
public void setSoundVolume(float soundVolume) {
this.audioService.setSoundVolume(soundVolume);
if (TimeUtils.timeSinceMillis(lastSndPlayTime) > 500L) {
this.lastSndPlayTime = TimeUtils.millis();
this.audioService.playSound(SoundAsset.SWORD_HIT);
}
}
public void startGame() {
game.setScreen(GameScreen.class);
}

View File

@@ -1,19 +1,53 @@
package io.github.com.quillraven.ui.view;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
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.Slider;
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> {
private final Image selectionImg;
public MenuView(Stage stage, Skin skin, MenuViewModel viewModel) {
super(stage, skin, viewModel);
this.selectionImg = new Image(skin, "selection");
this.selectionImg.setTouchable(Touchable.disabled);
selectMenuItem(this.findActor("startGameBtn"));
}
private void selectMenuItem(Group menuItem) {
if (selectionImg.getParent() != null) {
selectionImg.getParent().removeActor(selectionImg);
}
float extraSize = 7f;
float halfExtraSize = extraSize * 0.5f;
float resizeTime = 0.2f;
menuItem.addActor(selectionImg);
selectionImg.setPosition(-halfExtraSize, -halfExtraSize);
selectionImg.setSize(menuItem.getWidth() + extraSize, menuItem.getHeight() + extraSize);
selectionImg.clearActions();
selectionImg.addAction(Actions.forever(Actions.sequence(
Actions.parallel(
Actions.sizeBy(extraSize, extraSize, resizeTime, Interpolation.linear),
Actions.moveBy(-halfExtraSize, -halfExtraSize, resizeTime, Interpolation.linear)
),
Actions.parallel(
Actions.sizeBy(-extraSize, -extraSize, resizeTime, Interpolation.linear),
Actions.moveBy(halfExtraSize, halfExtraSize, resizeTime, Interpolation.linear)
)
)));
}
@Override
@@ -39,32 +73,37 @@ public class MenuView extends View<MenuViewModel> {
contentTable.padBottom(20.0f);
TextButton textButton = new TextButton("Start Game", skin);
onClick(textButton, ((event, x, y) -> viewModel.startGame()));
textButton.setName("startGameBtn");
onClick(textButton, viewModel::startGame);
onEnter(textButton, this::selectMenuItem);
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());
Slider musicSlider = setupVolumeSlider(contentTable, "Music Volume", viewModel.getMusicVolume());
onChange(musicSlider, (slider) -> viewModel.setMusicVolume(slider.getValue()));
Slider soundSlider = setupVolumeSlider(contentTable, "Sound Volume", viewModel.getSoundVolume());
onChange(soundSlider, (slider) -> viewModel.setSoundVolume(slider.getValue()));
textButton = new TextButton("Quit Game", skin);
onClick(textButton, ((event, x, y) -> viewModel.quitGame()));
onClick(textButton, viewModel::quitGame);
onEnter(textButton, this::selectMenuItem);
contentTable.add(textButton).padTop(10.0f);
add(contentTable).align(Align.top).expandY().padTop(20f).row();
}
private ProgressBar setupVolumeBar(Table contentTable, String title, float initialValue) {
private Slider setupVolumeSlider(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();
Slider slider = new Slider(0.0f, 1f, 0.05f, false, skin);
slider.setValue(initialValue);
table.add(slider);
contentTable.add(table).padTop(10.0f).row();
return progressBar;
onEnter(table, this::selectMenuItem);
return slider;
}
}

View File

@@ -2,9 +2,11 @@ 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.InputListener;
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.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import io.github.com.quillraven.ui.model.ViewModel;
@@ -24,17 +26,40 @@ public abstract class View<T extends ViewModel> extends Table {
protected abstract void setupUI();
public static void onClick(Actor actor, OnClickConsumer consumer) {
public static void onClick(Actor actor, OnEventConsumer consumer) {
actor.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
consumer.onClick(event, x, y);
consumer.onEvent();
}
});
}
public static <T extends Actor> void onEnter(T actor, OnActorEvent<T> consumer) {
actor.addListener(new InputListener() {
@Override
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
consumer.onEvent(actor);
}
});
}
public static <T extends Actor> void onChange(T actor, OnActorEvent<T> consumer) {
actor.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor eventActor) {
consumer.onEvent(actor);
}
});
}
@FunctionalInterface
public interface OnClickConsumer {
void onClick(InputEvent event, float x, float y);
public interface OnEventConsumer {
void onEvent();
}
@FunctionalInterface
public interface OnActorEvent<T extends Actor> {
void onEvent(T actor);
}
}

Binary file not shown.