[FEAT] Piece previews on hover
This commit is contained in:
@@ -64,6 +64,11 @@ public class GameBlock extends Canvas {
|
|||||||
*/
|
*/
|
||||||
private final IntegerProperty value = new SimpleIntegerProperty(0);
|
private final IntegerProperty value = new SimpleIntegerProperty(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preview value of this block (0 = empty, otherwise specifies the colour to render as)
|
||||||
|
*/
|
||||||
|
private final IntegerProperty previewValue = new SimpleIntegerProperty(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the block should appear as focussed
|
* Whether the block should appear as focussed
|
||||||
*/
|
*/
|
||||||
@@ -98,6 +103,7 @@ public class GameBlock extends Canvas {
|
|||||||
|
|
||||||
//When the value property is updated, call the internal updateValue method
|
//When the value property is updated, call the internal updateValue method
|
||||||
value.addListener(this::updateValue);
|
value.addListener(this::updateValue);
|
||||||
|
previewValue.addListener(this::updateValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,6 +127,9 @@ public class GameBlock extends Canvas {
|
|||||||
//If the block is not empty, paint with the colour represented by the value
|
//If the block is not empty, paint with the colour represented by the value
|
||||||
paintColor(COLOURS[value.get()]);
|
paintColor(COLOURS[value.get()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previewValue.get() != 0) paintPreview(COLOURS[previewValue.get()]);
|
||||||
|
|
||||||
// paint focus overlay if required
|
// paint focus overlay if required
|
||||||
if (isFocussed) paintFocusOverlay();
|
if (isFocussed) paintFocusOverlay();
|
||||||
|
|
||||||
@@ -128,6 +137,29 @@ public class GameBlock extends Canvas {
|
|||||||
if (showMiddleIndicator) paintMiddleIndicator();
|
if (showMiddleIndicator) paintMiddleIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle painting of the block canvas with block preview
|
||||||
|
*/
|
||||||
|
public void paintPreview(Color color) {
|
||||||
|
logger.info("Painting preview overlay on block at x: {}, y: {} with color: {}", getX(), getY(), color);
|
||||||
|
|
||||||
|
var gc = getGraphicsContext2D();
|
||||||
|
|
||||||
|
gc.setFill(color.deriveColor(0, 1, 1, 0.25));
|
||||||
|
gc.fillRect(0,0, width, height);
|
||||||
|
gc.setFill(color.deriveColor(0,1,0.8,0.25));
|
||||||
|
gc.fillPolygon(new double[]{
|
||||||
|
width,
|
||||||
|
width,
|
||||||
|
0
|
||||||
|
}, new double[]{
|
||||||
|
0,
|
||||||
|
height,
|
||||||
|
0
|
||||||
|
}, 3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paint this canvas empty
|
* Paint this canvas empty
|
||||||
*/
|
*/
|
||||||
@@ -283,6 +315,14 @@ public class GameBlock extends Canvas {
|
|||||||
value.bind(input);
|
value.bind(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind the value of this block to another property. Used to link the visual block to a corresponding block in the Grid.
|
||||||
|
* @param input property to bind the value to
|
||||||
|
*/
|
||||||
|
public void bindPreview(ObservableValue<? extends Number> input) {
|
||||||
|
previewValue.bind(input);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GameBlock{" +
|
return "GameBlock{" +
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import uk.mgrove.ac.soton.comp1206.event.BlockClickedListener;
|
import uk.mgrove.ac.soton.comp1206.event.BlockClickedListener;
|
||||||
import uk.mgrove.ac.soton.comp1206.event.MouseClickListener;
|
import uk.mgrove.ac.soton.comp1206.event.MouseClickListener;
|
||||||
|
import uk.mgrove.ac.soton.comp1206.game.GamePiece;
|
||||||
import uk.mgrove.ac.soton.comp1206.game.Grid;
|
import uk.mgrove.ac.soton.comp1206.game.Grid;
|
||||||
import uk.mgrove.ac.soton.comp1206.util.Multimedia;
|
import uk.mgrove.ac.soton.comp1206.util.Multimedia;
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ import java.util.Set;
|
|||||||
* A GameBoard is a visual component to represent the visual GameBoard.
|
* A GameBoard is a visual component to represent the visual GameBoard.
|
||||||
* It extends a GridPane to hold a grid of GameBlocks.
|
* It extends a GridPane to hold a grid of GameBlocks.
|
||||||
*
|
*
|
||||||
* The GameBoard can hold an internal grid of it's own, for example, for displaying an upcoming block. It also be
|
* The GameBoard can hold an internal grid of its own, for example, for displaying an upcoming block. It also be
|
||||||
* linked to an external grid, for the main game board.
|
* linked to an external grid, for the main game board.
|
||||||
*
|
*
|
||||||
* The GameBoard is only a visual representation and should not contain game logic or model logic in it, which should
|
* The GameBoard is only a visual representation and should not contain game logic or model logic in it, which should
|
||||||
@@ -81,6 +82,11 @@ public class GameBoard extends GridPane {
|
|||||||
*/
|
*/
|
||||||
protected boolean enableFocus = true;
|
protected boolean enableFocus = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current game piece
|
||||||
|
*/
|
||||||
|
protected GamePiece currentPiece;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new GameBoard, based off a given grid, with a visual width and height.
|
* Create a new GameBoard, based off a given grid, with a visual width and height.
|
||||||
* @param grid linked grid
|
* @param grid linked grid
|
||||||
@@ -151,6 +157,8 @@ public class GameBoard extends GridPane {
|
|||||||
logger.debug("Mouse button clicked: {}", event.getButton());
|
logger.debug("Mouse button clicked: {}", event.getButton());
|
||||||
if (event.getButton().equals(MouseButton.SECONDARY) && rightClickListener != null) {
|
if (event.getButton().equals(MouseButton.SECONDARY) && rightClickListener != null) {
|
||||||
rightClickListener.action();
|
rightClickListener.action();
|
||||||
|
grid.clearPreview();
|
||||||
|
grid.previewPiece(currentPiece, getXFocus(), getYFocus());
|
||||||
} else if (event.getButton().equals(MouseButton.PRIMARY) && leftClickListener != null) {
|
} else if (event.getButton().equals(MouseButton.PRIMARY) && leftClickListener != null) {
|
||||||
leftClickListener.action();
|
leftClickListener.action();
|
||||||
}
|
}
|
||||||
@@ -178,6 +186,7 @@ public class GameBoard extends GridPane {
|
|||||||
|
|
||||||
//Link the GameBlock component to the corresponding value in the Grid
|
//Link the GameBlock component to the corresponding value in the Grid
|
||||||
block.bind(grid.getGridProperty(x,y));
|
block.bind(grid.getGridProperty(x,y));
|
||||||
|
block.bindPreview(grid.getPreviewGridProperty(x,y));
|
||||||
|
|
||||||
//Add a mouse click handler to the block to trigger GameBoard blockClicked method
|
//Add a mouse click handler to the block to trigger GameBoard blockClicked method
|
||||||
block.setOnMouseClicked((e) -> {
|
block.setOnMouseClicked((e) -> {
|
||||||
@@ -212,11 +221,13 @@ public class GameBoard extends GridPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void blockHovered(GameBlock gameBlock) {
|
public void blockHovered(GameBlock gameBlock) {
|
||||||
|
grid.clearPreview();
|
||||||
if (enableFocus) {
|
if (enableFocus) {
|
||||||
logger.info("Block hovered: {}", gameBlock);
|
logger.info("Block hovered: {}", gameBlock);
|
||||||
if (focussedBlock != null) focussedBlock.setFocussed(false);
|
if (focussedBlock != null) focussedBlock.setFocussed(false);
|
||||||
focussedBlock = gameBlock;
|
focussedBlock = gameBlock;
|
||||||
focussedBlock.setFocussed(true);
|
focussedBlock.setFocussed(true);
|
||||||
|
grid.previewPiece(currentPiece, getXFocus(), getYFocus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,12 +281,28 @@ public class GameBoard extends GridPane {
|
|||||||
else return 0;
|
else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set listener for when secondary mouse button clicked
|
||||||
|
* @param listener listener to set
|
||||||
|
*/
|
||||||
public void setOnRightClicked(MouseClickListener listener) {
|
public void setOnRightClicked(MouseClickListener listener) {
|
||||||
rightClickListener = listener;
|
rightClickListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set listener for when primary mouse button clicked
|
||||||
|
* @param listener listener to set
|
||||||
|
*/
|
||||||
public void setOnLeftClicked(MouseClickListener listener) {
|
public void setOnLeftClicked(MouseClickListener listener) {
|
||||||
leftClickListener = listener;
|
leftClickListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update current game piece
|
||||||
|
* @param newPiece current piece
|
||||||
|
*/
|
||||||
|
public void setCurrentPiece(GamePiece newPiece) {
|
||||||
|
currentPiece = newPiece;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ public class Grid {
|
|||||||
*/
|
*/
|
||||||
private final SimpleIntegerProperty[][] grid;
|
private final SimpleIntegerProperty[][] grid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preview grid is a 2D arrow with rows and columns of SimpleIntegerProperties, storing which blocks are currently showing previews.
|
||||||
|
*/
|
||||||
|
private final SimpleIntegerProperty[][] previewGrid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Grid with the specified number of columns and rows and initialise them
|
* Create a new Grid with the specified number of columns and rows and initialise them
|
||||||
* @param cols number of columns
|
* @param cols number of columns
|
||||||
@@ -56,6 +61,16 @@ public class Grid {
|
|||||||
grid[x][y] = new SimpleIntegerProperty(0);
|
grid[x][y] = new SimpleIntegerProperty(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create the grid itself
|
||||||
|
previewGrid = new SimpleIntegerProperty[cols][rows];
|
||||||
|
|
||||||
|
//Add a SimpleIntegerProperty to every block in the grid
|
||||||
|
for(var y = 0; y < rows; y++) {
|
||||||
|
for(var x = 0; x < cols; x++) {
|
||||||
|
previewGrid[x][y] = new SimpleIntegerProperty(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,6 +83,16 @@ public class Grid {
|
|||||||
return grid[x][y];
|
return grid[x][y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Integer property contained inside the preview grid at a given row and column index. Can be used for binding.
|
||||||
|
* @param x column
|
||||||
|
* @param y row
|
||||||
|
* @return the IntegerProperty at the given x and y in this grid
|
||||||
|
*/
|
||||||
|
public IntegerProperty getPreviewGridProperty(int x, int y) {
|
||||||
|
return previewGrid[x][y];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the value at the given x and y index within the grid
|
* Update the value at the given x and y index within the grid
|
||||||
* @param x column
|
* @param x column
|
||||||
@@ -175,4 +200,37 @@ public class Grid {
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preview piece on board
|
||||||
|
* @param piece piece to preview
|
||||||
|
* @param x x-coordinate of piece centre
|
||||||
|
* @param y y-coordinate of piece centre
|
||||||
|
*/
|
||||||
|
public void previewPiece(GamePiece piece, int x, int y) {
|
||||||
|
if (!canPlayPiece(piece, x, y)) return;
|
||||||
|
|
||||||
|
logger.info("Previewing piece {} at {},{}", piece, x, y);
|
||||||
|
int value = piece.getValue();
|
||||||
|
int[][] blocks = piece.getBlocks();
|
||||||
|
|
||||||
|
for (var blockX = 0; blockX < blocks.length; blockX++) {
|
||||||
|
for (var blockY = 0; blockY < blocks.length; blockY++) {
|
||||||
|
if (blocks[blockX][blockY] > 0) {
|
||||||
|
previewGrid[x + blockX - 1][y + blockY - 1].set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear previewed blocks from the grid
|
||||||
|
*/
|
||||||
|
public void clearPreview() {
|
||||||
|
for (var resetX = 0; resetX < previewGrid.length; resetX++) {
|
||||||
|
for (var resetY = 0; resetY < previewGrid[0].length; resetY++) {
|
||||||
|
previewGrid[resetX][resetY].set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ public class ChallengeScene extends BaseScene {
|
|||||||
protected void setCurrentPiece(GamePiece currentPiece, GamePiece followingPiece) {
|
protected void setCurrentPiece(GamePiece currentPiece, GamePiece followingPiece) {
|
||||||
currentPieceBoard.displayPiece(currentPiece);
|
currentPieceBoard.displayPiece(currentPiece);
|
||||||
followingPieceBoard.displayPiece(followingPiece);
|
followingPieceBoard.displayPiece(followingPiece);
|
||||||
|
board.setCurrentPiece(currentPiece);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class Multimedia {
|
|||||||
if (audioPlayer != null) audioPlayer.stop();
|
if (audioPlayer != null) audioPlayer.stop();
|
||||||
var media = new Media(Multimedia.class.getResource("/" + filePath).toExternalForm());
|
var media = new Media(Multimedia.class.getResource("/" + filePath).toExternalForm());
|
||||||
audioPlayer = new MediaPlayer(media);
|
audioPlayer = new MediaPlayer(media);
|
||||||
// audioPlayer.play();
|
audioPlayer.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +47,7 @@ public class Multimedia {
|
|||||||
if (musicPlayer != null) musicPlayer.stop();
|
if (musicPlayer != null) musicPlayer.stop();
|
||||||
var media = new Media(Multimedia.class.getResource("/" + filePath).toExternalForm());
|
var media = new Media(Multimedia.class.getResource("/" + filePath).toExternalForm());
|
||||||
musicPlayer = new MediaPlayer(media);
|
musicPlayer = new MediaPlayer(media);
|
||||||
// musicPlayer.setAutoPlay(true);
|
musicPlayer.setAutoPlay(true);
|
||||||
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
|
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
-fx-fill: white;
|
-fx-fill: white;
|
||||||
|
-fx-font-family: 'Orbitron';
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|||||||
Reference in New Issue
Block a user