📄 jtetris.java
字号:
case DROP: newY = board.dropHeight(newPiece, newX); // trick: avoid the case where the drop would cause // the piece to appear to move up if (newY > currentY) { newY = currentY; } break; default: throw new RuntimeException("Bad verb"); } } public static final int ROTATE = 0; public static final int LEFT = 1; public static final int RIGHT = 2; public static final int DROP = 3; public static final int DOWN = 4; /** Called to change the position of the current piece. Each key press call this once with the verbs LEFT RIGHT ROTATE DROP for the user moves, and the timer calls it with the verb DOWN to move the piece down one square. Before this is called, the piece is at some location in the board. This advances the piece to be at its next location. Overriden by the brain when it plays. */ public void tick(int verb) { if (!gameOn) return; if (currentPiece != null) { board.undo(); // remove the piece from its old position } // Sets the newXXX ivars computeNewPosition(verb); // try out the new position (rolls back if it doesn't work) int result = setCurrent(newPiece, newX, newY); // if row clearing is going to happen, draw the // whole board so the green row shows up if (result == Board.PLACE_ROW_FILLED) repaint(); boolean failed = (result >= Board.PLACE_OUT_BOUNDS); // if it didn't work, put it back the way it was if (failed) { if (currentPiece != null) board.place(currentPiece, currentX, currentY); } /* How to detect when a piece has landed: if this move hits something on its DOWN verb, and the previous verb was also DOWN (i.e. the player was not still moving it), then the previous position must be the correct "landed" position, so we're done with the falling of this piece. */ if (failed && verb==DOWN && !moved) { // it's landed if (board.clearRows()) { repaint(); // repaint to show the result of the row clearing } // if the board is too tall, we've lost if (board.getMaxHeight() > board.getHeight() - TOP_SPACE) { stopGame(); } // Otherwise add a new piece and keep playing else { addNewPiece(); } } // Note if the player made a successful non-DOWN move -- // used to detect if the piece has landed on the next tick() moved = (!failed && verb!=DOWN); } /** Given a piece and a position for the piece, generates a repaint for the rectangle that just encloses the piece. */ public void repaintPiece(Piece piece, int x, int y) { if (DRAW_OPTIMIZE) { int px = xPixel(x); int py = yPixel(y + piece.getHeight() - 1); int pwidth = xPixel(x+piece.getWidth()) - px; int pheight = yPixel(y-1) - py; repaint(px, py, pwidth, pheight); } else { repaint(); } } /* Pixel helpers. These centralize the translation of (x,y) coords that refer to blocks in the board to (x,y) coords that count pixels. Centralizing these computations here is the only prayer that repaintPiece() and paintComponent() will be consistent. The +1's and -2's are to account for the 1 pixel rect around the perimeter. */ // width in pixels of a block private final float dX() { return( ((float)(getWidth()-2)) / board.getWidth() ); } // height in pixels of a block private final float dY() { return( ((float)(getHeight()-2)) / board.getHeight() ); } // the x pixel coord of the left side of a block private final int xPixel(int x) { return(Math.round(1 + (x * dX()))); } // the y pixel coord of the top of a block private final int yPixel(int y) { return(Math.round(getHeight() -1 - (y+1)*dY())); } /** Draws the current board with a 1 pixel border around the whole thing. Uses the pixel helpers above to map board coords to pixel coords. Draws rows that are filled all the way across in green. */ public void paintComponent(Graphics g) { // Draw a rect around the whole thing g.drawRect(0, 0, getWidth()-1, getHeight()-1); // Draw the line separating the top int spacerY = yPixel(board.getHeight() - TOP_SPACE - 1); g.drawLine(0, spacerY, getWidth()-1, spacerY); // check if we are drawing with clipping //Shape shape = g.getClip(); Rectangle clip = null; if (DRAW_OPTIMIZE) { clip = g.getClipBounds(); } // Factor a few things out to help the optimizer final int dx = Math.round(dX()-2); final int dy = Math.round(dY()-2); final int bWidth = board.getWidth(); final int bHeight = board.getHeight(); int x, y; // Loop through and draw all the blocks // left-right, bottom-top for (x=0; x<bWidth; x++) { int left = xPixel(x); // the left pixel // right pixel (useful for clip optimization) int right = xPixel(x+1) -1; // skip this x if it is outside the clip rect if (DRAW_OPTIMIZE && clip!=null) { if ((right<clip.x) || (left>=(clip.x+clip.width))) continue; } // draw from 0 up to the col height final int yHeight = board.getColumnHeight(x); for (y=0; y<yHeight; y++) { if (board.getGrid(x, y)) { final boolean filled = (board.getRowWidth(y)==bWidth); if (filled) g.setColor(Color.green); g.fillRect(left+1, yPixel(y)+1, dx, dy); // +1 to leave a white border if (filled) g.setColor(Color.black); } } } } /** Updates the timer to reflect the current setting of the speed slider. */ public void updateTimer() { double value = ((double)speed.getValue())/speed.getMaximum(); timer.setDelay((int)(DELAY - value*DELAY)); } /** Creates the panel of UI controls. This code is very repetitive -- the GUI/XML extensions in Java 1.4 should make this sort of ugly code less necessary. */ public java.awt.Container createControlPanel() { java.awt.Container panel = Box.createVerticalBox(); // COUNT countLabel = new JLabel("0"); panel.add(countLabel); // TIME timeLabel = new JLabel(" "); panel.add(timeLabel); panel.add(Box.createVerticalStrut(12)); // START button startButton = new JButton("Start"); panel.add(startButton); startButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { startGame(); } }); // STOP button stopButton = new JButton("Stop"); panel.add(stopButton); stopButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { stopGame(); } }); enableButtons(); JPanel row = new JPanel(); // SPEED slider panel.add(Box.createVerticalStrut(12)); row.add(new JLabel("Speed:")); speed = new JSlider(0, 200, 75); // min, max, current speed.setPreferredSize(new Dimension(100,15)); if (testMode) speed.setValue(200); // max for test mode updateTimer(); row.add(speed); panel.add(row); speed.addChangeListener( new ChangeListener() { // when the slider changes, sync the timer to its value public void stateChanged(ChangeEvent e) { updateTimer(); } }); return(panel); } /** Creates a Window, installs the JTetris or JBrainTetris, checks the testMode state, install the controls in the WEST. */ public static void main(String[] args) { JFrame frame = new JFrame("Tetris 2002"); JComponent container = (JComponent)frame.getContentPane(); container.setLayout(new BorderLayout()); // Set the metal look and feel try { UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() ); } catch (Exception ignored) {} // Could create a JTetris or JBrainTetris here final int pixels = 16; JTetris tetris = new JBrainTetris(WIDTH*pixels+2, (HEIGHT+TOP_SPACE)*pixels+2); //JTetris tetris = new JTetris(WIDTH*pixels+2, (HEIGHT+TOP_SPACE)*pixels+2); container.add(tetris, BorderLayout.CENTER); if (args.length != 0 && args[0].equals("test")) { tetris.testMode = true; } Container panel = tetris.createControlPanel(); // Add the quit button last so it's at the bottom panel.add(Box.createVerticalStrut(12)); JButton quit = new JButton("Quit"); panel.add(quit); quit.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); container.add(panel, BorderLayout.EAST); frame.pack(); frame.setVisible(true); // Quit on window close frame.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } } ); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -