This is the last course design for Java courses. Since Java is a beginner, I have referred to some blogs of technical experts when doing it. I would like to express my gratitude here.
Post here to communicate and learn.
If you need complete project files, documentation and runnable jar files, download address: click to open the link
RussianBlocksGame.java
package RussiaBlocksGame; import java.awt.*; import java.awt.event.*; import javax.swing.border.Border; import javax.swing.border.EtchedBorder; /** * The main game class, inherited from the JFrame class, is responsible for the global control of the game. Includes: 1. An instance object of the GameCanvas canvas class, * 2. An object that holds the current active block (RussiaBlock) instance; 3. An object that holds the current control panel (ControlPanel) instance; */ public class RussiaBlocksGame extends JFrame { private static final long serialVersionUID = -7332245439279674749L; /** * How many points are counted for each row filled*/ public final static int PER_LINE_SCORE = 100; /** * How many points can be upgraded after accumulation*/ public final static int PER_LEVEL_SCORE = PER_LINE_SCORE * 20; /** * The maximum series is level 10*/ public final static int MAX_LEVEL = 10; /** * The default series is 2 */ public final static int DEFAULT_LEVEL = 2; private GameCanvas canvas; private ErsBlock block; private boolean playing = false; private ControlPanel ctrlPanel; //Initialize menu bar private JMenuBar bar = new JMenuBar(); private JMenu mGame = new JMenu("Game"), mControl = new JMenu("Control"), mInfo = new JMenu("Help"); private JMenuItem miNewGame = new JMenuItem("New Game"), miSetBlockColor = new JMenuItem("Set Block Color..."), miSetBackColor = new JMenuItem("Set Background Color..."), miTurnHarder = new JMenuItem("Elevate Game Difficulty"), miTurnEasier = new JMenuItem("Reduce Game Difficulty"), miExit = new JMenuItem("Exit"), miPlay = new JMenuItem("Start"), miPause = new JMenuItem("Pause"), miResume = new JMenuItem("Resume"), miStop = new JMenuItem("Terminate the game"), miRule = new JMenuItem("Game Rules"), miAuthor = new JMenuItem("About this game"); /** * Create and set the window menu*/ private void creatMenu() { bar.add(mGame); bar.add(mControl); bar.add(mInfo); mGame.add(miNewGame); mGame.addSeparator();//Add horizontal split line mGame.add(miSetBlockColor); mGame.add(miSetBlockColor); mGame.addSeparator();//Add horizontal split line mGame.add(miTurnHarder); mGame.add(miTurnEasier); mGame.addSeparator();//Add horizontal split line mGame.add(miExit); mControl.add(miPlay); miPlay.setEnabled(true); mControl.add(miPause); miPause.setEnabled(false); mControl.add(miResume); miResume.setEnabled(false); mControl.add(miStop); miStop.setEnabled(false); mInfo.add(miRule); mInfo.add(miAuthor); setJMenuBar(bar); miNewGame.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { stopGame(); reset(); setLevel(DEFAULT_LEVEL); } }); //Set the block color miSetBlockColor.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Color newFrontColor = JColorChooser.showDialog(RussiaBlocksGame.this, "Set the block color", canvas.getBlockColor()); if (newFrontColor != null) { canvas.setBlockColor(newFrontColor); } } }); //Set the background color miSetBackColor.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Color newBackColor = JColorChooser.showDialog(RussiaBlocksGame.this, "Set the background color", canvas.getBackgroundColor()); if (newBackColor != null) { canvas.setBackgroundColor(newBackColor); } } }); //Define the function of the menu bar "About" and a confirmation box pops up. miAuthor.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, "Software Engineering (4) Class/n3115005372/nYang Yujie/n©All the right to interpret is owned by Yang Yujie", "About Tetris- 2016", 1); } }); //Game rules description miRule.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, "Plates of different shapes composed of small squares fall from the top of the screen one after another. The /n player adjusts the position and direction of the plates to make them spell out a complete one or several pieces at the bottom of the screen. These complete horizontal bars will disappear immediately, making room for the new /n fallen plates. At the same time, the player gets a score reward. The blocks that are not eliminated are constantly piled up. Once they are piled up to the top of the screen, the player will lose and the game ends. ", "Game Rules", 1); } }); //Increase the difficulty miTurnHarder.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int curLevel = getLevel(); if (!playing && curLevel < MAX_LEVEL) { setLevel(curLevel + 1); } } }); //Reduce the difficulty miTurnEasier.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int curLevel = getLevel(); if (!playing && curLevel > 1) { setLevel(curLevel - 1); } } }); //Exit button action response miExit.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); } /** * Constructor method of the main game class* * @param title String ,window title*/ public RussiaBlocksGame(String title) { super(title); //Set the title setSize(500, 600); //Set the window size setLocationRelativeTo(null); //Set the window center createMenu(); Container container = getContentPane(); //Create menu bar container.setLayout(new BorderLayout(6, 0)); //Set the layout manager of the window canvas = new GameCanvas(20, 15); //Create a new game canvas ctrlPanel = new ControlPanel(this); //Create a new control panel container.add(canvas, BorderLayout.CENTER); //Add canvas container.add(ctrlPanel, BorderLayout.EAST); //Add control panel on the right //Register window events. When the Close button is clicked, the game ends and the system exits. addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent we) { stopGame(); System.exit(0); } }); //Automatically adjust the size of the square according to the window size addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent ce) { canvas.adjust(); } }); setVisible(true); canvas.adjust(); } /** * Reset the game*/ public void reset() { //Reset the canvas, and reset the control panel ctrlPanel.setPlayButtonEnable(true); ctrlPanel.setPauseButtonEnable(false); ctrlPanel.setPauseButtonLabel(true); ctrlPanel.setStopButtonEnable(false); ctrlPanel.setTurnLevelDownButtonEnable(true); ctrlPanel.setTurnLevelUpButtonEnable(true); miPlay.setEnabled(true); miPause.setEnabled(false); miResume.setEnabled(false); miStop.setEnabled(false); ctrlPanel.reset(); canvas.reset(); } /** * Determine whether the game is still in progress* * @return boolean,true - is still running, false-has stopped*/ public boolean isPlaying() { return playing; } /** * Get the currently active block* * @return ErsBlock, the reference to the currently active block*/ public ErsBlock getCurBlock() { return block; } /** * Get the current canvas* * @return GameCanvas, the reference to the current canvas*/ public GameCanvas getCanvas() { return canvas; } /** * Start the game*/ public void playGame() { play(); ctrlPanel.setPlayButtonEnable(false); ctrlPanel.setPauseButtonEnable(true); ctrlPanel.setPauseButtonLabel(true); ctrlPanel.setStopButtonEnable(true); ctrlPanel.setTurnLevelDownButtonEnable(false); ctrlPanel.setTurnLevelUpButtonEnable(false); miStop.setEnabled(true); miTurnHarder.setEnabled(false); miTurnEasier.setEnabled(false); ctrlPanel.requestFocus(); //Set focus on the control panel} /** * Game pause*/ public void pauseGame() { if (block != null) { block.pauseMove(); } ctrlPanel.setPlayButtonEnable(false); ctrlPanel.setPauseButtonLabel(false); ctrlPanel.setStopButtonEnable(true); miPlay.setEnabled(false); miPause.setEnabled(false); miResume.setEnabled(true); miStop.setEnabled(true); } /** * Let the paused game continue*/ public void resumeGame() { if (block != null) { block.resumeMove(); } ctrlPanel.setPlayButtonEnable(false); ctrlPanel.setPauseButtonEnable(true); ctrlPanel.setPauseButtonLabel(true); miPause.setEnabled(true); miResume.setEnabled(false); ctrlPanel.requestFocus(); } /** * User stops the game*/ public void stopGame() { playing = false; if (block != null) { block.stopMove(); } ctrlPanel.setPlayButtonEnable(true); ctrlPanel.setPauseButtonEnable(false); ctrlPanel.setPauseButtonLabel(true); ctrlPanel.setStopButtonEnable(false); ctrlPanel.setTurnLevelDownButtonEnable(true); ctrlPanel.setTurnLevelUpButtonEnable(true); miPlay.setEnabled(true); miPause.setEnabled(false); miResume.setEnabled(false); miStop.setEnabled(false); miTurnHarder.setEnabled(true); miTurnEasier.setEnabled(true); reset();//Reset canvas and control panel} /** * Get the difficulty set by the player* * @return int , game difficulty 1-MAX_LEVEL */ public int getLevel() { return ctrlPanel.getLevel(); } /** * User sets game difficulty* * @param level int , game difficulty 1-MAX_LEVEL */ public void setLevel(int level) { if (level < 11 && level > 0) { ctrlPanel.setLevel(level); } } /** * Get game points* * @return int, points*/ public int getScore() { if (canvas != null) { return canvas.getScore(); } return 0; } /** * Get game points since the last upgrade. After the upgrade, this point will be cleared* * @return int,ints*/ public int getScoreForLevelUpdate() { if (canvas != null) { return canvas.getScoreForLevelUpdate(); } return 0; } /** * When the integral accumulates to a certain value, upgrade once* * @return Boolean, true-update successe, false-update fail */ public boolean levelUpdate() { int curLevel = getLevel(); if (curLevel < MAX_LEVEL) { setLevel(curLevel + 1); canvas.resetScoreForLevelUpdate(); return true; } return false; } /** * Game start*/ private void play() { reset(); playing = true; Thread thread = new Thread(new Game());//Start the game thread thread.start(); } /** * Report the game is over*/ private void reportGameOver() { new gameOverDialog(this, "Tetris", "The game ends, your score is " + canvas.getScore()); } /** * One round of game process, implementing the Runnable interface. A round of game is a big loop. In this loop, every 100 milliseconds, check whether the current block in the game has reached the end. If not, * will continue to wait. If it comes to the end, it depends on whether there is a fully filled line. If there is, delete it and add points to the player, and randomly generate a new current block and let it fall automatically. * When a new block is generated, first check whether the line on the top of the canvas has been occupied. If so, you can judge Game Over. */ private class Game implements Runnable { @Override public void run() { int col = (int) (Math.random() * (canvas.getCols() - 3));// Randomly generates the position of the block int style = ErsBlock.STYLES[ (int) (Math.random() * 7)][(int) (Math.random() * 4)];// Randomly generates a square of a shape while (playing) { if (block != null) { //The block is empty if (block.isAlive()) { try { Thread.currentThread(); Thread.sleep(500); } catch (InterruptedException ie) { ie.printStackTrace(); } continue; } } checkFullLine(); //Check whether there are fully filled lines if (isGameOver()) { reportGameOver(); miPlay.setEnabled(true); miPause.setEnabled(false); miResume.setEnabled(false); miStop.setEnabled(false); ctrlPanel.setPlayButtonEnable(true); ctrlPanel.setPauseButtonLabel(false); ctrlPanel.setStopButtonEnable(false); return; } block = new ErsBlock(style, -1, col, getLevel(), canvas); block.start(); col = (int) (Math.random() * (canvas.getCols() - 3)); style = ErsBlock.STYLES[ (int) (Math.random() * 7)][(int) (Math.random() * 4)]; ctrlPanel.setTipStyle(style); } } //Check whether there are fully filled lines in the canvas. If there is one, delete it public void checkFullLine() { for (int i = 0; i < canvas.getRows(); i++) { int row = -1; boolean fullLineColorBox = true; for (int j = 0; j < canvas.getCols(); j++) { if (!canvas.getBox(i, j).isColorBox()) { fullLineColorBox = false; break; } } if (fullLineColorBox) { row = i--; canvas.removeLine(row); } } } //Judge whether the game has ended based on whether the top row is occupied//@return boolean , true-the game is over, false-the game is not over private boolean isGameOver() { for (int i = 0; i < canvas.getCols(); i++) { ErsBox box = canvas.getBox(0, i); if (box.isColorBox()) { return true; } } return false; } } /** * Define the GameOver dialog box. */ @SuppressWarnings("serial") private class gameOverDialog extends JDialog implements ActionListener { private JButton againButton, exitButton; private Border border = new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140)); public gameOverDialog(JFrame parent, String title, String message) { super(parent, title, true); if (parent != null) { setSize(240, 120); this.setLocationRelativeTo(parent); JPanel messagePanel = new JPanel(); messagePanel.add(new JLabel(message)); messagePanel.setBorder(border); Container container = this.getContentPane(); container.setLayout(new GridLayout(2, 0, 0, 10)); container.add(messagePanel); JPanel choosePanel = new JPanel(); choosePanel.setLayout(new GridLayout(0, 2, 4, 0)); container.add(choosePanel); againButton = new JButton("play another game"); exitButton = new JButton("Exit game"); choosePanel.add(new JPanel().add(againButton)); choosePanel.add(new JPanel().add(exitButton)); choosePanel.setBorder(border); } againButton.addActionListener(this); exitButton.addActionListener(this); this.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == againButton) { this.setVisible(false); reset(); } else if (e.getSource() == exitButton) { stopGame(); System.exit(0); } } } }GameCanvas.java
package RussiaBlocksGame; import java.awt.Color; import java.awt.Graphics; import javax.swing.JPanel; import javax.swing.border.EtchedBorder; /** * Canvas class, with <number of rows>*<number of columns> square class instances. Inherited from JPanel class. The ErsBlock thread class dynamically changes the grid color of the canvas class. The canvas class uses * to check the grid color to reflect the movement of the ErsBlock block. */ public class GameCanvas extends JPanel { private static final long serialVersionUID = 6732901391026089276L; private Color backColor = Color.darkGray, frontColor = Color.WHITE; private int rows, cols, score = 0, scoreForLevelUpdate = 0; private ErsBox[][] boxes; private int boxWidth, boxHeight; /** * Constructor of the canvas class* * @param rows int, the number of rows of the canvas* @param cols int,The number of columns and columns of the canvas determines the number of squares the canvas has */ public GameCanvas(int rows, int cols) { this.rows = rows; this.cols = cols; boxes = new ErsBox[rows][cols]; for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { boxes[i][j] = new ErsBox(false); } } setBorder(new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140)); } /** * Constructor of the canvas class* * @param rows * @param cols * @param backColor * @param frontColor */ public GameCanvas(int rows, int cols, Color backColor, Color frontColor) { this(rows, cols); this.backColor = backColor; this.frontColor = frontColor; } /** * Set game background color* * @param backColor Color, background color*/ public void setBackgroundColor(Color backColor) { this.backColor = backColor; } /** * Get the game background color* * @return Color ,background color*/ public Color getBackgroundColor() { return backColor; } /** * Set the game block color* * @param frontColor Color, block color*/ public void setBlockColor(Color frontColor) { this.frontColor = frontColor; } /** * Get the game block color* * @return Color, block color*/ public Color getBlockColor() { return frontColor; } /** * Get the number of rows of the square in the canvas* * @return */ public int getRows() { return rows; } /** * Get the number of columns of the square in the canvas* * @return int, the number of columns of the square*/ public int getCols() { return cols; } /** * Get the game score* * @return int, score*/ public int getScore() { return score; } /** * Get the points since the last upgrade* * @return int , the points after the last upgrade*/ public int getScoreForLevelUpdate() { return scoreForLevelUpdate; } /** * After upgrading, clear the points since the last upgrade*/ public void resetScoreForLevelUpdate() { scoreForLevelUpdate -= RussiaBlocksGame.PER_LEVEL_SCORE; } /** * Get the square reference of a certain row and column* * @return row int , the row where the square is to be referenced* @param col int, the row where the square is to be referenced* @return ErsBox, the reference of the square in the row col column*/ public ErsBox getBox(int row, int col) { if (row < 0 || row > boxes.length - 1 || col < 0 || col > boxes[0].length - 1) { return null; } return (boxes[row][col]); } /** * Overwrite the functions of the JComponent class and draw components. * * @param g Graphics device environment*/ @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(frontColor); for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { g.setColor(boxes[i][j].isColorBox() ? frontColor : backColor); g.fill3DRect(j * boxWidth, i * boxHeight, boxWidth, boxHeight, true); } } } /** * Automatically adjust the size of the square according to the window size*/ public void adjust() { boxWidth = getSize().width / cols; boxHeight = getSize().height / rows; } /** * When a row is full by the player, clear this line and add points to the player* * @param row int, the row to be cleared is calculated by the ErsBoxesGame class */ public synchronized void removeLine(int row) { for (int i = row; i > 0; i--) { for (int j = 0; j < cols; j++) { boxes[i][j] = (ErsBox) boxes[i - 1][j].clone(); //Clone the color of the square in the previous row, } //Eliminate a row of squares} score += RussiaBlocksGame.PER_LEVEL_SCORE; scoreForLevelUpdate += RussiaBlocksGame.PER_LEVEL_SCORE; repaint(); } /** * Reset the canvas and set the integral to zero*/ public void reset() { score = 0; scoreForLevelUpdate = 0; for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { boxes[i][j].setColor(false); } } repaint(); } }ControlPanel.java
package RussiaBlocksGame; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.Timer; import javax.swing.border.Border; import javax.swing.border.EtchedBorder; /** * Control panel class, inherited from JPanel. The preview window, level, score, and control buttons are placed on it. */ class ControlPanel extends JPanel { private static final long serialVersionUID = 3900659640646175724L; private JTextField tfLevel = new JTextField("" + RussiaBlocksGame.DEFAULT_LEVEL), tfScore = new JTextField(" 0"), tfTime = new JTextField(" "); private JButton btPlay = new JButton(" Start"), btPause = new JButton(" pause"), btStop = new JButton("End the game"), btTurnLevelUp = new JButton("Increase the difficulty"), btTurnLevelDown = new JButton("Reduce the difficulty"); private JPanel plTip = new JPanel(new BorderLayout()); private TipPanel plTipBlock = new TipPanel(); private JPanel plInfo = new JPanel(new GridLayout(4, 1)); private JPanel plButton = new JPanel(new GridLayout(6, 1)); private Timer timer; private Border border = new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140)); /** * Constructor of the control panel class* * @param game ErsBlocksGame, an instance reference of the ErsBlocksGame class is convenient for directly controlling the behavior of the ErsBlocksGame class. */ public ControlPanel(final RussiaBlocksGame game) { setLayout(new GridLayout(3, 1, 0, 2)); plTip.add(new JLabel("Next Square"), BorderLayout.NORTH); //Add component plTip.add(plTipBlock); plTip.setBorder(border); plInfo.add(new JLabel("Difficulty coefficient")); plInfo.add(tfLevel); plInfo.add(new JLabel("Score")); plInfo.add(tfScore); plInfo.setBorder(border); plButton.add(btPlay); btPlay.setEnabled(true); plButton.add(btPause); btPause.setEnabled(false); plButton.add(btStop); btStop.setEnabled(false); plButton.add(btTurnLevelUp); plButton.add(btTurnLevelDown); plButton.add(tfTime); plButton.setBorder(border); tfLevel.setEditable(false); tfScore.setEditable(false); tfTime.setEditable(false); add(plTip); add(plInfo); add(plButton); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent ke) { if (!game.isPlaying()) { return; } ErsBlock block = game.getCurBlock(); switch (ke.getKeyCode()) { case KeyEvent.VK_DOWN: block.moveDown(); break; case KeyEvent.VK_LEFT: block.moveLeft(); break; case KeyEvent.VK_RIGHT: block.moveRight(); break; case KeyEvent.VK_UP: block.turnNext(); break; default: break; } } }); btPlay.addActionListener(new ActionListener() { //Start the game @Override public void actionPerformed(ActionEvent ae) { game.playGame(); } }); btPause.addActionListener(new ActionListener() { //Pause the game @Override public void actionPerformed(ActionEvent ae) { if (btPause.getText().equals(" Pause")) { game.pauseGame(); } else { game.resumeGame(); } } }); btStop.addActionListener(new ActionListener() { //Stop the game @Override public void actionPerformed(ActionEvent ae) { game.stopGame(); } }); btTurnLevelUp.addActionListener(new ActionListener() { // Increase difficulty @Override public void actionPerformed(ActionEvent ae) { try { int level = Integer.parseInt(tfLevel.getText()); if (level < RussiaBlocksGame.MAX_LEVEL) { tfLevel.setText("" + (level + 1)); } } catch (NumberFormatException e) { } requestFocus(); } }); btTurnLevelDown.addActionListener(new ActionListener() { //Reduce game difficulty @Override public void actionPerformed(ActionEvent ae) { try { int level = Integer.parseInt(tfLevel.getText()); if (level > 1) { tfLevel.setText("" + (level - 1)); } } catch (NumberFormatException e) { } requestFocus(); } }); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent ce) { plTipBlock.adjust(); } }); timer = new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { DateFormat format = new SimpleDateFormat("Time:HH:mm:ss"); //The system obtains time Date date = new Date(); tfTime.setText(format.format(date)); tfScore.setText("" + game.getScore()); int ScoreForLevelUpdate = //Judge whether the current score can be upgraded to game.getScoreForLevelUpdate(); if (ScoreForLevelUpdate >= RussiaBlocksGame.PER_LEVEL_SCORE && ScoreForLevelUpdate > 0) { game.levelUpdate(); } } }); timer.start(); } /** * Set the style of the pre-display window* * @param style int, corresponding to the 28 values in STYLES of the ErsBlock class*/ public void setTipStyle(int style) { plTipBlock.setStyle(style); } /** * Get the game level set by the user. * * @return int , difficulty level, 1-EsBlocksGame.MAX_LEVEL */ public int getLevel() { int level = 0; try { level = Integer.parseInt(tfLevel.getText()); } catch (NumberFormatException e) { } return level; } /** * Let users modify the game difficulty level. * * @param level Modified game difficulty level*/ public void setLevel(int level) { if (level > 0 && level < 11) { tfLevel.setText("" + level); } } /** * Set the status of the "Start" button. */ public void setPlayButtonEnable(boolean enable) { btPlay.setEnabled(enable); } public void setPauseButtonEnable(boolean enable) { btPause.setEnabled(enable); } public void setPauseButtonLabel(boolean pause) { btPause.setText(pause ? "Pause" : "Continue"); } public void setStopButtonEnable(boolean enable) { btStop.setEnabled(enable); } public void setTurnLevelUpButtonEnable(boolean enable) { btTurnLevelUp.setEnabled(enable); } public void setTurnLevelDownButtonEnable(boolean enable) { btTurnLevelDown.setEnabled(enable); } /** * Reset Control Panel*/ public void reset() { tfScore.setText(" 0"); plTipBlock.setStyle(0); } /** * Recalculate the size of the small box in boxes[][] in TipPanel*/ public void adjust() { plTipBlock.adjust(); } /** * Implementation details class of the pre-display window*/ public class TipPanel extends JPanel { //TipPanel is used to display the shape of the next block to appear private static final long serialVersionUID = 5160553671436997616L; private Color backColor = Color.darkGray, frontColor = Color.WHITE; private ErsBox[][] boxes = new ErsBox[EsBlock.BOXES_ROWS][EsBlock.BOXES_COLS]; private int style, boxWidth, boxHeight; private boolean isTiled = false; /** * Pre-display window class constructor*/ public TipPanel() { for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { boxes[i][j] = new ErsBox(false); } } } /** * Set the block style of the pre-display window* * @param style int, corresponding to 28 values in STYLES of the ErsBlock class*/ public void setStyle(int style) { this.style = style; repaint(); } /** * Overwrite the functions of the JComponent class and draw components. * * @param g Graphics device environment*/ @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (!isTiled) { adjust(); } int key = 0x8000; for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { Color color = ((key & style) != 0 ? frontColor : backColor); g.setColor(color); g.fill3DRect(j * boxWidth, i * boxHeight, boxWidth, boxHeight, true); key >>= 1; } } } /** * g Automatically adjust the size of the square according to the size of the window*/ public void adjust() { boxWidth = getSize().width / ErsBlock.BOXES_COLS; boxHeight = getSize().height / ErsBlock.BOXES_ROWS; isTiled = true; } } }ErsBox.java
package RussiaBlocksGame; import java.awt.Dimension; /** * The square class is the basic element that makes up the block, and uses its own color to represent the appearance of the block*/ public class ErsBox implements Cloneable { private boolean isColor; private Dimension size = new Dimension(); /** * The constructor of the square class, * * @param isColor Does the foreground color be used to color this square true foreground color, false Use background color*/ public ErsBox(boolean isColor) { this.isColor = isColor; } /** * Is this square expressed in the foreground color* * @return boolean , true expressed in the foreground color, false expressed in the background color*/ public boolean isColorBox() { return isColor; } /** * Set the color of the square, * * @param isColor boolean , true expressed in the foreground color, false expressed in the background color*/ public void setColor(boolean isColor) { this.isColor = isColor; } /** * Get the size of this square* * @return Dimension , the size of the square*/ public Dimension getSize() { return size; } /** * Set the size of the square, * * @param size Dimension , the size of the square*/ public void setSize(Dimension size) { this.size = size; } /** * Overwrite the Object's Object clone(), implement clone* * @return Object, clone result*/ @Override public Object clone() { Object clone = null; try { clone = super.clone(); } catch (Exception ex) { ex.printStackTrace(); } return clone; } }ErsBlock.java
package RussiaBlocksGame; /** * Block class, inherited from Thread class (Thread) consists of 4 × 4 blocks (ErsBox), controlling the movement, fall, deformation of the block, etc. */ class ErsBlock extends Thread { /** * The number of rows occupied by a block is 4 rows*/ public final static int BOXES_ROWS = 4; /** * The number of columns occupied by a block is 4 columns*/ public final static int BOXES_COLS = 4; /** * The factor that smoothes the upgrade changes to avoid nearly double the speed difference between the last stages*/ public final static int LEVEL_FLATNESS_GENE = 3; /** * What is the difference between the two similar levels of the block for each row (milliseconds) */ public final static int BETWEEN_LEVELS_DEGRESS_TIME = 50; /** * The number of styles of blocks is 7 */ public final static int BLOCK_KIND_NUMBER = 7; /** * The inversion state type of blocks of each style is 4 */ public final static int BLOCK_STATUS_NUMBER = 4; /** * 28 states corresponding to 7 models respectively*/ public final static int[][] STYLES = { //Total 28 states {0x0f00, 0x4444, 0x0f00, 0x4444}, //Four states of long stripes {0x04e0, 0x0464, 0x00e4, 0x04c4}, //Four states of T type {0x4620, 0x6c00, 0x4620, 0x6c00}, //Four states of reverse Z type {0x2640, 0xc600, 0x2640, 0xc600}, //Four states of Z type {0x6220, 0x1700, 0x2230, 0x0740}, //Four states of 7 type {0x6440, 0x0e20, 0x44c0, 0x8e00}, //Four states of inverse 7 type{0x0660, 0x0660, 0x0660, 0x0660}, //Four states of block}; private GameCanvas canvas; private ErsBox[][] boxes = new ErsBox[BOXES_ROWS][BOXES_COLS]; private int style, y, x, level; private boolean pausing = false, moving = true; /** * Constructor, generates a specific block* * @param style The style of the block, corresponding to one of the 28 values of STYLES* @param y Start position, coordinate line in the upper left corner in canvas* @param x Start position, coordinate column in the upper left corner in canvas* @param level Game level, control the falling speed of the block* @param canvas Artboard*/ public ErsBlock(int style, int y, int x, int level, GameCanvas canvas) { this.style = style; this.y = y; this.x = x; this.level = level; this.canvas = canvas; int key = 0x8000; for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { boolean isColor = ((style & key) != 0); boxes[i][j] = new ErsBox(isColor); key >>= 1; } } display(); } /** * The run() function of the thread class covers and drops the block until the block cannot fall again*/ @Override public void run() { while (moving) { try { sleep(BETWEEN_LEVELS_DEGRESS_TIME * (RussiaBlocksGame.MAX_LEVEL - level + LEVEL_FLATNESS_GENE)); } catch (InterruptedException ie) { ie.printStackTrace(); } //The subsequent moving means that the moving has not been changed during the 100 milliseconds of waiting if (!pausing) { moving = (moveTo(y + 1, x) && moving); } } } } /** * The block moves one grid to the left */ public void moveLeft() { moveTo(y, x - 1); } /** * The block moves one grid to the right */ public void moveRight() { moveTo(y, x + 1); } /** * The block moves one grid to the right */ public void moveDown() { moveTo(y + 1, x); } /** * Block variant*/ public void turnNext() { for (int i = 0; i < BLOCK_KIND_NUMBER; i++) { for (int j = 0; j < BLOCK_STATUS_NUMBER; j++) { if (STYLES[i][j] == style) { int newStyle = STYLES[i][(j + 1) % BLOCK_STATUS_NUMBER]; turnTo(newStyle); return; } } } } public void startMove() { pausing = false; moving = true; } /** * Pause the whereabouts of the block, corresponding to the game pause*/ public void pauseMove() { pausing = true; // moving = false; } /** * Continue the whereabouts of the block, corresponding to the game continue*/ public void resumeMove() { pausing = false; moving = true; } /** * Stop the whereabouts of the block, corresponding to the game pause*/ public void stopMove() { pausing = false; moving = false; } /** * Remove the current block from the corresponding position of the canvas and will not be reflected until the next time you repaint the canvas*/ private void erase() { for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { if (boxes[i][j].isColorBox()) { ErsBox box = canvas.getBox(i + y, j + x); if (box == null) { continue; } box.setColor(false); } } } } } /** * Let the current block be placed in the corresponding position of the canvas, and you have to wait until the next time you repaint the canvas*/ private void display() { for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { if (boxes[i][j].isColorBox()) { ErsBox box = canvas.getBox(i + y, j + x); if (box == null) { continue; } box.setColor(true); } } } } } /** * Can the current block be moved to the location specified by newRow/newCol* * @param newRow int, the destination line* @param newCol int,目的地所在列* @return boolean,true-能移动,false-不能移动*/ public boolean isMoveAble(int newRow, int newCol) { erase(); for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { if (boxes[i][j].isColorBox()) { ErsBox box = canvas.getBox(i + newRow, j + newCol); if (box == null || (box.isColorBox())) { display(); return false; } } } } display(); return true; } /** * 将当前块移动到newRow/newCol 所指定的位置* * @param newRow int,目的地所在行* @param newCol int,目的地所在列* @return boolean,true-移动成功,false-移动失败*/ private synchronized boolean moveTo(int newRow, int newCol) { if (!isMoveAble(newRow, newCol) || !moving) { return false; } erase(); y = newRow; x = newCol; display(); canvas.repaint(); return true; } /** * 当前块能否变成newStyle所指定的块样式,主要是考虑边界以及被其他块挡住,不能移动的情况* * @param newSytle int,希望改变的块样式,对应STYLES的28个值中的一个* @return boolean,true-能改变,false-不能改变*/ private boolean isTurnAble(int newStyle) { int key = 0x8000; erase(); for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { if ((newStyle & key) != 0) { ErsBox box = canvas.getBox(i + y, j + x); if (box == null || (box.isColorBox())) { display(); return false; } } key >>= 1; } } display(); return true; } /** * 将当前块变成newStyle所指定的块样式* * @param newStyle int,希望改变的块样式,对应STYLES的28个值中的一个* @return true-改变成功,false-改变失败*/ private boolean turnTo(int newStyle) { if (!isTurnAble(newStyle) || !moving) { return false; } erase(); int key = 0x8000; for (int i = 0; i < boxes.length; i++) { for (int j = 0; j < boxes[i].length; j++) { boolean isColor = ((newStyle & key) != 0); boxes[i][j].setColor(isColor); key >>= 1; } } style = newStyle; display(); canvas.repaint(); return true; } }Main.java
package RussiaBlocksGame; /** * 程序入口函数* * @param args String[],附带的命令行参数*/ public class Main { public static void main(String[] args) { new RussiaBlocksGame("俄罗斯方块:杨宇杰"); } } The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.