본문 바로가기
→ My Meta+IT/JAVA Source

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

by DigitalJobs 2022. 12. 6.

자바로 구현된 다양한 게임들이 많지만 아래의 소스 코드는 직접 구현된 코드들이라서 온라인 상에 다듬어진 코드와는 정리가 부족하다는 말씀 미리 드립니다. 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;
  }
 }
}

댓글