diff --git a/core/src/main/java/io/github/com/quillraven/input/Command.java b/core/src/main/java/io/github/com/quillraven/input/Command.java index 40ef8e3..2ce8e82 100644 --- a/core/src/main/java/io/github/com/quillraven/input/Command.java +++ b/core/src/main/java/io/github/com/quillraven/input/Command.java @@ -4,5 +4,6 @@ public enum Command { LEFT, RIGHT, DOWN, - UP + UP, + SELECT } diff --git a/core/src/main/java/io/github/com/quillraven/input/ControllerState.java b/core/src/main/java/io/github/com/quillraven/input/ControllerState.java index 000db78..cd71b3e 100644 --- a/core/src/main/java/io/github/com/quillraven/input/ControllerState.java +++ b/core/src/main/java/io/github/com/quillraven/input/ControllerState.java @@ -3,5 +3,6 @@ package io.github.com.quillraven.input; public interface ControllerState { void keyDown(Command command); - void keyUp(Command command); + default void keyUp(Command command) { + } } diff --git a/core/src/main/java/io/github/com/quillraven/input/GameControllerState.java b/core/src/main/java/io/github/com/quillraven/input/GameControllerState.java index d420e7e..ded8cba 100644 --- a/core/src/main/java/io/github/com/quillraven/input/GameControllerState.java +++ b/core/src/main/java/io/github/com/quillraven/input/GameControllerState.java @@ -1,15 +1,18 @@ package io.github.com.quillraven.input; +import com.badlogic.ashley.core.Engine; import com.badlogic.ashley.core.Entity; +import com.badlogic.ashley.core.Family; import com.badlogic.ashley.utils.ImmutableArray; +import io.github.com.quillraven.component.Controller; import io.github.com.quillraven.component.Move; public class GameControllerState implements ControllerState { private final ImmutableArray controllerEntities; - public GameControllerState(ImmutableArray controllerEntities) { - this.controllerEntities = controllerEntities; + public GameControllerState(Engine engine) { + this.controllerEntities = engine.getEntitiesFor(Family.all(Controller.class).get()); } private void moveEntities(float dx, float dy) { diff --git a/core/src/main/java/io/github/com/quillraven/input/KeyboardController.java b/core/src/main/java/io/github/com/quillraven/input/KeyboardController.java index 8678cca..d32a204 100644 --- a/core/src/main/java/io/github/com/quillraven/input/KeyboardController.java +++ b/core/src/main/java/io/github/com/quillraven/input/KeyboardController.java @@ -1,9 +1,9 @@ package io.github.com.quillraven.input; -import com.badlogic.ashley.core.Entity; -import com.badlogic.ashley.utils.ImmutableArray; +import com.badlogic.ashley.core.Engine; import com.badlogic.gdx.Input; import com.badlogic.gdx.InputAdapter; +import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.utils.GdxRuntimeException; import java.util.HashMap; @@ -14,20 +14,28 @@ public class KeyboardController extends InputAdapter { Map.entry(Input.Keys.W, Command.UP), Map.entry(Input.Keys.S, Command.DOWN), Map.entry(Input.Keys.A, Command.LEFT), - Map.entry(Input.Keys.D, Command.RIGHT) + Map.entry(Input.Keys.D, Command.RIGHT), + Map.entry(Input.Keys.SPACE, Command.SELECT) ); private final boolean[] commandState; private final Map, ControllerState> stateCache; private ControllerState activeState; - public KeyboardController(Class initialState, ImmutableArray controllerEntities) { + public KeyboardController(Class initialState, + Engine engine, + Stage stage) { this.commandState = new boolean[Command.values().length]; this.stateCache = new HashMap<>(); this.activeState = null; this.stateCache.put(IdleControllerState.class, new IdleControllerState()); - this.stateCache.put(GameControllerState.class, new GameControllerState(controllerEntities)); + if (engine != null) { + this.stateCache.put(GameControllerState.class, new GameControllerState(engine)); + } + if (stage != null) { + this.stateCache.put(UiControllerState.class, new UiControllerState(stage)); + } setActiveState(initialState); } diff --git a/core/src/main/java/io/github/com/quillraven/input/UiControllerState.java b/core/src/main/java/io/github/com/quillraven/input/UiControllerState.java new file mode 100644 index 0000000..8496d04 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/input/UiControllerState.java @@ -0,0 +1,17 @@ +package io.github.com.quillraven.input; + +import com.badlogic.gdx.scenes.scene2d.Stage; + +public class UiControllerState implements ControllerState { + + private final Stage stage; + + public UiControllerState(Stage stage) { + this.stage = stage; + } + + @Override + public void keyDown(Command command) { + this.stage.getRoot().fire(new UiEvent(command)); + } +} diff --git a/core/src/main/java/io/github/com/quillraven/input/UiEvent.java b/core/src/main/java/io/github/com/quillraven/input/UiEvent.java new file mode 100644 index 0000000..f737428 --- /dev/null +++ b/core/src/main/java/io/github/com/quillraven/input/UiEvent.java @@ -0,0 +1,15 @@ +package io.github.com.quillraven.input; + +import com.badlogic.gdx.scenes.scene2d.Event; + +public class UiEvent extends Event { + private final Command command; + + public UiEvent(Command command) { + this.command = command; + } + + public Command getCommand() { + return command; + } +} diff --git a/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java b/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java index 6f94cc2..b3ecc3f 100644 --- a/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java +++ b/core/src/main/java/io/github/com/quillraven/screen/GameScreen.java @@ -1,10 +1,7 @@ package io.github.com.quillraven.screen; import com.badlogic.ashley.core.Engine; -import com.badlogic.ashley.core.Entity; import com.badlogic.ashley.core.EntitySystem; -import com.badlogic.ashley.core.Family; -import com.badlogic.ashley.utils.ImmutableArray; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.ScreenAdapter; @@ -15,7 +12,6 @@ import com.badlogic.gdx.utils.Disposable; import io.github.com.quillraven.GdxGame; import io.github.com.quillraven.asset.MapAsset; import io.github.com.quillraven.audio.AudioService; -import io.github.com.quillraven.component.Controller; import io.github.com.quillraven.input.GameControllerState; import io.github.com.quillraven.input.KeyboardController; import io.github.com.quillraven.system.AnimationSystem; @@ -48,8 +44,7 @@ public class GameScreen extends ScreenAdapter { this.physicWorld.setAutoClearForces(false); this.engine = new Engine(); this.tiledAshleySpawner = new TiledAshleySpawner(this.engine, this.physicWorld, this.game.getAssetService()); - ImmutableArray controllerEntities = this.engine.getEntitiesFor(Family.all(Controller.class).get()); - this.keyboardController = new KeyboardController(GameControllerState.class, controllerEntities); + this.keyboardController = new KeyboardController(GameControllerState.class, engine, null); // add ECS systems this.engine.addSystem(new PhysicMoveSystem()); 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 cc80c90..c3b75c9 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 @@ -9,6 +9,8 @@ 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.input.KeyboardController; +import io.github.com.quillraven.input.UiControllerState; import io.github.com.quillraven.ui.model.MenuViewModel; import io.github.com.quillraven.ui.view.MenuView; @@ -19,12 +21,14 @@ public class MenuScreen extends ScreenAdapter { private final Skin skin; private final Viewport uiViewport; private final InputMultiplexer inputMultiplexer; + private final KeyboardController keyboardController; public MenuScreen(GdxGame game) { this.game = game; this.uiViewport = new FitViewport(800f, 450f); this.stage = new Stage(uiViewport, game.getBatch()); this.skin = game.getAssetService().get(SkinAsset.DEFAULT); + this.keyboardController = new KeyboardController(UiControllerState.class, null, stage); this.inputMultiplexer = game.getInputMultiplexer(); } @@ -37,6 +41,7 @@ public class MenuScreen extends ScreenAdapter { public void show() { this.inputMultiplexer.clear(); this.inputMultiplexer.addProcessor(stage); + this.inputMultiplexer.addProcessor(keyboardController); this.stage.addActor(new MenuView(stage, skin, new MenuViewModel(game))); this.game.getAudioService().playMusic(MusicAsset.MENU); 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 b3578d8..0fbecb9 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 @@ -12,23 +12,27 @@ 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 com.badlogic.gdx.utils.GdxRuntimeException; import io.github.com.quillraven.ui.model.MenuViewModel; public class MenuView extends View { private final Image selectionImg; + private Group selectedItem; 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")); + this.selectedItem = findActor(MenuOption.START_GAME.name()); + selectMenuItem(this.selectedItem); } private void selectMenuItem(Group menuItem) { if (selectionImg.getParent() != null) { selectionImg.getParent().removeActor(selectionImg); } + this.selectedItem = menuItem; float extraSize = 7f; float halfExtraSize = extraSize * 0.5f; @@ -73,18 +77,21 @@ public class MenuView extends View { contentTable.padBottom(20.0f); TextButton textButton = new TextButton("Start Game", skin); - textButton.setName("startGameBtn"); + textButton.setName(MenuOption.START_GAME.name()); onClick(textButton, viewModel::startGame); onEnter(textButton, this::selectMenuItem); contentTable.add(textButton).row(); - Slider musicSlider = setupVolumeSlider(contentTable, "Music Volume", viewModel.getMusicVolume()); + Slider musicSlider = setupVolumeSlider(contentTable, "Music Volume", MenuOption.MUSIC_VOLUME); + musicSlider.setValue(viewModel.getMusicVolume()); onChange(musicSlider, (slider) -> viewModel.setMusicVolume(slider.getValue())); - Slider soundSlider = setupVolumeSlider(contentTable, "Sound Volume", viewModel.getSoundVolume()); + Slider soundSlider = setupVolumeSlider(contentTable, "Sound Volume", MenuOption.SOUND_VOLUME); + soundSlider.setValue(viewModel.getSoundVolume()); onChange(soundSlider, (slider) -> viewModel.setSoundVolume(slider.getValue())); textButton = new TextButton("Quit Game", skin); + textButton.setName(MenuOption.QUIT_GAME.name()); onClick(textButton, viewModel::quitGame); onEnter(textButton, this::selectMenuItem); contentTable.add(textButton).padTop(10.0f); @@ -92,18 +99,82 @@ public class MenuView extends View { add(contentTable).align(Align.top).expandY().padTop(20f).row(); } - private Slider setupVolumeSlider(Table contentTable, String title, float initialValue) { + private Slider setupVolumeSlider(Table contentTable, String title, MenuOption menuOption) { Table table = new Table(); + table.setName(menuOption.name()); Label label = new Label(title, skin); label.setColor(skin.getColor("sand")); table.add(label).row(); Slider slider = new Slider(0.0f, 1f, 0.05f, false, skin); - slider.setValue(initialValue); table.add(slider); contentTable.add(table).padTop(10.0f).row(); onEnter(table, this::selectMenuItem); return slider; } + + @Override + public void onDown() { + Group menuContentTable = this.selectedItem.getParent(); + int currentIdx = menuContentTable.getChildren().indexOf(this.selectedItem, true); + if (currentIdx == -1) { + throw new GdxRuntimeException("'selectedItem' is not a child of 'menuContentTable'"); + } + + int numOptions = menuContentTable.getChildren().size; + currentIdx = (currentIdx + 1) % numOptions; + selectMenuItem((Group) menuContentTable.getChild(currentIdx)); + } + + @Override + public void onUp() { + Group menuContentTable = this.selectedItem.getParent(); + int currentIdx = menuContentTable.getChildren().indexOf(this.selectedItem, true); + if (currentIdx == -1) { + throw new GdxRuntimeException("'selectedItem' is not a child of 'menuContentTable'"); + } + + int numOptions = menuContentTable.getChildren().size; + currentIdx = currentIdx == 0 ? numOptions - 1 : currentIdx - 1; + selectMenuItem((Group) menuContentTable.getChild(currentIdx)); + } + + @Override + public void onRight() { + MenuOption menuOption = MenuOption.valueOf(this.selectedItem.getName()); + switch (menuOption) { + case MUSIC_VOLUME, SOUND_VOLUME -> { + Slider slider = (Slider) this.selectedItem.getChild(1); + slider.setValue(slider.getValue() + slider.getStepSize()); + } + } + } + + @Override + public void onLeft() { + MenuOption menuOption = MenuOption.valueOf(this.selectedItem.getName()); + switch (menuOption) { + case MUSIC_VOLUME, SOUND_VOLUME -> { + Slider slider = (Slider) this.selectedItem.getChild(1); + slider.setValue(slider.getValue() - slider.getStepSize()); + } + } + } + + @Override + public void onSelect() { + MenuOption menuOption = MenuOption.valueOf(this.selectedItem.getName()); + switch (menuOption) { + case START_GAME -> viewModel.startGame(); + case QUIT_GAME -> viewModel.quitGame(); + } + } + + private enum MenuOption { + START_GAME, + MUSIC_VOLUME, + SOUND_VOLUME, + QUIT_GAME + } } 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 642b71f..1cb7300 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 @@ -1,6 +1,8 @@ package io.github.com.quillraven.ui.view; import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Event; +import com.badlogic.gdx.scenes.scene2d.EventListener; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Stage; @@ -8,9 +10,10 @@ 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.input.UiEvent; import io.github.com.quillraven.ui.model.ViewModel; -public abstract class View extends Table { +public abstract class View extends Table implements EventListener { protected final Stage stage; protected final Skin skin; @@ -21,11 +24,43 @@ public abstract class View extends Table { this.stage = stage; this.skin = skin; this.viewModel = viewModel; + this.stage.addListener(this); setupUI(); } protected abstract void setupUI(); + public void onLeft() { + } + + public void onRight() { + } + + public void onUp() { + } + + public void onDown() { + } + + public void onSelect() { + } + + @Override + public boolean handle(Event event) { + if (event instanceof UiEvent uiEvent) { + switch (uiEvent.getCommand()) { + case LEFT -> onLeft(); + case RIGHT -> onRight(); + case UP -> onUp(); + case DOWN -> onDown(); + case SELECT -> onSelect(); + } + return true; + } + + return false; + } + public static void onClick(Actor actor, OnEventConsumer consumer) { actor.addListener(new ClickListener() { @Override