feat: display score & game over, code cleanup

This commit is contained in:
Martin Prokoph
2025-06-28 21:30:12 +02:00
parent 7aa1b0e73c
commit a906bc5381
6 changed files with 149 additions and 77 deletions

View File

@@ -1,17 +1,20 @@
package eu.midnightdust.yaytris;
import eu.midnightdust.yaytris.game.Space;
import eu.midnightdust.yaytris.ui.ScoreMenu;
import eu.midnightdust.yaytris.ui.TetrisUI;
import javax.swing.*;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class Tetris {
public static final Random random = new Random();
public static Space space;
public static Timer timer;
public static TetrisUI ui;
private static Space space;
private static Timer timer;
private static TetrisUI ui;
private static TimerTask gravityTask;
public static void main(String[] args) {
try {
@@ -23,4 +26,35 @@ public class Tetris {
space = new Space();
ui = new TetrisUI();
}
public static TetrisUI getUi() {
return ui;
}
public static Space getSpace() {
return space;
}
public static void resetSpace() {
space = new Space();
}
public static void startGame() {
space.spawnTetromino();
gravityTask = new TimerTask() {
@Override
public void run() {
if (space.getCurrentTetromino() != null) {
space.getCurrentTetromino().fall(1);
ui.getGamePanel().repaint();
}
}
};
timer.scheduleAtFixedRate(gravityTask, 1, Settings.difficulty.getTimerPeriod());
}
public static void stopGame() {
gravityTask.cancel();
if (ui.getMenuPanel() instanceof ScoreMenu) ((ScoreMenu) Tetris.ui.getMenuPanel()).gameOver();
ui.transferFocus();
}
}

View File

@@ -1,5 +1,8 @@
package eu.midnightdust.yaytris.game;
import eu.midnightdust.yaytris.Tetris;
import eu.midnightdust.yaytris.ui.ScoreMenu;
import java.awt.Color;
import java.util.*;
@@ -78,6 +81,7 @@ public class Space {
}
}
this.score += combo;
if (Tetris.getUi().getMenuPanel() instanceof ScoreMenu) ((ScoreMenu)Tetris.getUi().getMenuPanel()).updateScore(this.score);
//System.out.println(score);
}

View File

@@ -20,34 +20,20 @@ public class Tetromino {
public void fall(int length) {
Vec2i newPos = centerPos.offset(Vec2i.of(0, length));
if (collidesVertically(newPos)) {
if (fallLength < 1) System.out.println("Game over!");
int[] affectedLines = new int[this.collision.length];
int line = centerPos.getY();
for (int i = 0; i < this.collision.length; i++) {
affectedLines[i] = line;
line++;
}
Tetris.space.onLinesChanged(this, affectedLines);
Tetris.space.spawnTetromino();
Tetris.getSpace().onLinesChanged(this, affectedLines);
if (fallLength >= 1) Tetris.getSpace().spawnTetromino();
else Tetris.stopGame();
}
fallLength += 1;
centerPos = newPos;
}
private boolean collidesVertically(Vec2i newPos) {
if (Tetris.space == null) return false;
boolean collides = newPos.getY() + this.collision.length > Tetris.space.getMapHeight(); // Bottom check
if (!collides) {
for (int i = 0; i < collision[0].length; i++) {
int maxCollisionY = collision.length - 1;
while (collision[maxCollisionY][i] == 0) maxCollisionY--;
if (newPos.getY()+maxCollisionY >= Tetris.space.getGameMap().length) continue;
collides |= Tetris.space.getGameMap()[newPos.getY() + maxCollisionY][newPos.getX() + i] != null; // Check for other tetrominos
}
}
return collides;
}
public void move(int xOffset) {
Vec2i newPos = centerPos.offset(Vec2i.of(xOffset, 0));
if (collidesHorizontally(newPos, xOffset)) {
@@ -56,15 +42,34 @@ public class Tetromino {
centerPos = newPos;
}
private boolean collidesVertically(Vec2i newPos) {
if (Tetris.getSpace() == null) return false;
boolean collides = newPos.getY() + this.collision.length > Tetris.getSpace().getMapHeight(); // Bottom check
if (!collides) { // Check for other tetrominos
for (int i = 0; i < collision[0].length; i++) {
int maxCollisionY = collision.length - 1;
while (collision[maxCollisionY][i] == 0) maxCollisionY--; // Figure out the collision box's bounding
if (newPos.getY()+maxCollisionY >= Tetris.getSpace().getMapHeight() || newPos.getX() + i >= Tetris.getSpace().getMapWidth()) continue;
collides |= Tetris.getSpace().getGameMap()[newPos.getY() + maxCollisionY][newPos.getX() + i] != null;
}
}
return collides;
}
private boolean collidesHorizontally(Vec2i newPos, int xOffset) {
if (Tetris.space == null) return false;
boolean collides = newPos.getX() < 0 || newPos.getX() + collision[0].length > Tetris.space.getGameMap()[0].length;
if (!collides) {
if (Tetris.getSpace() == null) return false;
boolean collides = newPos.getX() < 0 || newPos.getX() + collision[0].length > Tetris.getSpace().getMapWidth(); // Side check
if (!collides) { // Check for other tetrominos
for (int i = 0; i < collision.length; i++) {
int maxCollisionX = xOffset > 0 ? collision[i].length - 1 : 0;
while (collision[i][maxCollisionX] == 0) maxCollisionX += xOffset > 0 ? -1 : 1;
if (newPos.getY()+maxCollisionX >= Tetris.space.getGameMap().length) continue;
collides |= Tetris.space.getGameMap()[newPos.getY() + i][newPos.getX() + maxCollisionX] != null; // Check for other tetrominos
while (collision[i][maxCollisionX] == 0) maxCollisionX += xOffset > 0 ? -1 : 1; // Figure out the collision box's bounding
if (newPos.getY()+maxCollisionX >= Tetris.getSpace().getMapHeight()) continue;
collides |= Tetris.getSpace().getGameMap()[newPos.getY() + i][newPos.getX() + maxCollisionX] != null;
}
}
return collides;

View File

@@ -25,11 +25,11 @@ public class GameCanvas extends JPanel {
super.paintComponent(graphics);
if (graphics == null) return;
for (int y = 0; y < Tetris.space.getGameMapWithTetromino().length; y++) {
for (int x = 0; x < Tetris.space.getGameMapWithTetromino()[y].length; x++) {
Color color = Tetris.space.getGameMapWithTetromino()[y][x];
for (int y = 0; y < Tetris.getSpace().getMapHeight(); y++) {
for (int x = 0; x < Tetris.getSpace().getMapWidth(); x++) {
Color color = Tetris.getSpace().getGameMapWithTetromino()[y][x];
if (color == null) continue;
int blockSize = (int) Math.ceil((float) (this.getWidth() - this.getInsets().left - this.getInsets().right) / Tetris.space.getGameMapWithTetromino()[0].length);
int blockSize = (int) Math.ceil((float) (this.getWidth() - this.getInsets().left - this.getInsets().right) / Tetris.getSpace().getMapWidth());
//graphics.setXORMode(withAlpha(color,0));
graphics.drawImage(texture, x*blockSize +getInsets().left, y*blockSize + getInsets().top, blockSize, blockSize, color, this);
graphics.setColor(withAlpha(color, 120));

View File

@@ -0,0 +1,34 @@
package eu.midnightdust.yaytris.ui;
import javax.swing.*;
public class ScoreMenu extends AbstractMenu {
final TetrisUI ui;
final JLabel gameOverLabel;
final JLabel currentScoreLabel;
ScoreMenu(int x, int y, int width, int height, TetrisUI ui) {
this.ui = ui;
this.setBounds(x, y, width, height);
this.setLayout(null);
this.gameOverLabel = new JLabel();
this.add(gameOverLabel);
this.currentScoreLabel = new JLabel("Score: 0");
this.add(currentScoreLabel);
JButton backButton = new JButton("Zurück");
backButton.addActionListener(ui::openMainMenu);
this.add(backButton);
}
public void updateScore(int score) {
this.currentScoreLabel.setText("Score: %s".formatted(score));
this.repaint();
}
public void gameOver() {
this.gameOverLabel.setText("Game over :(");
}
}

View File

@@ -1,6 +1,5 @@
package eu.midnightdust.yaytris.ui;
import eu.midnightdust.yaytris.Settings;
import eu.midnightdust.yaytris.Tetris;
import eu.midnightdust.yaytris.game.Space;
@@ -12,10 +11,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.util.TimerTask;
import static eu.midnightdust.yaytris.Settings.guiScale;
import static eu.midnightdust.yaytris.Tetris.timer;
public class TetrisUI extends JFrame implements KeyListener {
JLabel titleLabel;
@@ -76,25 +73,22 @@ public class TetrisUI extends JFrame implements KeyListener {
return gamePanel;
}
public JPanel getMenuPanel() {
return menuPanel;
}
public void startGame(ActionEvent actionEvent) {
Tetris.space.spawnTetromino();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (Tetris.space.getCurrentTetromino() != null) {
Tetris.space.getCurrentTetromino().fall(1);
Tetris.ui.getGamePanel().repaint();
}
}
}, 1, Settings.difficulty.getTimerPeriod());
Tetris.startGame();
//this.remove(menuPanel);
//menuPanel = null;
this.openScoreMenu(actionEvent);
this.requestFocus();
this.repaint();
}
public void openMainMenu(ActionEvent actionEvent) {
if (this.menuPanel != null) this.remove(menuPanel);
Tetris.resetSpace();
rescale();
menuPanel = new MainMenu(scale(170), scale(40), scale(220), scale(226), this);
menuPanel.setBackground(Color.DARK_GRAY);
@@ -112,6 +106,15 @@ public class TetrisUI extends JFrame implements KeyListener {
this.repaint();
}
public void openScoreMenu(ActionEvent actionEvent) {
if (this.menuPanel != null) this.remove(menuPanel);
menuPanel = new ScoreMenu(scale(170), scale(40), scale(220), scale(226), this);
menuPanel.setBackground(Color.DARK_GRAY);
menuPanel.setBorder(new LineBorder(Color.GRAY, scale(2)));
this.add(menuPanel);
this.repaint();
}
// Source: https://stackoverflow.com/a/19746437
private void setWindowPosition(JFrame window, int screen) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
@@ -139,44 +142,36 @@ public class TetrisUI extends JFrame implements KeyListener {
window.setLocation(windowPosX, windowPosY);
}
@Override
public void keyTyped(KeyEvent e) {
//System.out.println("Typed");
}
@Override
public void keyPressed(KeyEvent e) {
//System.out.println("Pressed");
if (e.getKeyCode() == KeyEvent.VK_E) {
Tetris.space.spawnTetromino();
//Tetris.space.onLinesChanged(Tetris.space.getCurrentTetromino(), 0, 1, 2, 3);
}
else if (e.getKeyCode() == KeyEvent.VK_W) {
Tetris.space.getCurrentTetromino().rotate();
//Tetris.space.onLinesChanged(Tetris.space.getCurrentTetromino(), 0, 1, 2, 3);
}
else if (e.getKeyCode() == KeyEvent.VK_D) {
Tetris.space.getCurrentTetromino().move(1);
//Tetris.space.onLinesChanged(Tetris.space.getCurrentTetromino(), 0, 1, 2, 3);
}
else if (e.getKeyCode() == KeyEvent.VK_A) {
Tetris.space.getCurrentTetromino().move(-1);
//Tetris.space.onLinesChanged(Tetris.space.getCurrentTetromino(), 0, 1, 2, 3);
}
else if (e.getKeyCode() == KeyEvent.VK_S) {
Tetris.space.getCurrentTetromino().fall(1);
//Tetris.space.onLinesChanged(Tetris.space.getCurrentTetromino(), 0, 1, 2, 3);
}
else if (e.getKeyCode() == KeyEvent.VK_SPACE) {
Tetris.space.getCurrentTetromino().fall(12);
//Tetris.space.onLinesChanged(Tetris.space.getCurrentTetromino(), 0, 1, 2, 3);
switch (e.getKeyCode()) {
case KeyEvent.VK_E:
Tetris.getSpace().spawnTetromino();
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
Tetris.getSpace().getCurrentTetromino().rotate();
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_D:
Tetris.getSpace().getCurrentTetromino().move(1);
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
Tetris.getSpace().getCurrentTetromino().move(-1);
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_S:
Tetris.getSpace().getCurrentTetromino().fall(1);
break;
case KeyEvent.VK_SPACE:
Tetris.getSpace().getCurrentTetromino().fall(12);
break;
}
gamePanel.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
//System.out.println("Released");
}
// Unused (But required overrides)
@Override public void keyReleased(KeyEvent e) {}
@Override public void keyTyped(KeyEvent e) {}
}