diff options
Diffstat (limited to 'pacman/view/src/main')
11 files changed, 302 insertions, 59 deletions
diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/AnimatedSprite.java b/pacman/view/src/main/java/com/gr15/pacman/view/AnimatedSprite.java index 0e82a56..a5e5dd8 100644 --- a/pacman/view/src/main/java/com/gr15/pacman/view/AnimatedSprite.java +++ b/pacman/view/src/main/java/com/gr15/pacman/view/AnimatedSprite.java @@ -40,7 +40,6 @@ public class AnimatedSprite extends Sprite { * @param y the Y-coordinate of the sprite's top-left corner * @param width the width of the sprite * @param height the height of the sprite - * * @throws IllegalArgumentException if {@code frames} is null or empty */ public AnimatedSprite(Image[] frames, double x, double y, @@ -83,4 +82,3 @@ public class AnimatedSprite extends Sprite { } } } - diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/ResourceManager.java b/pacman/view/src/main/java/com/gr15/pacman/view/ResourceManager.java index 3e7103e..d370178 100644 --- a/pacman/view/src/main/java/com/gr15/pacman/view/ResourceManager.java +++ b/pacman/view/src/main/java/com/gr15/pacman/view/ResourceManager.java @@ -57,10 +57,13 @@ public class ResourceManager { * the default "missing texture" {@link Image} is returned. * * @param path the path to the image resource, relative to the classpath - * @return the loaded {@link Image}, - * or a default image if the resource could not be found + * @return the loaded {@link Image}, or a default image if the resource could not be found + * @throws IllegalArgumentException if path is {@code null} */ public Image getTexture(String path) { + if (path == null) { + throw new IllegalArgumentException("path must not be null"); + } if (resources.containsKey(path)) { return resources.get(path); } else { diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/Sprite.java b/pacman/view/src/main/java/com/gr15/pacman/view/Sprite.java index 74be172..258a8f3 100644 --- a/pacman/view/src/main/java/com/gr15/pacman/view/Sprite.java +++ b/pacman/view/src/main/java/com/gr15/pacman/view/Sprite.java @@ -40,8 +40,23 @@ public class Sprite { * @param y the Y-coordinate of the sprite's top-left corner * @param width the width of the sprite * @param height the height of the sprite + * @throws IllegalArgumentException if image is {@code null} or if + * - x is less than 0 + * - y is less than 0 + * - width is less than 0 + * - height is less than 0 */ public Sprite(Image image, double x, double y, double width, double height) { + if (image == null) { + throw new IllegalArgumentException("image must not be be null"); + } + if (x < 0 || y < 0) { + throw new IllegalArgumentException("x and y must be a positive number"); + } + if (width < 0 || height < 0) { + throw new IllegalArgumentException("width and height must be a positive number"); + } + this.image = image; this.x = x; this.y = y; @@ -59,8 +74,12 @@ public class Sprite { * The transformation for rotation is applied around the sprite's center.</p> * * @param gc the {@link GraphicsContext} to render the sprite to + * @trows IllegalArgumentException if gc is {@code null} */ public void render(GraphicsContext gc) { + if (gc == null) { + throw new IllegalArgumentException("gc must not be null"); + } /* Saving the current transformation */ gc.save(); @@ -141,8 +160,14 @@ public class Sprite { * Sets a new image for the sprite. * * @param newImage the new image to set + * @throws IllegalArgumentException if newImage is {@code null} */ - public void setImage(Image newImage) { this.image = newImage; } + public void setImage(Image newImage) { + if (newImage == null) { + throw new IllegalArgumentException("newImage must not be null"); + } + this.image = newImage; + } /** * Sets a new image for the sprite. diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/ViewManager.java b/pacman/view/src/main/java/com/gr15/pacman/view/ViewManager.java index b648786..b77d37d 100644 --- a/pacman/view/src/main/java/com/gr15/pacman/view/ViewManager.java +++ b/pacman/view/src/main/java/com/gr15/pacman/view/ViewManager.java @@ -28,7 +28,13 @@ public class ViewManager { /** * Enumeration of all possible view keys used to identify different views. */ - public enum ViewKeys { MAIN_MENU, GAME_VIEW, PAUSE }; + public enum ViewKeys { + MAIN_MENU_VIEW, + GAME_VIEW, + PAUSE_VIEW, + GAME_OVER_VIEW, + YOU_WON_VIEW + }; /** * A map that stores views associated with their corresponding keys. @@ -51,15 +57,23 @@ public class ViewManager { * * @param key the key to identify the view. * @param view the view to be added. - * @throws IllegalArgumentException if a view with the same key already exists. + * @throws IllegalArgumentException if a view with the same key already exists or + * - key is {@code null} + * - view is {@code null} */ public void addView(ViewKeys key, BaseView view) { + if (key == null) { + throw new IllegalArgumentException("key must not be null"); + } + if (view == null) { + throw new IllegalArgumentException("view must not be null"); + } if (views.containsKey(key)) { throw new IllegalArgumentException( "View with key " + key + " already exists."); - } else { - views.put(key, view); } + + views.put(key, view); } /** @@ -67,9 +81,13 @@ public class ViewManager { * Calls {@code onExit} on the current view (if any) and {@code onEnter} on the new view. * * @param key the key of the view to be shown. - * @throws IllegalArgumentException if no view exists for the specified key. + * @throws IllegalArgumentException if no view exists for the specified key, + * or key is {@code null} */ public void showView(ViewKeys key) { + if (key == null) { + throw new IllegalArgumentException("key must not be null"); + } if (!views.containsKey(key)) { throw new IllegalArgumentException( "No view with key " + key + " exists."); @@ -90,8 +108,12 @@ public class ViewManager { * * @param key the key to check. * @return {@code true} if a view with the key exists, {@code false} otherwise. + * @throws IllegalArgumentException if key is {@code null} */ public boolean hasView(ViewKeys key) { + if (key == null) { + throw new IllegalArgumentException("key must not be null"); + } return views.containsKey(key); } @@ -99,8 +121,12 @@ public class ViewManager { * Removes the view associated with the specified key. * * @param key the key of the view to remove. + * @throws IllegalArgumentException if key is {@code null} */ public void removeView(ViewKeys key) { + if (key == null) { + throw new IllegalArgumentException("key must not be null"); + } views.remove(key); } diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/screen/GameOverView.java b/pacman/view/src/main/java/com/gr15/pacman/view/screen/GameOverView.java new file mode 100644 index 0000000..2991bef --- /dev/null +++ b/pacman/view/src/main/java/com/gr15/pacman/view/screen/GameOverView.java @@ -0,0 +1,48 @@ +package com.gr15.pacman.view.screen; + +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; + +/** + * Simple Game Over screen with message and button. + */ +public class GameOverView + extends BaseView { + + private final VBox root = new VBox(); + + Label gameOverLabel = new Label("Game Over!"); + private final Button mainMenuButton = new Button("Return to Main Menu"); + private final Label scoreLabel; + + public GameOverView(int score) { + scoreLabel = new Label("Score: " + score); + + gameOverLabel.setTextFill(Color.RED); + gameOverLabel.setFont(Font.font("Arial", FontWeight.BOLD, 40)); + + scoreLabel.setTextFill(Color.WHITE); + scoreLabel.setFont(Font.font("Arial", FontWeight.NORMAL, 24)); + + mainMenuButton.setFont(Font.font("Arial", 18)); + + root.getChildren().addAll(gameOverLabel, scoreLabel, mainMenuButton); + this.getChildren().add(root); + } + + @Override + public void onEnter() { + /* No specific behavior */ + } + + @Override + public void onExit() { + /* No specific behavior */ + } + + public Button getMainMenuButton() { return mainMenuButton; } +} diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/screen/GameView.java b/pacman/view/src/main/java/com/gr15/pacman/view/screen/GameView.java index 1c333d7..d79dc2c 100644 --- a/pacman/view/src/main/java/com/gr15/pacman/view/screen/GameView.java +++ b/pacman/view/src/main/java/com/gr15/pacman/view/screen/GameView.java @@ -9,11 +9,15 @@ import com.gr15.pacman.view.AnimatedSprite; import com.gr15.pacman.view.ResourceManager; import com.gr15.pacman.view.Sprite; -import javafx.geometry.Pos; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; +import javafx.scene.control.Label; import javafx.scene.image.Image; -import javafx.scene.layout.VBox; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; import javafx.scene.transform.Affine; /** @@ -28,22 +32,25 @@ public class GameView extends BaseView { /** The root layout container for this view. */ - private VBox root = new VBox(); + private BorderPane root = new BorderPane(); - /** Canvas used to draw the game graphics. */ + /** {@link Canvas} used to draw the game graphics. */ private Canvas canvas; + /** {@link HBox} used as layout container for HUD */ + private HBox hudPanel = new HBox(20); + /************************************************************* * CONSTANTS * *************************************************************/ - /** Logical width of the canvas for consistent rendering. */ - private static final double LOGICAL_WIDTH = 1920; + /** Target width of the {@link Canvas} for consistent rendering. */ + private static final double VIRTUAL_WIDTH = 1000; - /** Logical width of the canvas for consistent rendering. */ - private static final double LOGICAL_HEIGHT = 1080; + /** Target width of the {@link Canvas} for consistent rendering. */ + private static final double VIRTUAL_HEIGHT = 800; - /** Reference to the current game state for rendering entities and board. */ + /** Size of each tile in pixels. */ private static final int TILE_SIZE = 16; /************************************************************* @@ -53,7 +60,7 @@ public class GameView /** Reference to the {@link ResourceManager} singleton instance. */ private ResourceManager resourceManager = ResourceManager.getInstance(); - /** Reference to the current game state for rendering entities and board. */ + /** Reference to the current {@link GameState} for rendering entities and board. */ private final GameState gameState; /** Graphics context used for rendering on the {@link Canvas}. */ @@ -65,17 +72,54 @@ public class GameView /** Current zoom factor. */ private double currentZoom = 1; + /** Boolean for keeping track of power mode. Needed for changing sprites */ + private boolean powerMode = false; + /************************************************************* * SPRITES * *************************************************************/ - /** Pacman sprite for rendering the player's character. */ + /** {@link Sprite} for rendering {@link Pacman}. */ private AnimatedSprite pacman; - private Sprite redGhost; - private Sprite blueGhost; - private Sprite pinkGhost; - private Sprite orangeGhost; + /** {@link Sprite} for rendering red {@link Ghost}. */ + private Sprite redGhost = new Sprite(resourceManager + .getTexture("/gameAssets/redGhost.png"), + 0, 0, TILE_SIZE, TILE_SIZE); + + /** {@link Sprite} for rendering blue {@link Ghost}. */ + private Sprite blueGhost = new Sprite(resourceManager + .getTexture("/gameAssets/blueGhost.png"), + 0, 0, TILE_SIZE, TILE_SIZE); + + /** {@link Sprite} for rendering pink {@link Ghost}. */ + private Sprite pinkGhost = new Sprite(resourceManager + .getTexture("/gameAssets/pinkGhost.png"), + 0, 0, TILE_SIZE, TILE_SIZE); + + /** {@link Sprite} for rendering orange {@link Ghost}. */ + private Sprite orangeGhost = new Sprite(resourceManager + .getTexture("/gameAssets/orangeGhost.png"), + 0, 0, TILE_SIZE, TILE_SIZE); + + /************************************************************* + * UI ELEMENTS * + *************************************************************/ + + /** {@link Label} for displaying game score. */ + private Label scoreLabel = new Label("Score: 0"); + + /** {@link Label} for displaying game lives. */ + Label livesLabel = new Label("Lives: "); + + /** {@link Label} for displaying game time. */ + Label timeLabel = new Label("Time: "); + /* To keep track of time */ + private double time = 0; + + /************************************************************* + * CONSTRUCTOR * + *************************************************************/ /** * Constructs a new {@code GameView} with the given game state. @@ -86,15 +130,26 @@ public class GameView public GameView(GameState gameState) { this.gameState = gameState; - canvas = new Canvas(LOGICAL_WIDTH, LOGICAL_HEIGHT); + canvas = new Canvas(VIRTUAL_WIDTH, VIRTUAL_HEIGHT); gc = canvas.getGraphicsContext2D(); - root.setAlignment(Pos.TOP_CENTER); - root.getChildren().add(canvas); - getChildren().add(root); + scoreLabel.setTextFill(Color.WHITE); + scoreLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); - /* Setting up animated sprites */ + livesLabel.setTextFill(Color.WHITE); + livesLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + timeLabel.setTextFill(Color.WHITE); + timeLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + + hudPanel.setStyle("-fx-background-color: red; -fx-padding: 10;"); + hudPanel.getChildren().addAll(scoreLabel, livesLabel, timeLabel); + + root.setCenter(canvas); + root.setTop(hudPanel); + this.getChildren().add(root); + + /* Setting up animated sprites */ Image[] pacmanFrames = { resourceManager.getTexture("/gameAssets/pacman1.png"), resourceManager.getTexture("/gameAssets/pacman2.png"), @@ -104,7 +159,7 @@ public class GameView } /************************************************************* - * METHODS * + * RENDER LOOP * *************************************************************/ /** @@ -119,9 +174,12 @@ public class GameView updateCamara(); renderBoard(); - for (Ghost ghost : gameState.getGhosts()) { - /* Update each ghost sprite */ - } + /* Updating hud */ + scoreLabel.setText("Score: " + gameState.getScore()); + livesLabel.setText("Lives: " + gameState.getLives()); + time += deltaSeconds; + int integerTime = (int)Math.round(time); + timeLabel.setText("Time: " + integerTime); /* Rotating pacman sprite */ Pacman pacmanEntity = gameState.getPacman(); @@ -132,10 +190,48 @@ public class GameView case RIGHT -> pacman.setRotation(90); default -> {} } + + /* Updating animated sprite */ pacman.update(deltaSeconds); renderEntity(pacmanEntity, pacman); + + for (Ghost ghost : gameState.getGhosts()) { + switch (ghost.getGhostType()) { + case RED -> renderEntity(ghost, redGhost); + case BLUE -> renderEntity(ghost, blueGhost); + case PINK -> renderEntity(ghost, pinkGhost); + case ORANGE -> renderEntity(ghost, orangeGhost); + } + } + + /* Updating sprite texture if in power mode */ + if (gameState.getPowerModeDuration() > 0 && !powerMode) { + powerMode = true; + redGhost.setImage(resourceManager + .getTexture("/gameAssets/scaredGhost.png")); + blueGhost.setImage(resourceManager + .getTexture("/gameAssets/scaredGhost.png")); + pinkGhost.setImage(resourceManager + .getTexture("/gameAssets/scaredGhost.png")); + orangeGhost.setImage(resourceManager + .getTexture("/gameAssets/scaredGhost.png")); + } else if (gameState.getPowerModeDuration() == 0 && powerMode) { + powerMode = false; + redGhost.setImage(resourceManager + .getTexture("/gameAssets/redGhost.png")); + blueGhost.setImage(resourceManager + .getTexture("/gameAssets/blueGhost.png")); + pinkGhost.setImage(resourceManager + .getTexture("/gameAssets/pinkGhost.png")); + orangeGhost.setImage(resourceManager + .getTexture("/gameAssets/orangeGhost.png")); + } } + /************************************************************* + * HELPER FUNCTIONS * + *************************************************************/ + /** * Updates the camera transform based on Pacman's position, * centering the camera and applying zoom. @@ -144,13 +240,14 @@ public class GameView camara.setToIdentity(); double screenWidth = canvas.getWidth(); double screenHeight = canvas.getHeight(); - double scaleX = (screenWidth / LOGICAL_WIDTH) * currentZoom; - double scaleY = (screenHeight / LOGICAL_HEIGHT) * currentZoom; + double scaleX = (screenWidth / VIRTUAL_WIDTH) * currentZoom; + double scaleY = (screenHeight / VIRTUAL_HEIGHT) * currentZoom; double scale = Math.min(scaleX, scaleY); - /* Define center of camara */ - double centerX = gameState.getPacman().getPositionX() * TILE_SIZE; - double centerY = gameState.getPacman().getPositionY() * TILE_SIZE; + /* Define center of camara. + * Could be changed to center on anything. */ + double centerX = gameState.getPacman().getX() * TILE_SIZE; + double centerY = gameState.getPacman().getY() * TILE_SIZE; camara.appendTranslation(screenWidth / 2, screenHeight / 2); camara.appendScale(scale, scale); @@ -178,6 +275,16 @@ public class GameView worldX, worldY, TILE_SIZE, TILE_SIZE ); } + case PELLET -> { + gc.drawImage(ResourceManager.getInstance() + .getTexture("/gameAssets/food.png"), + worldX, worldY, TILE_SIZE, TILE_SIZE); + } + case POWER_PELLET -> { + gc.drawImage(ResourceManager.getInstance() + .getTexture("/gameAssets/powerFood.png"), + worldX, worldY, TILE_SIZE, TILE_SIZE); + } case EMPTY -> {} } } @@ -192,8 +299,8 @@ public class GameView * @param sprite the sprite to use for rendering the entity */ private void renderEntity(Entity entity, Sprite sprite) { - double spriteX = entity.getPositionX() * TILE_SIZE; - double spriteY = entity.getPositionY() * TILE_SIZE; + double spriteX = entity.getX() * TILE_SIZE; + double spriteY = entity.getY() * TILE_SIZE; sprite.setX(spriteX - sprite.getWidth() / 2); sprite.setY(spriteY - sprite.getHeight() / 2); @@ -201,13 +308,17 @@ public class GameView sprite.render(gc); } + /************************************************************* + * METHODS * + *************************************************************/ + /** * Adjusts the current zoom level of the camera. * * @param deltaZoom the amount to change the zoom level by */ public void changeZoom(double deltaZoom) { - currentZoom += deltaZoom; + currentZoom = Math.max(currentZoom + deltaZoom, 1); } /** @@ -219,7 +330,7 @@ public class GameView } /** - * Called when this view is deactivated or switched away from. + * Called when this view is switched away from. */ @Override public void onExit() { @@ -231,7 +342,7 @@ public class GameView * * @return the root VBox */ - public VBox getRoot() { + public BorderPane getRoot() { return root; } } diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/screen/MainMenuView.java b/pacman/view/src/main/java/com/gr15/pacman/view/screen/MainMenuView.java index 912ec7b..73286aa 100644 --- a/pacman/view/src/main/java/com/gr15/pacman/view/screen/MainMenuView.java +++ b/pacman/view/src/main/java/com/gr15/pacman/view/screen/MainMenuView.java @@ -6,7 +6,7 @@ import javafx.scene.layout.VBox; /** * Represents the main menu view of the application, containing UI elements - * such as buttons for starting a new game, resuming a game, and exiting. + * such as buttons for starting a new game, and exiting. * * This view extends {@link BaseView} and lays out the buttons vertically * centered using a {@link VBox}. @@ -24,21 +24,18 @@ public class MainMenuView /** Button to start a new game. */ private Button newGameButton = new Button("New Game"); - /** Button resume an existing game. */ - private Button resumeButton = new Button("Resume"); - /** Button to exit the game. */ private Button exitButton = new Button("Exit Game"); /** * Constructs a new {@code MainMenuView} and initializes the layout with - * three main buttons: Resume, New Game, and Exit Game. The layout is + * three main buttons: New Game, and Exit Game. The layout is * vertically centered. */ public MainMenuView() { root.setAlignment(Pos.CENTER); - root.getChildren().addAll(resumeButton, newGameButton, exitButton); - getChildren().add(root); + root.getChildren().addAll(newGameButton, exitButton); + this.getChildren().add(root); } /************************************************************* @@ -75,13 +72,6 @@ public class MainMenuView public Button getNewGameButton() { return this.newGameButton; } /** - * Returns the button used to resume an existing game. - * - * @return the resume button - */ - public Button getResumeButton() { return this.resumeButton; } - - /** * Returns the button used to exit the game. * * @return the exit button diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/screen/PauseView.java b/pacman/view/src/main/java/com/gr15/pacman/view/screen/PauseView.java index fdf32b9..1832f25 100644 --- a/pacman/view/src/main/java/com/gr15/pacman/view/screen/PauseView.java +++ b/pacman/view/src/main/java/com/gr15/pacman/view/screen/PauseView.java @@ -38,7 +38,7 @@ public class PauseView public PauseView() { root.getChildren().addAll(resumeButton, mainMenuButton, quitButton); root.setAlignment(Pos.CENTER); - getChildren().add(root); + this.getChildren().add(root); } /** diff --git a/pacman/view/src/main/java/com/gr15/pacman/view/screen/YouWonView.java b/pacman/view/src/main/java/com/gr15/pacman/view/screen/YouWonView.java new file mode 100644 index 0000000..3b2f66a --- /dev/null +++ b/pacman/view/src/main/java/com/gr15/pacman/view/screen/YouWonView.java @@ -0,0 +1,42 @@ +package com.gr15.pacman.view.screen; + +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; + +public class YouWonView + extends BaseView { + + private VBox root = new VBox(20); + + private final Label winMessage = new Label("🎉 You Win! 🎉"); + private final Label finalScoreLabel; + private final Button mainMenuButton = new Button("Main Menu"); + + public YouWonView(int score) { + finalScoreLabel = new Label("Final Score: " + score); + + winMessage.setStyle("-fx-font-size: 36px; -fx-text-fill: white;"); + finalScoreLabel.setStyle("-fx-font-size: 20px; -fx-text-fill: white;"); + mainMenuButton.setPrefWidth(200); + + + + root.setAlignment(Pos.CENTER); + setStyle("-fx-background-color: black;"); + this.getChildren().addAll(winMessage, finalScoreLabel, mainMenuButton); + } + + @Override + public void onEnter() { + /* No specific behavior */ + } + + @Override + public void onExit() { + /* No specific behavior */ + } + + public Button getMainMenuButton() { return mainMenuButton; } +} diff --git a/pacman/view/src/main/resources/gameAssets/food.png b/pacman/view/src/main/resources/gameAssets/food.png Binary files differnew file mode 100644 index 0000000..ad1e1aa --- /dev/null +++ b/pacman/view/src/main/resources/gameAssets/food.png diff --git a/pacman/view/src/main/resources/gameAssets/powerFood.png b/pacman/view/src/main/resources/gameAssets/powerFood.png Binary files differnew file mode 100644 index 0000000..7e18c47 --- /dev/null +++ b/pacman/view/src/main/resources/gameAssets/powerFood.png |