I'm working on a tetris game and I've been trying to get the keystroke actions to work; i've done it before in other applications but for some reason my jpanel is not calling any of the key methods when i hit a key. I don't know if this is a problem with the focus, whether I left out some needed code, or something else altogether... please help!
The only code i need help on is PlayPanel (and possibly MainFrame) but I provided all my code for those who wish to compile and run it
EDIT: I have also been looking at KeyBindings, does anyone know if that would help in a situation like this?
MainFrame:
package Tetris; import java.awt.*; import javax.swing.*; @SuppressWarnings({ "serial" }) public class MainFrame extends JFrame { public MainFrame() { super("Tetris"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); JPanel optionInfo = new JPanel(); optionInfo.setLayout(new BoxLayout(optionInfo, BoxLayout.Y_AXIS)); optionInfo.setBackground(Color.BLACK); PlayPanel play = new PlayPanel(); InfoPanel info = new InfoPanel(play); OptionPanel options = new OptionPanel(info, play); optionInfo.add(options); optionInfo.add(info); add(play, BorderLayout.CENTER); add(optionInfo, BorderLayout.EAST); pack(); setVisible(true); } public static void main(String[] args) { new MainFrame(); } }
PlayPanel:
package Tetris; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.Border; import java.util.TimerTask; import java.util.Timer; @SuppressWarnings({ "serial", "unused" }) public class PlayPanel extends JPanel implements KeyListener { private Tetromino tet; private Timer dropTimer; private Timer colorTimer; private JButton[][] buttonAt; private boolean[][] isDeadButtonAt; private int linesCleared = 0; private int dropSpeed = 1000; public PlayPanel() { super(); setBackground(Color.BLACK); GridLayout grid = new GridLayout(18, 10); //rows, columns setLayout(grid); buttonAt = new JButton[10][18]; isDeadButtonAt = new boolean[10][18]; for(int y = 0; y < 18; y++) { for(int x = 0; x < 10; x++) { buttonAt[x][y] = new JButton(); buttonAt[x][y].setEnabled(false); buttonAt[x][y].setPreferredSize(new Dimension(30, 30)); buttonAt[x][y].setOpaque(false); //false makes background visible add(buttonAt[x][y]); } } setFocusable(true); addKeyListener(this); setVisible(true); } public void newGame() { if(!(dropTimer == null)) { dropTimer.cancel(); } if(!(colorTimer == null)) { colorTimer.cancel(); } for(int x = 0; x < 10; x++) { for(int y = 0; y < 18; y++) { buttonAt[x][y].setBackground(null); buttonAt[x][y].setOpaque(false); isDeadButtonAt[x][y] = false; } } dropSpeed = 1000; dropTimer = new Timer(); dropTimer.schedule(new DropTimer(), dropSpeed, dropSpeed); colorTimer = new Timer(); colorTimer.schedule(new ColorTimer(), 0, 100); tet = new Tetromino(); } public void moveTet(boolean rotated) { int[] x = new int[4]; int lastX[] = new int[4]; int[] y = new int[4]; int lastY[] = new int[4]; int dropLevel = tet.getDropLevel(); int lastDropLevel = tet.getLastDropLevel(); int rotation = tet.getRotation(); int height = tet.getHeight(); int movedPos = tet.getMovedPos(); if(movedPos < 0 || movedPos > 9) { return; } for(int i = 0; i < 4; i++) { x[i] = tet.getPosX()[rotation][i] + movedPos; y[i] = tet.getPosY()[rotation][i] + dropLevel; lastX[i] = tet.getLastPosX()[i] + movedPos; lastY[i] = tet.getLastPosY()[i] + lastDropLevel; if(dropLevel + height > 18 || isDeadButtonAt[x[i]][y[i]]) { if(rotated == true) { return; } movedPos = 0; setDeadButtons(); tet = new Tetromino(); return; } } for(int i = 0; i < 4; i++) { buttonAt[lastX[i]][lastY[i]].setOpaque(false); buttonAt[lastX[i]][lastY[i]].setBackground(null); } for(int i = 0; i < 4; i++) { buttonAt[x[i]][y[i]].setOpaque(true); buttonAt[x[i]][y[i]].setBackground(tet.getNewColor()); } tet.setLastPos(tet.getPosX()[rotation], tet.getPosY()[rotation]); if(rotated == false) { tet.setLastDropLevel(dropLevel); tet.setDropLevel(dropLevel + 1); } } public void setDeadButtons() { for(int x = 0; x < 10; x++) { for(int y = 0; y < 18; y++) { if(buttonAt[x][y].isOpaque()) { isDeadButtonAt[x][y] = true; } else { isDeadButtonAt[x][y] = false; } } } } public void keyTyped(KeyEvent e) { } public void keyReleased(KeyEvent e) { } public void keyPressed(KeyEvent e) { System.out.println("KEY PRESSED"); switch(e.getKeyCode()) { case KeyEvent.VK_UP: System.out.println("UP PRESSED"); tet.rotate(); moveTet(true); break; case KeyEvent.VK_LEFT: System.out.println("LEFT PRESSED"); tet.setMovedPos(tet.getMovedPos() - 1); moveTet(false); break; } } public void pause() { dropTimer.cancel(); //colorTimer.cancel(); } public void resume() { dropTimer = new Timer(); //colorTimer = new Timer(); dropTimer.schedule(new DropTimer(), dropSpeed, dropSpeed); //colorTimer.schedule(new ColorTimer(), 0, 100); } public class DropTimer extends TimerTask implements Runnable { public void run() { moveTet(false); } } public class ColorTimer extends TimerTask implements Runnable { public void run() { for(int x = 0; x < 10; x++) { for(int y = 0; y < 18; y++) { if(buttonAt[x][y].isOpaque()) { buttonAt[x][y].setBackground(tet.getNewColor()); } } } } } public int getLinesCleared() { return linesCleared; } public void resetLinesCleared() { linesCleared = 0; } public void updateLinesCleared() { } }
Tetromino:
package Tetris; import java.util.Random; import java.awt.Color; public class Tetromino { private int type; private Color tetColor; private int[] lastPosX = new int[]{0, 0, 0, 0}; private int[] lastPosY = new int[]{0, 0, 0, 0}; private int[][] posX = new int[4][4]; private int[][] posY = new int[4][4]; private int height; private int movedPos = 5; //middle of grid layout private int dropLevel = 0; private int lastDropLevel = 0; private int rotation = 0; private int lastRotation = -1; public Tetromino() { Random genNum = new Random(); type = genNum.nextInt(7); switch(type) { case 0: buildIBlock(); break; case 1: buildOBlock(); break; case 2: buildTBlock(); break; case 3: buildLBlock(); break; case 4: buildJBlock(); break; case 5: buildSBlock(); break; case 6: buildZBlock(); break; } if(type == 0) { height = 1; } else { height = 2; } tetColor = getNewColor(); } public Color getNewColor() { Color newColor = null; Random genNum = new Random(); int colorType = genNum.nextInt(7); switch(colorType) { case 0: newColor = Color.BLUE; break; case 1: newColor = Color.CYAN; break; case 2: newColor = Color.GREEN; break; case 3: newColor = Color.MAGENTA; break; case 4: newColor = Color.ORANGE; break; case 5: newColor = Color.RED; break; case 6: newColor = Color.WHITE; break; } if(newColor.equals(tetColor)) { getNewColor(); } return newColor; } public void buildIBlock() { for(int i = 0; i < 4; i++) { if(i%2 == 0) { posX[i] = new int[] {-2, -1, 0, 1}; posY[i] = new int[] {0, 0, 0, 0}; } else { posX[i] = new int[] {0, 0, 0, 0}; posY[i] = new int[] {0, 1, 2, 3}; } } } public void buildOBlock() { for(int i = 0; i < 4; i++) { posX[i] = new int[] {-1, 0, -1, 0}; posY[i] = new int[] {0, 0, 1, 1}; } } public void buildTBlock() { posX[0] = new int[] {-1, 0, 1, 0}; posY[0] = new int[] {0, 0, 0, 1}; posX[1] = new int[] {0, -1, 0, 0}; posY[1] = new int[] {0, 1, 1, 2}; posX[2] = new int[] {0, -1, 0, 1}; posY[2] = new int[] {0, 1, 1, 1}; posX[3] = new int[] {-1, -1, 0, -1}; posY[3] = new int[] {0, 1, 1, 2}; } public void buildLBlock() { posX[0] = new int[] {-1, 0, 1, -1}; posY[0] = new int[] {0, 0, 0, 1}; posX[1] = new int[] {-1, 0, 0, 0}; posY[1] = new int[] {0, 0, 1, 2}; posX[2] = new int[] {1, -1, 0, 1}; posY[2] = new int[] {0, 1, 1, 1}; posX[3] = new int[] {-1, -1, -1, 0}; posY[3] = new int[] {0, 1, 2, 2}; } public void buildJBlock() { posX[0] = new int[] {-1, 0, 1, 1}; posY[0] = new int[] {0, 0, 0, 1}; posX[1] = new int[] {0, 0, -1, 0}; posY[1] = new int[] {0, 1, 2, 2}; posX[2] = new int[] {-1, -1, 0, 1}; posY[2] = new int[] {0, 1, 1, 1}; posX[3] = new int[] {-1, 0, -1, -1}; posY[3] = new int[] {0, 0, 1, 2}; } public void buildSBlock() { for(int i = 0; i < 4; i++) { if(i%2 == 0) { posX[i] = new int[] {0, 1, -1, 0}; posY[i] = new int[] {0, 0, 1, 1}; } else { posX[i] = new int[] {-1, -1, 0, 0}; posY[i] = new int[] {0, 1, 1, 2}; } } } public void buildZBlock() { for(int i = 0; i < 4; i++) { if(i%2 == 0) { posX[i] = new int[] {-1, 0, 0, 1}; posY[i] = new int[] {0, 0, 1, 1}; } else { posX[i] = new int[] {0, 0, -1, -1}; posY[i] = new int[] {0, 1, 1, 2}; } } } public void rotate() { lastRotation = rotation; rotation = (rotation+1)%4; switch(type) { case 0: if(rotation % 2 == 0) { height = 1; } else { height = 4; } break; case 1: height = 2; break; default: if(rotation % 2 == 0) { height = 2; } else { height = 3; } break; } } public void setLastPos(int[] posX, int[] posY) { lastPosX = posX; lastPosY = posY; } public void resetLastPos() { lastPosX = new int[] {0,0,0,0}; lastPosY = new int[] {0,0,0,0}; lastDropLevel = 0; } public int getType() { return type; } public int[] getLastPosX() { return lastPosX; } public int[] getLastPosY() { return lastPosY; } public int[][] getPosX() { return posX; } public int[][] getPosY() { return posY; } public int getHeight() { return height; } public int getMovedPos() { return movedPos; } public int getDropLevel() { return dropLevel; } public int getLastDropLevel() { return lastDropLevel; } public int getLastRotation() { return lastRotation; } public int getRotation() { return rotation; } public Color getTetColor() { return tetColor; } public void setMovedPos(int m) { movedPos = m; } public void setDropLevel(int d) { dropLevel = d; } public void setLastDropLevel(int l) { lastDropLevel = l; } }
InfoPanel:
package Tetris; import java.awt.*; import javax.swing.*; @SuppressWarnings({ "serial" }) public class InfoPanel extends JPanel { private ScoreTracker scoreTracker; private PlayPanel playPanel; private JLabel score = new JLabel(); private JLabel level = new JLabel(); private JLabel linesCleared = new JLabel(); public InfoPanel(PlayPanel play) { playPanel = play; scoreTracker = new ScoreTracker(); setOpaque(false); setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); JLabel scoreLabel = new JLabel("SCORE"); JLabel levelLabel = new JLabel("LEVEL"); JLabel linesLabel = new JLabel("LINES"); JLabel clearedLabel = new JLabel("CLEARED"); scoreLabel.setForeground(Color.WHITE); levelLabel.setForeground(Color.WHITE); linesLabel.setForeground(Color.WHITE); clearedLabel.setForeground(Color.WHITE); score.setForeground(Color.WHITE); level.setForeground(Color.WHITE); linesCleared.setForeground(Color.WHITE); scoreLabel.setAlignmentX(Component.CENTER_ALIGNMENT); levelLabel.setAlignmentX(Component.CENTER_ALIGNMENT); linesLabel.setAlignmentX(Component.CENTER_ALIGNMENT); clearedLabel.setAlignmentX(Component.CENTER_ALIGNMENT); score.setAlignmentX(Component.CENTER_ALIGNMENT); level.setAlignmentX(Component.CENTER_ALIGNMENT); linesCleared.setAlignmentX(Component.CENTER_ALIGNMENT); add(scoreLabel); add(score); add(levelLabel); add(level); add(linesLabel); add(clearedLabel); add(linesCleared); setVisible(true); } public void paintComponent(Graphics g) { super.paintComponent(g); score.setText("" + scoreTracker.getScore()); level.setText("" + (1 + Integer.parseInt(score.getText())/1000)); linesCleared.setText("" + playPanel.getLinesCleared()); } public void updateInfo() { if(scoreTracker == null) scoreTracker = new ScoreTracker(); else scoreTracker.updateScore(); repaint(); } public void resetInfo() { scoreTracker.resetScore(); playPanel.resetLinesCleared(); repaint(); } }
OptionPanel:
package Tetris; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; @SuppressWarnings({ "serial", "unused" }) public class OptionPanel extends JPanel implements ActionListener { private InfoPanel infoPanel; private PlayPanel playPanel; private JButton newGame = new JButton("NEW GAME"); private JButton pause = new JButton("PAUSE"); private JButton quit = new JButton("QUIT"); private JButton addScore = new JButton("+100"); private Font f = new Font("SERIF", 0, 14); private boolean isPaused; public OptionPanel(InfoPanel info, PlayPanel play) { infoPanel = info; playPanel = play; setOpaque(false); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); newGame.addActionListener(this); pause.addActionListener(this); quit.addActionListener(this); addScore.addActionListener(this); newGame.setFont(f); newGame.setForeground(Color.GREEN); newGame.setBackground(Color.BLACK); newGame.setBorder(null); newGame.setAlignmentX(Component.CENTER_ALIGNMENT); pause.setFont(f); pause.setForeground(Color.ORANGE); pause.setBackground(Color.BLACK); pause.setBorder(null); pause.setAlignmentX(Component.CENTER_ALIGNMENT); quit.setFont(f); quit.setForeground(Color.RED); quit.setBackground(Color.BLACK); quit.setBorder(null); quit.setAlignmentX(Component.CENTER_ALIGNMENT); add(newGame); add(pause); add(quit); setVisible(true); } public void actionPerformed(ActionEvent event) { Object source = event.getSource(); if(source.equals(newGame)) { pause.setText("PAUSE"); infoPanel.resetInfo(); playPanel.newGame(); } if(source.equals(pause)) { if(pause.getText().equals("PAUSE")) { pause.setText("RESUME"); playPanel.pause(); } else { pause.setText("PAUSE"); playPanel.resume(); } } if(source.equals(quit)) { System.exit(0); } if(source.equals(addScore)) { infoPanel.updateInfo(); } } }
ScoreTracker (necessary to compile but I haven't fleshed out the methods yet)
package Tetris; public class ScoreTracker { private int score; public ScoreTracker() { score = 0; } public void updateScore() { score += 100; } public void resetScore() { score = 0; } public int getScore() { return score; } }