feat: preview of next shape

This commit is contained in:
Martin Prokoph
2025-07-09 13:00:48 +02:00
parent 7671bdec04
commit 7ef6e246ff
9 changed files with 99 additions and 13 deletions

View File

@@ -16,23 +16,27 @@ public class Space {
public Space() { public Space() {
gameMap = new Color[14][8]; gameMap = new Color[14][8];
nextShape = getNextShape(); nextShape = generateNextShape();
score = 0; score = 0;
} }
public void spawnTetromino() { public void spawnTetromino() {
currentTetromino = new Tetromino(nextShape); currentTetromino = new Tetromino(nextShape);
nextShape = getNextShape(); nextShape = generateNextShape();
} }
public Tetromino getCurrentTetromino() { public Tetromino getCurrentTetromino() {
return currentTetromino; return currentTetromino;
} }
public TetrominoShape getNextShape() { public TetrominoShape generateNextShape() {
return TetrominoShape.values()[random.nextInt(TetrominoShape.values().length)]; return TetrominoShape.values()[random.nextInt(TetrominoShape.values().length)];
} }
public TetrominoShape getNextShape() {
return nextShape;
}
public int getMapWidth() { public int getMapWidth() {
return gameMap[0].length; return gameMap[0].length;
} }

View File

@@ -41,8 +41,17 @@ public enum TetrominoShape {
final int[][] boundary; final int[][] boundary;
final Color color; final Color color;
TetrominoShape(int[][] boundary, Color color) { TetrominoShape(int[][] boundary, Color color) {
this.boundary = boundary; this.boundary = boundary;
this.color = color; this.color = color;
} }
public int[][] getBoundary() {
return boundary;
}
public Color getColor() {
return color;
}
} }

View File

@@ -0,0 +1,44 @@
package eu.midnightdust.yaytris.ui;
import eu.midnightdust.yaytris.Tetris;
import eu.midnightdust.yaytris.game.Tetromino;
import eu.midnightdust.yaytris.game.TetrominoShape;
import eu.midnightdust.yaytris.util.FileUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import static eu.midnightdust.yaytris.game.TetrominoShape.T;
public class PreviewCanvas extends JPanel {
final TetrisUI ui;
final BufferedImage texture;
PreviewCanvas(TetrisUI ui) {
this.ui = ui;
this.texture = FileUtil.loadImage("/textures/tetromino.png");
}
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
if (graphics == null) return;
TetrominoShape nextShape = Tetris.getSpace().getNextShape();
for (int y = 0; y < nextShape.getBoundary().length; y++) {
for (int x = 0; x < nextShape.getBoundary()[0].length; x++) {
Color color = nextShape.getBoundary()[y][x] == 0 ? null : nextShape.getColor(); // Get the color of the blob at these coordinates
if (color == null) continue; // Ignore empty cells
int blockSize = (int) Math.ceil((float) (this.getWidth() - this.getInsets().left - this.getInsets().right) / 4);
int startX = x * blockSize + getInsets().left + ((blockSize / 2) * (4 - nextShape.getBoundary()[0].length));
graphics.drawImage(texture, startX, y*blockSize + getInsets().top, blockSize, blockSize, color, this); // Draw out (grayscale) texture
graphics.setColor(withAlpha(color, 120)); // Overlay the texture with the blobs color
graphics.fillRect(startX, y*blockSize + getInsets().top, blockSize, blockSize);
}
}
}
public static Color withAlpha(Color color, int alpha) {
return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
}
}

View File

@@ -1,11 +1,18 @@
package eu.midnightdust.yaytris.ui; package eu.midnightdust.yaytris.ui;
import eu.midnightdust.yaytris.util.CatppuccinColor;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import static eu.midnightdust.yaytris.ui.TetrisUI.scale;
public class ScoreMenu extends AbstractMenu { public class ScoreMenu extends AbstractMenu {
final TetrisUI ui; final TetrisUI ui;
final PreviewCanvas previewCanvas;
final JLabel gameOverLabel; final JLabel gameOverLabel;
final JLabel currentScoreLabel; final JLabel currentScoreLabel;
final JLabel currentTimeLabel; final JLabel currentTimeLabel;
@@ -15,6 +22,12 @@ public class ScoreMenu extends AbstractMenu {
this.setBounds(x, y, width, height); this.setBounds(x, y, width, height);
this.setLayout(null); this.setLayout(null);
this.previewCanvas = new PreviewCanvas(ui);
this.previewCanvas.setBounds(scale(92), scale(4), scale(40), scale(40));
this.previewCanvas.setBackground(Color.BLACK);
this.previewCanvas.setBorder(new LineBorder(CatppuccinColor.SURFACE0.getColor()));
this.add(previewCanvas, 0);
this.gameOverLabel = new JLabel(); this.gameOverLabel = new JLabel();
this.add(gameOverLabel); this.add(gameOverLabel);

View File

@@ -25,7 +25,7 @@ public class SettingsMenu extends JPanel {
}); });
this.add(musicVolumeSlider); this.add(musicVolumeSlider);
this.add(new JLabel("Sound-Lautstärke:")); this.add(new JLabel("Effekt-Lautstärke:"));
JSlider soundVolumeSlider = new JSlider(0, 100, Settings.soundVolume); JSlider soundVolumeSlider = new JSlider(0, 100, Settings.soundVolume);
soundVolumeSlider.addChangeListener(change -> { soundVolumeSlider.addChangeListener(change -> {
Settings.soundVolume = soundVolumeSlider.getValue(); Settings.soundVolume = soundVolumeSlider.getValue();
@@ -33,8 +33,8 @@ public class SettingsMenu extends JPanel {
}); });
this.add(soundVolumeSlider); this.add(soundVolumeSlider);
this.add(new JLabel("Skalierung:")); this.add(new JLabel("Fenster-Skalierung:"));
JSlider scaleSlider = new JSlider(100, 500, (int) (Settings.guiScale * 100)); JSlider scaleSlider = new JSlider(100, 600, (int) (Settings.guiScale * 100));
scaleSlider.addChangeListener(change -> { scaleSlider.addChangeListener(change -> {
Settings.guiScale = scaleSlider.getValue() / 100f; Settings.guiScale = scaleSlider.getValue() / 100f;
Settings.write(); Settings.write();

View File

@@ -21,7 +21,6 @@ public class TetrisUI extends JFrame implements KeyListener {
JPanel menuPanel; JPanel menuPanel;
public TetrisUI() { public TetrisUI() {
Space space = new Space();
this.setLayout(null); this.setLayout(null);
this.setTitle("Tetris"); this.setTitle("Tetris");
this.setSize((int) (400 * guiScale), (int) (300 * guiScale)); this.setSize((int) (400 * guiScale), (int) (300 * guiScale));

View File

@@ -1,14 +1,26 @@
package eu.midnightdust.yaytris.util; package eu.midnightdust.yaytris.util;
public enum Difficulty { public enum Difficulty {
NOOB(2000), EASY(1200), NORMAL(1000), HARD(500), EXTREME(100), WTF(30); NOOB(2000), EASY(1200), NORMAL(1000), HARD(500), EXTREME(100), WTF(30, "WTF");
private final int timerPeriod; private final int timerPeriod;
private final String name;
Difficulty(int timerPeriod) { Difficulty(int timerPeriod) {
this.timerPeriod = timerPeriod; this.timerPeriod = timerPeriod;
this.name = this.name().charAt(0) + this.name().substring(1).toLowerCase();
}
Difficulty(int timerPeriod, String name) {
this.timerPeriod = timerPeriod;
this.name = name;
} }
public int getTimerPeriod() { public int getTimerPeriod() {
return timerPeriod; return timerPeriod;
} }
@Override
public String toString() {
return this.name;
}
} }

View File

@@ -4,6 +4,7 @@ import eu.midnightdust.yaytris.Settings;
import javax.sound.sampled.*; import javax.sound.sampled.*;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -23,7 +24,7 @@ public class SoundUtil {
// Adapted from: https://www.baeldung.com/java-play-sound // Adapted from: https://www.baeldung.com/java-play-sound
public static void playSoundClip(String fileLocation) { public static void playSoundClip(String fileLocation) {
try (AudioInputStream stream = AudioSystem.getAudioInputStream(SoundUtil.class.getResource(fileLocation))) { // FIXME: Support audio files from JAR. File streams won't work here for some reason. try (AudioInputStream stream = AudioSystem.getAudioInputStream(getResource(fileLocation))) { // FIXME: Support audio files from JAR. File streams won't work here for some reason.
AudioFormat format = stream.getFormat(); AudioFormat format = stream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format); DataLine.Info info = new DataLine.Info(Clip.class, format);
@@ -37,6 +38,10 @@ public class SoundUtil {
} }
} }
private static URL getResource(String fileLocation) {
return SoundUtil.class.getResource(fileLocation);
}
// Adapted from: https://stackoverflow.com/a/40698149 // Adapted from: https://stackoverflow.com/a/40698149
private static void setVolume(Line line, int volume) { private static void setVolume(Line line, int volume) {
if (volume < 0 || volume > 100) if (volume < 0 || volume > 100)
@@ -68,7 +73,7 @@ public class SoundUtil {
// Adapted from: https://www.baeldung.com/java-play-sound // Adapted from: https://www.baeldung.com/java-play-sound
private void playMusic(String fileLocation) { private void playMusic(String fileLocation) {
try (AudioInputStream stream = AudioSystem.getAudioInputStream(SoundUtil.class.getResource(fileLocation))) { try (AudioInputStream stream = AudioSystem.getAudioInputStream(getResource(fileLocation))) {
AudioFormat format = stream.getFormat(); AudioFormat format = stream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

View File

@@ -1,6 +1,6 @@
{ {
"musicVolume": 50, "musicVolume": 100,
"soundVolume": 100, "soundVolume": 100,
"guiScale": 5.0, "guiScale": 6.0,
"difficulty": "NORMAL" "difficulty": "Normal"
} }