📄 game.java
字号:
/* * @(#)Game.java * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (c) 2003 Per Cederberg. All rights reserved. */package net.percederberg.tetris;import java.awt.Button;import java.awt.Component;import java.awt.Container;import java.awt.Dimension;import java.awt.Font;import java.awt.Graphics;import java.awt.GridBagConstraints;import java.awt.GridBagLayout;import java.awt.Insets;import java.awt.Label;import java.awt.Rectangle;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyAdapter;import java.awt.event.KeyEvent;/** * The Tetris game. This class controls all events in the game and * handles all the game logics. The game is started through user * interaction with the graphical game component provided by this * class. * * @version 1.2 * @author Per Cederberg, per@percederberg.net */public class Game extends Object { /** * The main square board. This board is used for the game itself. */ private SquareBoard board = null; /** * The preview square board. This board is used to display a * preview of the figures. */ private SquareBoard previewBoard = new SquareBoard(5, 5); /** * The figures used on both boards. All figures are reutilized in * order to avoid creating new objects while the game is running. * Special care has to be taken when the preview figure and the * current figure refers to the same object. */ private Figure[] figures = { new Figure(Figure.SQUARE_FIGURE), new Figure(Figure.LINE_FIGURE), new Figure(Figure.S_FIGURE), new Figure(Figure.Z_FIGURE), new Figure(Figure.RIGHT_ANGLE_FIGURE), new Figure(Figure.LEFT_ANGLE_FIGURE), new Figure(Figure.TRIANGLE_FIGURE) }; /** * The graphical game component. This component is created on the * first call to getComponent(). */ private GamePanel component = null; /** * The thread that runs the game. When this variable is set to * null, the game thread will terminate. */ private GameThread thread = null; /** * The game level. The level will be increased for every 20 lines * removed from the square board. */ private int level = 1; /** * The current score. The score is increased for every figure that * is possible to place on the main board. */ private int score = 0; /** * The current figure. The figure will be updated when */ private Figure figure = null; /** * The next figure. */ private Figure nextFigure = null; /** * The rotation of the next figure. */ private int nextRotation = 0; /** * The figure preview flag. If this flag is set, the figure * will be shown in the figure preview board. */ private boolean preview = true; /** * The move lock flag. If this flag is set, the current figure * cannot be moved. This flag is set when a figure is moved all * the way down, and reset when a new figure is displayed. */ private boolean moveLock = false; /** * Creates a new Tetris game. The square board will be given * the default size of 10x20. */ public Game() { this(10, 20); } /** * Creates a new Tetris game. The square board will be given * the specified size. * * @param width the width of the square board (in positions) * @param height the height of the square board (in positions) */ public Game(int width, int height) { board = new SquareBoard(width, height); board.setMessage("Press start"); thread = new GameThread(); } /** * Kills the game running thread and makes necessary clean-up. * After calling this method, no further methods in this class * should be called. Neither should the component returned * earlier be trusted upon. */ public void quit() { thread = null; } /** * Returns a new component that draws the game. * * @return the component that draws the game */ public Component getComponent() { if (component == null) { component = new GamePanel(); } return component; } /** * Handles a game start event. Both the main and preview square * boards will be reset, and all other game parameters will be * reset. Finally the game thread will be launched. */ private void handleStart() { // Reset score and figures level = 1; score = 0; figure = null; nextFigure = randomFigure(); nextFigure.rotateRandom(); nextRotation = nextFigure.getRotation(); // Reset components board.setMessage(null); board.clear(); previewBoard.clear(); handleLevelModification(); handleScoreModification(); component.button.setLabel("Pause"); // Start game thread thread.reset(); } /** * Handles a game over event. This will stop the game thread, * reset all figures and print a game over message. */ private void handleGameOver() { // Stop game thred thread.setPaused(true); // Reset figures if (figure != null) { figure.detach(); } figure = null; if (nextFigure != null) { nextFigure.detach(); } nextFigure = null; // Handle components board.setMessage("Game Over"); component.button.setLabel("Start"); } /** * Handles a game pause event. This will pause the game thread and * print a pause message on the game board. */ private void handlePause() { thread.setPaused(true); board.setMessage("Paused"); component.button.setLabel("Resume"); } /** * Handles a game resume event. This will resume the game thread * and remove any messages on the game board. */ private void handleResume() { board.setMessage(null); component.button.setLabel("Pause"); thread.setPaused(false); } /** * Handles a level modification event. This will modify the level * label and adjust the thread speed. */ private void handleLevelModification() { component.levelLabel.setText("Level: " + level); thread.adjustSpeed(); } /** * Handle a score modification event. This will modify the score * label. */ private void handleScoreModification() { component.scoreLabel.setText("Score: " + score); } /** * Handles a figure start event. This will move the next figure * to the current figure position, while also creating a new * preview figure. If the figure cannot be introduced onto the * game board, a game over event will be launched. */ private void handleFigureStart() { int rotation; // Move next figure to current figure = nextFigure; moveLock = false; rotation = nextRotation; nextFigure = randomFigure(); nextFigure.rotateRandom(); nextRotation = nextFigure.getRotation(); // Handle figure preview if (preview) { previewBoard.clear(); nextFigure.attach(previewBoard, true); nextFigure.detach(); } // Attach figure to game board figure.setRotation(rotation); if (!figure.attach(board, false)) { previewBoard.clear(); figure.attach(previewBoard, true); figure.detach(); handleGameOver(); } } /** * Handles a figure landed event. This will check that the figure * is completely visible, or a game over event will be launched. * After this control, any full lines will be removed. If no full * lines could be removed, a figure start event is launched * directly. */ private void handleFigureLanded() { // Check and detach figure if (figure.isAllVisible()) { score += 10; handleScoreModification(); } else { handleGameOver(); return; } figure.detach(); figure = null; // Check for full lines or create new figure if (board.hasFullLines()) { board.removeFullLines(); if (level < 9 && board.getRemovedLines() / 20 > level) { level = board.getRemovedLines() / 20; handleLevelModification(); } } else { handleFigureStart(); } } /** * Handles a timer event. This will normally move the figure down * one step, but when a figure has landed or isn't ready other * events will be launched. This method is synchronized to avoid * race conditions with other asynchronous events (keyboard and * mouse). */ private synchronized void handleTimer() { if (figure == null) { handleFigureStart(); } else if (figure.hasLanded()) { handleFigureLanded(); } else { figure.moveDown(); } } /** * Handles a button press event. This will launch different events * depending on the state of the game, as the button semantics * change as the game changes. This method is synchronized to * avoid race conditions with other asynchronous events (timer and * keyboard). */ private synchronized void handleButtonPressed() { if (nextFigure == null) { handleStart(); } else if (thread.isPaused()) { handleResume(); } else { handlePause(); } } /**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -