[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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -98,6 +103,7 @@ public class GameBlock extends Canvas {
|
||||
|
||||
//When the value property is updated, call the internal updateValue method
|
||||
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
|
||||
paintColor(COLOURS[value.get()]);
|
||||
}
|
||||
|
||||
if (previewValue.get() != 0) paintPreview(COLOURS[previewValue.get()]);
|
||||
|
||||
// paint focus overlay if required
|
||||
if (isFocussed) paintFocusOverlay();
|
||||
|
||||
@@ -128,6 +137,29 @@ public class GameBlock extends Canvas {
|
||||
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
|
||||
*/
|
||||
@@ -283,6 +315,14 @@ public class GameBlock extends Canvas {
|
||||
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
|
||||
public String toString() {
|
||||
return "GameBlock{" +
|
||||
|
||||
@@ -7,6 +7,7 @@ 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.GamePiece;
|
||||
import uk.mgrove.ac.soton.comp1206.game.Grid;
|
||||
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.
|
||||
* 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.
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Current game piece
|
||||
*/
|
||||
protected GamePiece currentPiece;
|
||||
|
||||
/**
|
||||
* Create a new GameBoard, based off a given grid, with a visual width and height.
|
||||
* @param grid linked grid
|
||||
@@ -151,6 +157,8 @@ public class GameBoard extends GridPane {
|
||||
logger.debug("Mouse button clicked: {}", event.getButton());
|
||||
if (event.getButton().equals(MouseButton.SECONDARY) && rightClickListener != null) {
|
||||
rightClickListener.action();
|
||||
grid.clearPreview();
|
||||
grid.previewPiece(currentPiece, getXFocus(), getYFocus());
|
||||
} else if (event.getButton().equals(MouseButton.PRIMARY) && leftClickListener != null) {
|
||||
leftClickListener.action();
|
||||
}
|
||||
@@ -178,6 +186,7 @@ public class GameBoard extends GridPane {
|
||||
|
||||
//Link the GameBlock component to the corresponding value in the Grid
|
||||
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
|
||||
block.setOnMouseClicked((e) -> {
|
||||
@@ -212,11 +221,13 @@ public class GameBoard extends GridPane {
|
||||
}
|
||||
|
||||
public void blockHovered(GameBlock gameBlock) {
|
||||
grid.clearPreview();
|
||||
if (enableFocus) {
|
||||
logger.info("Block hovered: {}", gameBlock);
|
||||
if (focussedBlock != null) focussedBlock.setFocussed(false);
|
||||
focussedBlock = gameBlock;
|
||||
focussedBlock.setFocussed(true);
|
||||
grid.previewPiece(currentPiece, getXFocus(), getYFocus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,12 +281,28 @@ public class GameBoard extends GridPane {
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set listener for when secondary mouse button clicked
|
||||
* @param listener listener to set
|
||||
*/
|
||||
public void setOnRightClicked(MouseClickListener listener) {
|
||||
rightClickListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set listener for when primary mouse button clicked
|
||||
* @param listener listener to set
|
||||
*/
|
||||
public void setOnLeftClicked(MouseClickListener 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param cols number of columns
|
||||
@@ -56,6 +61,16 @@ public class Grid {
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param x column
|
||||
@@ -175,4 +200,37 @@ public class Grid {
|
||||
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) {
|
||||
currentPieceBoard.displayPiece(currentPiece);
|
||||
followingPieceBoard.displayPiece(followingPiece);
|
||||
board.setCurrentPiece(currentPiece);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,7 @@ public class Multimedia {
|
||||
if (audioPlayer != null) audioPlayer.stop();
|
||||
var media = new Media(Multimedia.class.getResource("/" + filePath).toExternalForm());
|
||||
audioPlayer = new MediaPlayer(media);
|
||||
// audioPlayer.play();
|
||||
audioPlayer.play();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ public class Multimedia {
|
||||
if (musicPlayer != null) musicPlayer.stop();
|
||||
var media = new Media(Multimedia.class.getResource("/" + filePath).toExternalForm());
|
||||
musicPlayer = new MediaPlayer(media);
|
||||
// musicPlayer.setAutoPlay(true);
|
||||
musicPlayer.setAutoPlay(true);
|
||||
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
Text {
|
||||
-fx-fill: white;
|
||||
-fx-font-family: 'Orbitron';
|
||||
}
|
||||
|
||||
Label {
|
||||
|
||||
Reference in New Issue
Block a user