→ My Meta+IT/JAVA Source

자바 테트리스 게임(Tetris App in Java) 프로젝트

DigitalJobs 2022. 12. 6. 16:48

자바로 구현된 다양한 게임들이 많지만 아래의 소스 코드는 직접 구현된 코드들이라서 온라인 상에 다듬어진 코드와는 정리가 부족하다는 말씀 미리 드립니다. 7가지의 블럭(모양)을 구성하고 그 블럭들의 상태에따라 동작하는 이벤트 처리가 되는 방식입니다.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class MultiClient extends JFrame implements KeyListener {
 static int[][] state = new int[21][11]; 
 /*                                                                        
  * state = 0 : 빈 공간
  * state %2 == 1 : 멈춰져 있는 블럭
  * state %2 == 0 && state != 20 : 움직이고 있는 (떨어지고 있는) 블럭 
  * state = 999 : 맨 밑의 줄 (떨어지는 블럭이 멈추게 하기 위함.) 
  */
 boolean isThreadEnd = false;// 쓰레드 작동이 끝났는지 (블럭이 떨어질수 있는 곳까지 떨어졌는지)
 Blocks currentBlock; // 떨어지는 블럭의 정보
 static boolean isGameOver; // 게임이 끝났는지 안 끝났는지 
 Image tetris_defaultblock_1 = new ImageIcon("TetrisDefaultBlock1.jpg").getImage(); // 블럭 이미지 1
 Image tetris_defaultblock_2 = new ImageIcon("TetrisDefaultBlock2.jpg").getImage(); // 블럭 이미지 2
 Image tetris_defaultblock_3 = new ImageIcon("TetrisDefaultBlock3.jpg").getImage(); // 블럭 이미지 3
 Image tetris_defaultblock_4 = new ImageIcon("TetrisDefaultBlock4.jpg").getImage(); // 블럭 이미지 4
 Image tetris_defaultblock_5 = new ImageIcon("TetrisDefaultBlock5.jpg").getImage(); // 블럭 이미지 5
 Image tetris_defaultblock_6 = new ImageIcon("TetrisDefaultBlock6.jpg").getImage(); // 블럭 이미지 6
 Image tetris_defaultblock_7 = new ImageIcon("TetrisDefaultBlock7.jpg").getImage(); // 블럭 이미지 7
 Image tetris_BackGround = new ImageIcon("TetrisBackGround.jpg").getImage(); // 테트리스 배경
 
 private Image offScreenImageDrawed = null;
 private Graphics offScreenGraphicsDrawed = null;
 
 public static void main(String[] args) {
  new MultiClient();
 }
 
 

 public MultiClient() {
  super("Tetris");
  
  setSize(233, 485);
  addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {System.exit(0);}});
  addKeyListener(this);
  setResizable(false);
  setBackground(Color.WHITE);
  
  // 메뉴
  MenuBar mb = new MenuBar();
  Menu mOption = new Menu("기능");
  MenuItem mQuit = new MenuItem("종료");
  mQuit.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {System.exit(0);}});
  mOption.add(mQuit);
  mb.add(mOption);
  setMenuBar(mb);
  
  for(int i=0; i<21; i++)
   for(int j=0; j<10; j++) {
    if(i==20)
     state[20][j]=999;
    else
     state[i][j]=0;
   }
  
  setVisible(true);
  isThreadEnd = true;
  while(true) {
   if(isThreadEnd) {
    isThreadEnd = false;
    new Thread(new FallingBlockThread()).start();
   }
  }
 }
 
 public void paint(Graphics g) {
  final Dimension d = getSize();
  if(offScreenImageDrawed == null)
   offScreenImageDrawed = createImage(d.width, d.height);
  offScreenGraphicsDrawed = offScreenImageDrawed.getGraphics();
  offScreenGraphicsDrawed.setColor(Color.WHITE);
  offScreenGraphicsDrawed.fillRect(0, 0, d.width, d.height);
  
  renderOffScreen(offScreenImageDrawed.getGraphics());
  g.drawImage(offScreenImageDrawed, 0, 0, null);
  
 }
 public void renderOffScreen(final Graphics g) {
     g.setColor(Color.black);
     g.drawImage(tetris_BackGround, 0, 50, 230, 434, this);
     for(int i=0; i<20; i++)
        for(int j=0; j<10; j++) {
            if(state[i][j]==1 || state[i][j]==2)
            g.drawImage(tetris_defaultblock_1, 14+j*20, 67+i*20, 20, 20, this);
           if(state[i][j]==3 || state[i][j]==4)
            g.drawImage(tetris_defaultblock_2, 14+j*20, 67+i*20, 20, 20, this);
       if(state[i][j]==5 || state[i][j]==6)
        g.drawImage(tetris_defaultblock_3, 14+j*20, 67+i*20, 20, 20, this);
       if(state[i][j]==7 || state[i][j]==8)
        g.drawImage(tetris_defaultblock_4, 14+j*20, 67+i*20, 20, 20, this);
       if(state[i][j]==9 || state[i][j]==10)
        g.drawImage(tetris_defaultblock_5, 14+j*20, 67+i*20, 20, 20, this);
       if(state[i][j]==11 || state[i][j]==12)
        g.drawImage(tetris_defaultblock_6, 14+j*20, 67+i*20, 20, 20, this);
       if(state[i][j]==13 || state[i][j]==14)
        g.drawImage(tetris_defaultblock_7, 14+j*20, 67+i*20, 20, 20, this);
      }                          
 }
 public void removeLine() {
  ArrayList<Integer> v = new ArrayList<Integer>();
  for(int i=19; i>=0; i--) {
   int tmp = 0;
   for(int j=0; j<10; j++)
    if(state[i][j]%2==1) {
     tmp++;
    }
   if(tmp==10) {
    for(int j=0; j<10; j++)
     state[i][j]=0;
    v.add(i);
   }
  }
  for(int k=0; k<v.size(); k++) {
   for(int i=v.get(k); i>=0; i--)
    for(int j=0; j<10; j++)
     if(state[i+1][j]==0) {
      state[i+1][j] = state[i][j];
      state[i][j]=0;
     }
  }
 }
 public boolean isGameOver() {
  boolean result = false;
  for(int i=0; i<10; i++)
   if(state[0][i]%2==1)
    result = true;
  return result;
 }
 public static void setGameOver() {
  isGameOver = true;
  JFrame game_over = new JFrame();
  game_over.setSize(150, 100);
  game_over.setLayout(null);
  game_over.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {System.exit(0);}});
  
  JLabel l = new JLabel("Game Over!");

  l.setBounds(25, 40, 100, 10);
  game_over.add(l);
  
  game_over.setResizable(false);
  game_over.setVisible(true);
 }
 public Point getBlockPosition() {
  int temp_Xpos=100, temp_Ypos=100;
  for(int i=0; i<20; i++)
   for(int j=0; j<10; j++)
    if(state[i][j]!=0 && state[i][j]%2==0 && state[i][j]!=20 && state[i][j]!=20) {
     if(temp_Xpos>i)
      temp_Xpos=i;
     if(temp_Ypos>j)
      temp_Ypos=j;
    }
  return new Point(temp_Xpos, temp_Ypos);
 }
 public void set(boolean isRot) {
  Point tmp = getBlockPosition();
  int temp_Xpos = (int)tmp.getX();
  int temp_Ypos = (int)tmp.getY();
  int[][] block_Info = currentBlock.getBlockCodeInfo();
  if(isRot) {
   boolean isRotPoss = true;
   try {
   for(int i=0; i<4; i++)
    for(int j=0; j<4; j++) {
     if(block_Info[i][j]!=0 && state[temp_Xpos+i][temp_Ypos+j]%2==1)
      isRotPoss = false;
    }
   } catch (ArrayIndexOutOfBoundsException e) {return;}
   if(!isRotPoss)
    return;
   boolean tmp_Boolean = true;
   try {
    for(int i=0; i<4; i++)
     for(int j=0; j<4; j++) {
      if(!(state[temp_Xpos+i][temp_Ypos+j]==1 && block_Info[i][j]==0)) {}
     }
   } catch (ArrayIndexOutOfBoundsException e) {tmp_Boolean = false;} // 예외 발생 확인
   if(tmp_Boolean) {
    for(int i=0; i<4; i++)
     for(int j=0; j<4; j++) {
      if(!(state[temp_Xpos+i][temp_Ypos+j]%2==1 && block_Info[i][j]==0)) {
       state[temp_Xpos+i][temp_Ypos+j]=block_Info[i][j];
      }
     }
   }
  } else {
   for(int i=0; i<4; i++)
    for(int j=0; j<4; j++)
     if(block_Info[i][j]!=0)
      state[i][j+3]=block_Info[i][j];
  }
 }
 public void keyPressed(KeyEvent e) {
  if(isGameOver)
   return;
  if(e.getKeyCode()==KeyEvent.VK_LEFT || e.getKeyCode()==KeyEvent.VK_A) { // <- 키를 눌렀을때
   Vector<Point> v = new Vector<Point>();
   for(int i=0; i<20; i++)
    for(int j=0; j<10; j++) {
     if(state[i][j]!=0 && state[i][j]%2==0 && state[i][j]!=20) {
      if(j-1<0)
       return;
      Point tmp = getBlockPosition();
      int tmpX = (int)tmp.getX();
      int tmpY = (int)tmp.getY();
      
      int[][] block_Info = currentBlock.getBlockCodeInfo();
      for(int a=0; a<4; a++)
       for(int b=0; b<4; b++) {
        if(block_Info[a][b]!=0)
         if(state[tmpX+a][tmpY+b-1]%2==1 && state[tmpX+a][tmpY+b-1]!=999)
          return;
       }
      v.add(new Point(i, j));
     }
    }
   for(int i=0; i<v.size(); i++) {
    Point tmp = v.get(i);
    state[(int)tmp.getX()][((int)tmp.getY())-1]=state[(int)tmp.getX()][(int)tmp.getY()];
    state[(int)tmp.getX()][(int)tmp.getY()]=0;
   }
   repaint();
  }
  if(e.getKeyCode()==KeyEvent.VK_RIGHT || e.getKeyCode()==KeyEvent.VK_D) {
   Vector<Point> v = new Vector<Point>();
   for(int i=0; i<20; i++)
    for(int j=9; j>=0; j--) {
     if(state[i][j]!=0 && state[i][j]%2==0 && state[i][j]!=20) {
      if(j+1>9)
       return;
      Point tmp = getBlockPosition();
      int tmpX = (int)tmp.getX();
      int tmpY = (int)tmp.getY();
      
      int[][] block_Info = currentBlock.getBlockCodeInfo();
      for(int a=0; a<4; a++)
       for(int b=0; b<4; b++) {
        if(block_Info[a][b]!=0 && state[tmpX+a][tmpY+b+1]%2==1 && state[tmpX+a][tmpY+b+1]!=999)
         return;
       }
      v.add(new Point(i, j));
     }
    }
   for(int i=0; i<v.size(); i++) {
    Point tmp = v.get(i);
    state[(int)tmp.getX()][((int)tmp.getY())+1]=state[(int)tmp.getX()][(int)tmp.getY()];
    state[(int)tmp.getX()][(int)tmp.getY()]=0;
   }
   repaint(); 
  }
  if(e.getKeyCode()==KeyEvent.VK_DOWN || e.getKeyCode()==KeyEvent.VK_S) {
   while(true) {
    boolean temp_Bool = false;
    for(int i=19; i>=0; i--) // 밑에서 부터 확인
     for(int j=0; j<10; j++)
      if((state[i+1][j]%2==1 || state[i+1][j]==20) && state[i][j]%2==0 && state[i][j]!=20 && state[i][j]!=0) // 떨어질 수 있는 끝까지 떨어졌을때
       temp_Bool = true; // while(true) 문 종료
    repaint();
    if(temp_Bool)
     break;
    for(int i=19; i>=0; i--)
     for(int j=0; j<10; j++)
      if(state[i][j]!=0 && state[i][j]%2==0 && state[i][j]!=20) {
       state[i+1][j]=state[i][j];
       state[i][j]=0;
      }
   }
  }
  if(e.getKeyCode()==KeyEvent.VK_UP || e.getKeyCode()==KeyEvent.VK_W) {
   currentBlock.rotateBlock();
   set(true);
   repaint();
  }
 }
 public void keyReleased(KeyEvent e) {}
 public void keyTyped(KeyEvent e) {}
 

 class FallingBlockThread extends Thread {
  @Override
  public void run() {
   // 랜덤으로 블럭 소환
   currentBlock = new Blocks();
   set(false);
   repaint();
   
   // 떨구기
   while(true) {
    try {Thread.sleep(500);} catch (InterruptedException e) {}
    boolean temp_Bool = false;
    for(int i=19; i>=0; i--) // 밑에서 부터 확인
     for(int j=0; j<10; j++)
      if((state[i+1][j]%2==1 || state[i+1][j]==20) && state[i][j]%2==0 && state[i][j]!=20 && state[i][j]!=0 && state[i][j]!=20) { // 떨어질 수 있는 끝까지 떨어졌을때
       temp_Bool = true; // while(true) 문 종료
      }
    repaint();
    if(temp_Bool)
     break;
    for(int i=19; i>=0; i--)
     for(int j=0; j<10; j++)
      if(state[i][j]!=0 && state[i][j]%2==0 && state[i][j]!=20 && state[i][j]!=20) {
       state[i+1][j]=state[i][j];
       state[i][j]=0;
      }
   }
   
   for(int i=0; i<20; i++)
    for(int j=0; j<10; j++)
     if(state[i][j]%2==0 && state[i][j]!=20 && state[i][j]!=0 && state[i][j]!=20)
      state[i][j]-=1;  
   removeLine();
   
   if(isGameOver())
    setGameOver();
   if(!isGameOver() && !isGameOver)
    isThreadEnd = true;
  }
 }
}