From ba2cc8e6ef30d12c8507c46750e44ba558206d6a Mon Sep 17 00:00:00 2001 From: Matthew Grove Date: Thu, 6 Apr 2023 20:13:14 +0100 Subject: [PATCH] [FEAT] Several new usability features Keyboard controls Current piece and following piece now shown, with ability to swap between them Animation to fade out game blocks when clearing Pieces can be rotated before playing, with keyboard or mouse controls Currently-focussed block is highlighted Indicator shown on middle block of current piece board --- .../soton/comp1206/component/GameBlock.java | 111 +++++++++++++++++- .../soton/comp1206/component/GameBoard.java | 108 ++++++++++++++++- .../soton/comp1206/component/PieceBoard.java | 20 +++- .../comp1206/event/LineClearedListener.java | 11 ++ .../comp1206/event/MouseClickListener.java | 13 ++ .../comp1206/event/NextPieceListener.java | 5 +- .../mgrove/ac/soton/comp1206/game/Game.java | 101 ++++++++++++++-- .../soton/comp1206/scene/ChallengeScene.java | 53 +++++++-- .../comp1206/scene/InstructionsScene.java | 4 +- 9 files changed, 394 insertions(+), 32 deletions(-) create mode 100644 src/main/java/uk/mgrove/ac/soton/comp1206/event/LineClearedListener.java create mode 100644 src/main/java/uk/mgrove/ac/soton/comp1206/event/MouseClickListener.java diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBlock.java b/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBlock.java index 33b318b..4b347d7 100644 --- a/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBlock.java +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBlock.java @@ -1,5 +1,6 @@ package uk.mgrove.ac.soton.comp1206.component; +import javafx.animation.AnimationTimer; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.ObservableValue; @@ -63,6 +64,16 @@ public class GameBlock extends Canvas { */ private final IntegerProperty value = new SimpleIntegerProperty(0); + /** + * Whether the block should appear as focussed + */ + private boolean isFocussed = false; + + /** + * Whether indicator should be shown in the middle of the block + */ + private boolean showMiddleIndicator = false; + /** * Create a new single Game Block * @param gameBoard the board this block belongs to @@ -110,12 +121,18 @@ public class GameBlock extends Canvas { //If the block is not empty, paint with the colour represented by the value paintColor(COLOURS[value.get()]); } + // paint focus overlay if required + if (isFocussed) paintFocusOverlay(); + + // paint middle indicator if required + if (showMiddleIndicator) paintMiddleIndicator(); } /** * Paint this canvas empty */ private void paintEmpty() { + logger.info("Painting empty block at x: {}, y: {}", getX(), getY()); var gc = getGraphicsContext2D(); //Clear @@ -132,23 +149,111 @@ public class GameBlock extends Canvas { /** * Paint this canvas with the given colour - * @param colour the colour to paint + * @param color the colour to paint */ - private void paintColor(Paint colour) { + private void paintColor(Color color) { + logger.info("Painting color block at x: {}, y: {}", getX(), getY()); var gc = getGraphicsContext2D(); //Clear gc.clearRect(0,0,width,height); //Colour fill - gc.setFill(colour); + gc.setFill(color); gc.fillRect(0,0, width, height); + gc.setFill(color.deriveColor(0,1,0.8,1)); + gc.fillPolygon(new double[]{ + width, + width, + 0.0 + }, new double[]{ + 0, + height, + 0 + }, 3 + ); //Border gc.setStroke(Color.GRAY); gc.strokeRect(0,0,width,height); } + /** + * Paint indicator in middle of block + */ + private void paintMiddleIndicator() { + logger.info("Painting indicator on block at x: {}, y: {}", getX(), getY()); + + var gc = getGraphicsContext2D(); + gc.setFill(Color.WHITE.deriveColor(0,1,1,0.7)); + gc.fillOval(width/4, height/4, width/2, height/2); + } + + /** + * Paint overlay to indicate focus + */ + private void paintFocusOverlay() { + logger.info("Painting focus overlay on block at x: {}, y: {}", getX(), getY()); + + var gc = getGraphicsContext2D(); + gc.setFill(Color.WHITE.deriveColor(0,1,1,0.5)); + gc.fillRect(0,0,width,height); + } + + /** + * Set whether the block should appear as focussed + * @param value whether block should appear as focussed + */ + public void setFocussed(boolean value) { + logger.info("Block at x: {}, y: {} has been set to focus: {}", getX(), getY(), value); + isFocussed = value; + // TODO: add/remove overlay + paint(); + } + + /** + * Set whether an indicator should be shown in the middle of the block + * @param value whether indicator should be shown + */ + public void showMiddleIndicator(boolean value) { + logger.info("Setting middle indicator on block at x: {}, y: {} to: {}", getX(), getY(), value); + showMiddleIndicator = value; + paint(); + } + + /** + * Fade out block + */ + public void fadeOut() { + logger.info("Fading out block at x: {}, y: {}", getX(), getY()); + + AnimationTimer timer = new AnimationTimer() { + Color color = COLOURS[getValue()]; + int iterations = 0; + @Override + public void handle(long now) { + if (iterations < 12) { + color = color.deriveColor(0,1,1.2,1); + paintColor(color); + logger.info("New color brightness is {}", color.getBrightness()); + if (isFocussed) paintFocusOverlay(); + } else if (color.getOpacity() > 0.1) { + color = color.deriveColor(0,1,1,0.95); + paintColor(color); + logger.info("New color opacity is {}", color.getOpacity()); + if (isFocussed) paintFocusOverlay(); + } else { + paintEmpty(); + if (isFocussed) paintFocusOverlay(); + logger.info("Stopping timer at x: {}, y: {}", getX(), getY()); + stop(); + } + iterations++; + } + }; + timer.start(); + } + /** * Get the column of this block * @return column number diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBoard.java b/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBoard.java index 2ed9c35..81608ca 100644 --- a/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBoard.java +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/component/GameBoard.java @@ -1,11 +1,16 @@ package uk.mgrove.ac.soton.comp1206.component; +import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.layout.GridPane; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.mgrove.ac.soton.comp1206.event.BlockClickedListener; +import uk.mgrove.ac.soton.comp1206.event.MouseClickListener; import uk.mgrove.ac.soton.comp1206.game.Grid; +import uk.mgrove.ac.soton.comp1206.util.Multimedia; + +import java.util.Set; /** * A GameBoard is a visual component to represent the visual GameBoard. @@ -56,6 +61,25 @@ public class GameBoard extends GridPane { */ private BlockClickedListener blockClickedListener; + /** + * Listener to call when board is right-clicked + */ + protected MouseClickListener rightClickListener; + + /** + * Listener for board being left-clicked + */ + protected MouseClickListener leftClickListener; + + /** + * Currently focussed game block + */ + private GameBlock focussedBlock; + + /** + * Whether focus overlays should be shown + */ + protected boolean enableFocus = true; /** * Create a new GameBoard, based off a given grid, with a visual width and height. @@ -122,6 +146,15 @@ public class GameBoard extends GridPane { createBlock(x,y); } } + + setOnMouseClicked((event) -> { + logger.debug("Mouse button clicked: {}", event.getButton()); + if (event.getButton().equals(MouseButton.SECONDARY) && rightClickListener != null) { + rightClickListener.action(); + } else if (event.getButton().equals(MouseButton.PRIMARY) && leftClickListener != null) { + leftClickListener.action(); + } + }); } /** @@ -130,6 +163,7 @@ public class GameBoard extends GridPane { * @param y row */ protected GameBlock createBlock(int x, int y) { + logger.info("Creating new block at x: {}, y: {}", x, y); var blockWidth = width / cols; var blockHeight = height / rows; @@ -146,7 +180,12 @@ public class GameBoard extends GridPane { block.bind(grid.getGridProperty(x,y)); //Add a mouse click handler to the block to trigger GameBoard blockClicked method - block.setOnMouseClicked((e) -> blockClicked(e, block)); + block.setOnMouseClicked((e) -> { + if (e.getButton().equals(MouseButton.PRIMARY)) blockClicked(e, block); + }); + block.hoverProperty().addListener(((observable, oldValue, newValue) -> { + if (newValue) blockHovered(block); + })); return block; } @@ -172,4 +211,71 @@ public class GameBoard extends GridPane { } } + public void blockHovered(GameBlock gameBlock) { + if (enableFocus) { + logger.info("Block hovered: {}", gameBlock); + if (focussedBlock != null) focussedBlock.setFocussed(false); + focussedBlock = gameBlock; + focussedBlock.setFocussed(true); + } + } + + /** + * Change current x-coordinate focus by given amount + * @param amount amount to change by + */ + public void changeXFocus(int amount) { + logger.info("Change of {} requested to x-coordinate of focussed block", amount); + if (focussedBlock == null) blockHovered(getBlock(0,0)); + else if (focussedBlock.getX() + amount >= 0 && focussedBlock.getX() + amount < cols) blockHovered(getBlock(focussedBlock.getX() + amount, focussedBlock.getY())); + else Multimedia.playAudio("sounds/fail.wav"); + } + + /** + * Change current y-coordinate focus by given amount + * @param amount amount to change by + */ + public void changeYFocus(int amount) { + logger.info("Change of {} requested to y-coordinate of focussed block", amount); + if (focussedBlock == null) blockHovered(getBlock(0,0)); + else if (focussedBlock.getY() + amount >= 0 && focussedBlock.getY() + amount < rows) blockHovered(getBlock(focussedBlock.getX(), focussedBlock.getY() + amount)); + else Multimedia.playAudio("sounds/fail.wav"); + } + + /** + * Fade out all blocks located at a set of coordinates + * @param blockCoordinates block coordinates + */ + public void fadeOut(Set blockCoordinates) { + for (var block : blockCoordinates) { + getBlock(block.getX(), block.getY()).fadeOut(); + } + } + + /** + * Get the x-coordinate of the currently-focussed block + * @return x-coordinate of block + */ + public int getXFocus() { + if (focussedBlock != null) return focussedBlock.getX(); + else return 0; + } + + /** + * Get the y-coordinate of the currently-focussed block + * @return y-coordinate of block + */ + public int getYFocus() { + if (focussedBlock != null) return focussedBlock.getY(); + else return 0; + } + + public void setOnRightClicked(MouseClickListener listener) { + rightClickListener = listener; + } + + public void setOnLeftClicked(MouseClickListener listener) { + leftClickListener = listener; + } + } diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/component/PieceBoard.java b/src/main/java/uk/mgrove/ac/soton/comp1206/component/PieceBoard.java index 7cc584e..1eb25f0 100644 --- a/src/main/java/uk/mgrove/ac/soton/comp1206/component/PieceBoard.java +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/component/PieceBoard.java @@ -1,12 +1,18 @@ package uk.mgrove.ac.soton.comp1206.component; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import uk.mgrove.ac.soton.comp1206.game.GamePiece; -import uk.mgrove.ac.soton.comp1206.game.Grid; public class PieceBoard extends GameBoard { /** - * Create a new GameBoard with it's own internal grid, specifying the number of columns and rows, along with the + * Logger + */ + private static final Logger logger = LogManager.getLogger(PieceBoard.class); + + /** + * Create a new GameBoard with its own internal grid, specifying the number of columns and rows, along with the * visual width and height. * * @param width the visual width @@ -14,11 +20,21 @@ public class PieceBoard extends GameBoard { */ public PieceBoard(double width, double height) { super(3, 3, width, height); + enableFocus = false; } public void displayPiece(GamePiece piece) { + logger.info("Displaying new piece: {}", piece.toString()); grid.clearGrid(); grid.playPiece(piece,1,1); } + /** + * Set whether an indicator should be shown on the middle block of the grid + * @param value whether indicator should be shown + */ + public void showMiddleIndicator(boolean value) { + getBlock(1,1).showMiddleIndicator(value); + } + } diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/event/LineClearedListener.java b/src/main/java/uk/mgrove/ac/soton/comp1206/event/LineClearedListener.java new file mode 100644 index 0000000..66be8e5 --- /dev/null +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/event/LineClearedListener.java @@ -0,0 +1,11 @@ +package uk.mgrove.ac.soton.comp1206.event; + +import uk.mgrove.ac.soton.comp1206.component.GameBlockCoordinate; + +import java.util.Set; + +public interface LineClearedListener { + + public void clearLine(Set blockCoordinates); + +} diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/event/MouseClickListener.java b/src/main/java/uk/mgrove/ac/soton/comp1206/event/MouseClickListener.java new file mode 100644 index 0000000..0d7ae5e --- /dev/null +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/event/MouseClickListener.java @@ -0,0 +1,13 @@ +package uk.mgrove.ac.soton.comp1206.event; + +/** + * Listener for a node being right-clicked + */ +public interface MouseClickListener { + + /** + * Handle node being right-clicked + */ + public void action(); + +} diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/event/NextPieceListener.java b/src/main/java/uk/mgrove/ac/soton/comp1206/event/NextPieceListener.java index eabed6f..93f5fc4 100644 --- a/src/main/java/uk/mgrove/ac/soton/comp1206/event/NextPieceListener.java +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/event/NextPieceListener.java @@ -9,7 +9,8 @@ public interface NextPieceListener { /** * Handle a new piece being received by the game - * @param piece the piece that was received + * @param currentPiece the piece that was received + * @param followingPiece the piece that was received for the next iteration */ - public void nextPiece(GamePiece piece); + public void nextPiece(GamePiece currentPiece, GamePiece followingPiece); } \ No newline at end of file diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/game/Game.java b/src/main/java/uk/mgrove/ac/soton/comp1206/game/Game.java index df28c18..29e7ef6 100644 --- a/src/main/java/uk/mgrove/ac/soton/comp1206/game/Game.java +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/game/Game.java @@ -5,7 +5,10 @@ import javafx.beans.property.SimpleIntegerProperty; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.mgrove.ac.soton.comp1206.component.GameBlock; +import uk.mgrove.ac.soton.comp1206.component.GameBlockCoordinate; +import uk.mgrove.ac.soton.comp1206.event.LineClearedListener; import uk.mgrove.ac.soton.comp1206.event.NextPieceListener; +import uk.mgrove.ac.soton.comp1206.util.Multimedia; import java.util.*; @@ -45,6 +48,11 @@ public class Game { */ private GamePiece currentPiece; + /** + * Next game piece for player to use + */ + private GamePiece followingPiece; + /** * Player's current score */ @@ -67,6 +75,11 @@ public class Game { */ private NextPieceListener nextPieceListener; + /** + * Listener for when lines of blocks are cleared + */ + private LineClearedListener lineClearedListener; + /** * Create a new game with the specified rows and columns. Creates a corresponding grid model. * @param cols number of columns @@ -93,12 +106,24 @@ public class Game { * @return the next piece */ public GamePiece nextPiece() { - currentPiece = spawnPiece(); - if (nextPieceListener != null) nextPieceListener.nextPiece(currentPiece); - logger.info("Next piece is: {}", currentPiece); + currentPiece = followingPiece; + followingPiece = spawnPiece(); + if (nextPieceListener != null) nextPieceListener.nextPiece(currentPiece, followingPiece); + logger.info("Next piece is: {} and following piece is: {}", currentPiece, followingPiece); return currentPiece; } + /** + * Swap the current and next game pieces + */ + public void swapPieces() { + logger.info("Swapping current and next pieces"); + var tmp = currentPiece; + currentPiece = followingPiece; + followingPiece = tmp; + if (nextPieceListener != null) nextPieceListener.nextPiece(currentPiece, followingPiece); + } + /** * Create a new game piece * @return newly created piece @@ -115,6 +140,7 @@ public class Game { */ public void initialiseGame() { logger.info("Initialising game"); + followingPiece = spawnPiece(); nextPiece(); } @@ -127,12 +153,27 @@ public class Game { int x = gameBlock.getX(); int y = gameBlock.getY(); + dropPiece(x,y); + } + + /** + * Play current piece at given coordinates + * @param x x-coordinate + * @param y y-coordinate + */ + public void dropPiece(int x, int y) { if (grid.canPlayPiece(currentPiece,x,y)) { + logger.info("Playing piece at x: {}, y: {}", x, y); grid.playPiece(currentPiece,x,y); afterPiece(); nextPiece(); + + Multimedia.playAudio("sounds/place.wav"); } else { // can't play the piece + logger.info("Couldn't play piece at x: {}, y: {}", x, y); + + Multimedia.playAudio("sounds/fail.wav"); } } @@ -143,20 +184,22 @@ public class Game { logger.info("Checking for columns and rows that need clearing"); Set blocksToRemove = new HashSet<>(); + Set blockCoordinatesToRemove = new HashSet<>(); int linesToRemove = 0; for (var x=0; x < grid.getCols(); x++) { - List columnBlocksToRemove = new ArrayList<>(); + Map columnBlocksToRemove = new HashMap<>(); for (var y=0; y < grid.getRows(); y++) { // if column isn't full then move to next column if (grid.get(x,y) <= 0) break; - columnBlocksToRemove.add(grid.getGridProperty(x,y)); + columnBlocksToRemove.put(new GameBlockCoordinate(x,y), grid.getGridProperty(x,y)); } // if column is full then store blocks to reset if (columnBlocksToRemove.size() == grid.getRows()) { - for (var block : columnBlocksToRemove) { - blocksToRemove.add(block); + for (var coordinates : columnBlocksToRemove.keySet()) { + blocksToRemove.add(columnBlocksToRemove.get(coordinates)); + blockCoordinatesToRemove.add(coordinates); linesToRemove++; } } @@ -164,22 +207,28 @@ public class Game { // do the same for rows for (var y=0; y < grid.getRows(); y++) { - List rowBlocksToRemove = new ArrayList<>(); + Map rowBlocksToRemove = new HashMap<>(); for (var x=0; x < grid.getCols(); x++) { // if row isn't full then move to next row if (grid.get(x,y) <= 0) break; - rowBlocksToRemove.add(grid.getGridProperty(x,y)); + rowBlocksToRemove.put(new GameBlockCoordinate(x,y), grid.getGridProperty(x,y)); } // if row is full then store blocks to reset if (rowBlocksToRemove.size() == grid.getCols()) { - for (var block : rowBlocksToRemove) { - blocksToRemove.add(block); + for (var coordinates : rowBlocksToRemove.keySet()) { + blocksToRemove.add(rowBlocksToRemove.get(coordinates)); + blockCoordinatesToRemove.add(coordinates); linesToRemove++; } } } + if (linesToRemove > 0) { + Multimedia.playAudio("sounds/clear.wav"); + if (lineClearedListener != null) lineClearedListener.clearLine(blockCoordinatesToRemove); + } + // update score and multiplier score(linesToRemove,blocksToRemove.size()); @@ -209,6 +258,28 @@ public class Game { level.set((int) Math.floor((double) score.get() / 1000)); } + /** + * Rotate the current piece clockwise + */ + public void rotateCurrentPiece() { + logger.info("Rotating current piece clockwise"); + currentPiece.rotate(); + Multimedia.playAudio("sounds/rotate.wav"); + + if (nextPieceListener != null) nextPieceListener.nextPiece(currentPiece, followingPiece); + } + + /** + * Rotate the current piece anticlockwise + */ + public void rotateCurrentPieceAnticlockwise() { + logger.info("Rotating current piece anticlockwise"); + currentPiece.rotate(3); + Multimedia.playAudio("sounds/rotate.wav"); + + if (nextPieceListener != null) nextPieceListener.nextPiece(currentPiece, followingPiece); + } + /** * Get the grid model inside this game representing the game state of the board * @return game grid model @@ -333,4 +404,12 @@ public class Game { nextPieceListener = listener; } + /** + * Set line cleared listener + * @param listener listener to set + */ + public void setLineClearedListener(LineClearedListener listener) { + lineClearedListener = listener; + } + } diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/scene/ChallengeScene.java b/src/main/java/uk/mgrove/ac/soton/comp1206/scene/ChallengeScene.java index 544cb43..093f055 100644 --- a/src/main/java/uk/mgrove/ac/soton/comp1206/scene/ChallengeScene.java +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/scene/ChallengeScene.java @@ -1,13 +1,12 @@ package uk.mgrove.ac.soton.comp1206.scene; -import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.mgrove.ac.soton.comp1206.component.GameBlock; import uk.mgrove.ac.soton.comp1206.component.GameBoard; import uk.mgrove.ac.soton.comp1206.component.PieceBoard; -import uk.mgrove.ac.soton.comp1206.event.NextPieceListener; import uk.mgrove.ac.soton.comp1206.game.Game; import uk.mgrove.ac.soton.comp1206.game.GamePiece; import uk.mgrove.ac.soton.comp1206.ui.GamePane; @@ -28,6 +27,16 @@ public class ChallengeScene extends BaseScene { */ private PieceBoard currentPieceBoard; + /** + * PieceBoard to display next piece + */ + private PieceBoard followingPieceBoard; + + /** + * GameBoard for main game + */ + private GameBoard board; + /** * Create a new Single Player challenge scene * @param gameWindow the Game Window @@ -57,15 +66,19 @@ public class ChallengeScene extends BaseScene { var mainPane = new BorderPane(); challengePane.getChildren().add(mainPane); - var board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2); + board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2); + board.setOnRightClicked(game::rotateCurrentPiece); mainPane.setCenter(board); var statsMenu = new StatsMenu(game.scoreProperty(),game.levelProperty(),game.livesProperty(),game.multiplierProperty()); currentPieceBoard = new PieceBoard(100, 100); + currentPieceBoard.showMiddleIndicator(true); + currentPieceBoard.setOnLeftClicked(game::rotateCurrentPiece); + followingPieceBoard = new PieceBoard(72, 72); var rightMenu = new VBox(); - rightMenu.getChildren().addAll(statsMenu,currentPieceBoard); + rightMenu.getChildren().addAll(statsMenu,currentPieceBoard,followingPieceBoard); mainPane.setRight(rightMenu); @@ -83,6 +96,24 @@ public class ChallengeScene extends BaseScene { game.blockClicked(gameBlock); } + /** + * Handle keypress for game keyboard controls + * @param event the keypress + */ + private void handleKeyboardControls(KeyEvent event) { + switch (event.getCode()) { + case ESCAPE -> returnToMenu(); + case ENTER, X -> game.dropPiece(board.getXFocus(),board.getYFocus()); + case SPACE, R -> game.swapPieces(); + case OPEN_BRACKET, Q, Z -> game.rotateCurrentPieceAnticlockwise(); + case CLOSE_BRACKET, E, C -> game.rotateCurrentPiece(); + case LEFT, A -> board.changeXFocus(-1); + case RIGHT, D -> board.changeXFocus(1); + case DOWN, S -> board.changeYFocus(1); + case UP, W -> board.changeYFocus(-1); + } + } + /** * Set up the game object and model */ @@ -100,15 +131,12 @@ public class ChallengeScene extends BaseScene { public void initialise() { logger.info("Initialising Challenge"); - // exit challenge when escape key pressed - scene.setOnKeyPressed((event) -> { - if (event.getCode() == KeyCode.ESCAPE) { - returnToMenu(); - } - }); + scene.setOnKeyPressed(this::handleKeyboardControls); game.setNextPieceListener(this::setCurrentPiece); + game.setLineClearedListener(board::fadeOut); + game.start(); } @@ -119,8 +147,9 @@ public class ChallengeScene extends BaseScene { gameWindow.startMenu(); } - private void setCurrentPiece(GamePiece piece) { - currentPieceBoard.displayPiece(piece); + private void setCurrentPiece(GamePiece currentPiece, GamePiece followingPiece) { + currentPieceBoard.displayPiece(currentPiece); + followingPieceBoard.displayPiece(followingPiece); } } diff --git a/src/main/java/uk/mgrove/ac/soton/comp1206/scene/InstructionsScene.java b/src/main/java/uk/mgrove/ac/soton/comp1206/scene/InstructionsScene.java index 0e07214..9e59a17 100644 --- a/src/main/java/uk/mgrove/ac/soton/comp1206/scene/InstructionsScene.java +++ b/src/main/java/uk/mgrove/ac/soton/comp1206/scene/InstructionsScene.java @@ -39,7 +39,7 @@ public class InstructionsScene extends BaseScene { public void initialise() { logger.info("Initialising Instructions"); - // exit challenge when escape key pressed + // exit instructions when escape key pressed scene.setOnKeyPressed((event) -> { if (event.getCode() == KeyCode.ESCAPE) { gameWindow.startMenu(); @@ -52,6 +52,8 @@ public class InstructionsScene extends BaseScene { */ @Override public void build() { + // TODO: styling and insets + logger.info("Building " + this.getClass().getName()); root = new GamePane(gameWindow.getWidth(),gameWindow.getHeight());