diff --git a/README.md b/README.md index b140d46..5ea3b69 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/assets/audio/menu.ogg b/assets/audio/menu.ogg new file mode 100644 index 0000000..84268f6 Binary files /dev/null and b/assets/audio/menu.ogg differ diff --git a/assets/audio/sword_hit.wav b/assets/audio/sword_hit.wav new file mode 100644 index 0000000..dcccc28 Binary files /dev/null and b/assets/audio/sword_hit.wav differ diff --git a/assets/ui/skin.atlas b/assets/ui/skin.atlas index 6a52365..6cb1c3b 100644 --- a/assets/ui/skin.atlas +++ b/assets/ui/skin.atlas @@ -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 diff --git a/assets/ui/skin.json b/assets/ui/skin.json index a70e41c..cfe95df 100644 Binary files a/assets/ui/skin.json and b/assets/ui/skin.json differ diff --git a/assets/ui/skin.png b/assets/ui/skin.png index cb2307a..8d66176 100644 Binary files a/assets/ui/skin.png and b/assets/ui/skin.png differ diff --git a/assets_raw/ui/selection.9.png b/assets_raw/ui/selection.9.png new file mode 100644 index 0000000..a144d35 Binary files /dev/null and b/assets_raw/ui/selection.9.png differ diff --git a/core/src/main/java/io/github/com/quillraven/asset/AssetService.java b/core/src/main/java/io/github/com/quillraven/asset/AssetService.java index 0205048..36fdac9 100644 --- a/core/src/main/java/io/github/com/quillraven/asset/AssetService.java +++ b/core/src/main/java/io/github/com/quillraven/asset/AssetService.java @@ -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(); } diff --git a/core/src/main/java/io/github/com/quillraven/asset/MusicAsset.java b/core/src/main/java/io/github/com/quillraven/asset/MusicAsset.java index 94558bb..00088bb 100644 --- a/core/src/main/java/io/github/com/quillraven/asset/MusicAsset.java +++ b/core/src/main/java/io/github/com/quillraven/asset/MusicAsset.java @@ -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; diff --git a/core/src/main/java/io/github/com/quillraven/asset/SoundAsset.java b/core/src/main/java/io/github/com/quillraven/asset/SoundAsset.java new file mode 100644 index 0000000..f16a61d --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/asset/SoundAsset.java @@ -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; + } +} diff --git a/core/src/main/java/io/github/com/quillraven/audio/AudioService.java b/core/src/main/java/io/github/com/quillraven/audio/AudioService.java index 294ae63..f0beaeb 100644 --- a/core/src/main/java/io/github/com/quillraven/audio/AudioService.java +++ b/core/src/main/java/io/github/com/quillraven/audio/AudioService.java @@ -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()) { diff --git a/core/src/main/java/io/github/com/quillraven/screen/LoadingScreen.java b/core/src/main/java/io/github/com/quillraven/screen/LoadingScreen.java index 60fe545..fe4173a 100644 --- a/core/src/main/java/io/github/com/quillraven/screen/LoadingScreen.java +++ b/core/src/main/java/io/github/com/quillraven/screen/LoadingScreen.java @@ -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 diff --git a/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java b/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java index 640a6a6..cc80c90 100644 --- a/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java +++ b/core/src/main/java/io/github/com/quillraven/screen/MenuScreen.java @@ -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 diff --git a/core/src/main/java/io/github/com/quillraven/ui/model/MenuViewModel.java b/core/src/main/java/io/github/com/quillraven/ui/model/MenuViewModel.java index ba06b89..8e8e271 100644 --- a/core/src/main/java/io/github/com/quillraven/ui/model/MenuViewModel.java +++ b/core/src/main/java/io/github/com/quillraven/ui/model/MenuViewModel.java @@ -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); } diff --git a/core/src/main/java/io/github/com/quillraven/ui/view/MenuView.java b/core/src/main/java/io/github/com/quillraven/ui/view/MenuView.java index dd0ab92..b3578d8 100644 --- a/core/src/main/java/io/github/com/quillraven/ui/view/MenuView.java +++ b/core/src/main/java/io/github/com/quillraven/ui/view/MenuView.java @@ -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 { + 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 { 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; } } diff --git a/core/src/main/java/io/github/com/quillraven/ui/view/View.java b/core/src/main/java/io/github/com/quillraven/ui/view/View.java index d753319..642b71f 100644 --- a/core/src/main/java/io/github/com/quillraven/ui/view/View.java +++ b/core/src/main/java/io/github/com/quillraven/ui/view/View.java @@ -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 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 void onEnter(T actor, OnActorEvent consumer) { + actor.addListener(new InputListener() { + @Override + public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) { + consumer.onEvent(actor); + } + }); + } + + public static void onChange(T actor, OnActorEvent 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 { + void onEvent(T actor); } } diff --git a/sceneComposer.scmp b/sceneComposer.scmp index ed71f1f..d167303 100644 Binary files a/sceneComposer.scmp and b/sceneComposer.scmp differ