I am creating a tetris clone as a personal project to help me get better at drawing images, moving them, and to learn collision detection.
Everything is going fine but I am stumped at the logic behind getting the program to add a new tetrimino shape to the frame when the first has stopped moving. So far I use a random number generator to create a tetrimino at random, and can add that to the frame. I just can't figure out how to loop it so that once that shape stops moving it adds another shape at the top of the screen.
This is a pre alpha build and in its current implementation I have not yet added the collision detection, any scoring, backgrounds, ability to rotate shapes, etc. I just can't get past this logic roadblock. Please help!
Some code:
public class tetrisGame extends JFrame
{
//Frame dimensions
private static final int FRAME_WIDTH = 600;
private static final int FRAME_HEIGHT = 600;
private final int MAX_VALUE = 7; //RNG MAX VALUE
private final int MIN_VALUE = 1; //RNG MIN VALUE
private static int dy = 10;
private static int dx = 0;
private JLabel welcomeLabel, imageLabel, blankLabel, blankLabel2, creditsLabel1, creditsLabel2, creditsLabel3;
private JButton startButton, creditsButton, exitButton, returnButton, leftButton, rightButton;
private Shapes component; //Tetrimino Shape
private JPanel totalGUI, buttonPanel, startPanel, creditsPanel, gamePanel, movePanel;
public tetrisGame()
{
createComponents();
setSize(FRAME_WIDTH, FRAME_HEIGHT);
setTitle("Tetris");
}
//Moves tetrimino's down using a timer
class TimerListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
component.moveRectangleBy(dx,dy);
}
}
//Moves the tetrimino to the right
class moveRightListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
dy = 0;
component.moveRectangleBy(dx+10,dy);
dy = 10;
}
}
//Moves the tetrimino to the left.
class moveLeftListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
dy = 0;
component.moveRectangleBy(dx-10,dy);
dy = 10;
}
}
//Executed when a new game is started. The heart of the program.
class newGameListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
int randomNum = createRNG(MAX_VALUE, MIN_VALUE);
if (randomNum == 1)
{
component = new SquareShape();
}
else if (randomNum == 2)
{
component = new RectangleShape();
}
else if (randomNum == 3)
{
component = new JShape();
}
else if (randomNum == 4)
{
component = new SShape();
}
else if (randomNum == 5)
{
component = new TShape();
}
else if (randomNum == 6)
{
component = new LShape();
}
else
{
component = new ZShape();
}
//Creates and starts timer that moves shapes
int delay = 1000;
ActionListener timerListener = new TimerListener();
javax.swing.Timer t = new javax.swing.Timer(delay, timerListener);
t.start();
remove(totalGUI);
add(component, BorderLayout.CENTER);
add(movePanel, BorderLayout.SOUTH);
repaint();
revalidate();
}
}
//Adds components of the credit screen when button is pressed
class creditsListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
totalGUI.remove(startPanel);
totalGUI.add(creditsPanel);
repaint();
revalidate();
}
}
//Exits the program
class exitListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
//returns to the main menu screen.
class returnListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
totalGUI.remove(creditsPanel);
totalGUI.add(startPanel);
repaint();
}
}
//Creates all components of the GUI
private void createComponents()
{
//Imports Tetris image
try
{
String path = "http://static3.gamespot.com/uploads/screen_kubrick/1179/11799911/2550560-tetris.jpg";
URL url = new URL(path);
BufferedImage image = ImageIO.read(url);
Image newImage = image.getScaledInstance(300,120,java.awt.Image.SCALE_SMOOTH);
imageLabel = new JLabel(new ImageIcon(newImage));
} catch(Exception e){}
//Creates welcome prompt and new game buttons
welcomeLabel = new JLabel(" Welcome to Tetris!");
Font boldFont = welcomeLabel.getFont();
welcomeLabel.setFont(boldFont.deriveFont(boldFont.getStyle() ^ Font.BOLD));
welcomeLabel.setForeground(Color.orange);
blankLabel = new JLabel("");
blankLabel2 = new JLabel("");
startButton = new JButton("New Game");
creditsButton = new JButton("Credits");
exitButton = new JButton("Exit");
//Adds action listeners to new game buttons
ActionListener newGameListener = new newGameListener();
startButton.addActionListener(newGameListener);
ActionListener creditsListener = new creditsListener();
creditsButton.addActionListener(creditsListener);
ActionListener exitListener = new exitListener();
exitButton.addActionListener(exitListener);
//Adds new game buttons to panel
buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(6,1));
buttonPanel.setBackground(Color.black);
buttonPanel.add(blankLabel);
buttonPanel.add(blankLabel2);
buttonPanel.add(welcomeLabel);
buttonPanel.add(startButton);
buttonPanel.add(creditsButton);
buttonPanel.add(exitButton);
//Buttons that move the tetrimino's
leftButton = new JButton("<--");
ActionListener leftListener = new moveLeftListener();
leftButton.addActionListener(leftListener);
rightButton = new JButton("-->");
ActionListener rightListener = new moveRightListener();
rightButton.addActionListener(rightListener);
//Panel that contains movement buttons
movePanel = new JPanel();
movePanel.add(leftButton);
movePanel.add(rightButton);
//Continue to add elements to panel
startPanel = new JPanel();
startPanel.setLayout(new BorderLayout());
startPanel.add(imageLabel, BorderLayout.NORTH);
startPanel.add(buttonPanel, BorderLayout.CENTER);
//Create elements of credits screen
creditsLabel1 = new JLabel("The Tetris logo, block shapes, and dimensions are registered trademarks of The Tetris Company.");
creditsLabel1.setFont(boldFont.deriveFont(boldFont.getStyle() ^ Font.BOLD));
creditsLabel1.setForeground(Color.orange);
creditsLabel2 = new JLabel(" This product is an academic work intended for individual use only.");
creditsLabel2.setFont(boldFont.deriveFont(boldFont.getStyle() ^ Font.BOLD));
creditsLabel2.setForeground(Color.orange);
creditsLabel3 = new JLabel(" All programming written in the Java language by NAME REMOVED.");
creditsLabel3.setFont(boldFont.deriveFont(boldFont.getStyle() ^ Font.BOLD));
creditsLabel3.setForeground(Color.orange);
returnButton = new JButton("Return");
ActionListener returnListener = new returnListener();
returnButton.addActionListener(returnListener);
creditsPanel = new JPanel();
creditsPanel.setLayout(new GridLayout(5,1));
creditsPanel.setBackground(Color.black);
creditsPanel.add(creditsLabel1);
creditsPanel.add(creditsLabel2);
creditsPanel.add(blankLabel);
creditsPanel.add(creditsLabel3);
creditsPanel.add(returnButton);
//Initial game panel
totalGUI = new JPanel();
totalGUI.add(startPanel);
totalGUI.setBackground(Color.black);
add(totalGUI, BorderLayout.CENTER);
}
//generates a random number.
private int createRNG(int MAX_VALUE, int MIN_VALUE)
{
Random rand = new Random();
int randomNum = rand.nextInt(MAX_VALUE - MIN_VALUE + 1) + MIN_VALUE;
return randomNum;
}
}
And here is the code of one of the shapes classes in case you need to reference that:
public class SquareShape extends Shapes
{
private static final int RECTANGLE_WIDTH = 40;
private static final int RECTANGLE_HEIGHT = 40;
private int xLeft;
private int yTop;
boolean stopped = false;
public SquareShape()
{
xLeft = 280;
yTop = 0;
}
public void paintComponent(Graphics g)
{
//draws 1 large square
g.setColor(Color.cyan);
g.fillRect(xLeft,yTop,RECTANGLE_WIDTH,RECTANGLE_HEIGHT);
//Divides the square into parts
g.setColor(Color.black);
g.drawLine(xLeft,yTop,xLeft+40,yTop);
g.drawLine(xLeft,yTop,xLeft,yTop+40);
g.drawLine(xLeft,yTop+40,xLeft+40,yTop+40);
g.drawLine(xLeft+40,yTop+40,xLeft+40,yTop);
g.drawLine(xLeft,yTop+20,xLeft+40,yTop+20);
g.drawLine(xLeft+20,yTop,xLeft+20,yTop+40);
}
public void moveRectangleBy(int dx, int dy)
{
if (yTop < 450)
{
xLeft += dx;
yTop += dy;
if (xLeft < 0)
{
xLeft = 0;
}
if (xLeft > 500)
{
xLeft = 500;
}
}
repaint();
}
}
Thanks in advance for any help. I'm confident I can implement the rest of the program once I get past this issue where I just can't seem to figure out how to get the shapes to keep coming down.