그림 맞추기 퍼즐 만들기

--·2023년 1월 3일
0
  • 4 X 3 격자 타일로 나누고 섞어서 화면에 표시
  • 우측 하단의 마지막 타일은 빈칸으로 표기하고 이 빈칸은 타일이 움직일 수 있는 공간을 제공한다.
  • 그림을 클릭하면 빈칸에 그림이 채워지고 그림이 있던 칸은 빈칸으로 바뀐다.

코드

package PuzzlePkg;

import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

// 프레임 생성과 이미지 불러오기 및 쪼개기

// GUI Swing
   class make_Puzzle extends JFrame implements MouseListener{
	   public int[][] idx = new int[4][3];               // 이미지 인덱스
	   int M_count; // 조각 이동 횟수
	   public BufferedImage img;   // 이미지
	   public BufferedImage[][] sub_img = new BufferedImage[4][3];   // 이미지 분할한 이미지 객체
	   int B_r, B_c;         // 빈 칸의 인덱스 
   
	   
	   public make_Puzzle(){ // GUI 생성
	   setLayout(null); // Layout은 NULL
	   setSize(600,600); // 크기 지정
	   setTitle("Picture Drawing"); // 제목
	   try {
		  img = ImageIO.read(new File("images/image01.jpg")); // 이미지를 가져옵니다.
	      } catch (IOException e) {
	         e.printStackTrace();
	      }
	   ImageDiv();      // 이미지 분할 함수 호출
	   addMouseListener(this);      // MouseListener 연결
	   setVisible(true);   // 보이게 해줍니다.
	   setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 프로세스 완전 종료
	      
   }
   
   public void ImageDiv(){ // 이미지를 분할해주는 함수
      int width = img.getWidth(this);// 가로분할길이
      int height = img.getHeight(this);// 세로분할길이
      width /= 3; // 3칸
      height /= 4; // 4칸
      int R_number[] = new int[12];// 랜덤수 저장 배열
      for (int i=0 ; i<12 ; i++){// 랜덤수 저장 배열 초기화      
          R_number[i] = 0;
       }
      int number = 0; // 랜덤수 저장
      boolean compare;// 랜덤수 비교
      int r, c;// 분할 이미지 저장 인덱스
      int R_index = 0;// 랜덤수 배열 인덱스
       
         
         R_number[0] = (int)(Math.random()*12 + 1);   // 랜덤수 생성
         for (int j=1 ; j<12 ; j++){   // 랜덤수 저장 배열에 1~12의 서로 다른 수 저장
            compare = true; // 겹치지 않게 하기 위해서 선언해줍니다.
            number = (int)(Math.random()*12 + 1); // 1~12까지 랜덤 수
            for (int z=0 ; z<12 ; z++){ // 12번 반복합니다.
               if (R_number[z] == number){ // 중복되면
                  compare = false; //false
               }
            }
            if (compare == true){ // 중복이 안되면
               R_number[j] = number; // 값을 넣어줍니다.
            }
            else{ // 중복이 되면 
               j--; // 다시 랜덤수를 가집니다.
            }
         }
        
         for (r=0 ; r<4 ; r++){   // 이미지를 무작위로 배치합니다.
             for (c=0 ; c<3 ; c++){ // getSubimage로 그림을 자릅니다.
                switch (R_number[R_index]){
                case 1:
                   sub_img[r][c] = img.getSubimage(0,  0, width, height);
                   idx[r][c] = 1;
                   break;
                case 2:
                   sub_img[r][c] = img.getSubimage(width,  0, width, height);
                   idx[r][c] = 2;
                   break;
                case 3:
                   sub_img[r][c] = img.getSubimage(width * 2,  0, width, height);
                   idx[r][c] = 3;
                   break;
                case 4:
                   sub_img[r][c] = img.getSubimage(0,  height, width, height);
                   idx[r][c] = 4;
                   break;
                case 5:
                   sub_img[r][c] = img.getSubimage(width,  height, width, height);
                   idx[r][c] = 5;
                   break;
                case 6:
                   sub_img[r][c] = img.getSubimage(width * 2,  height, width, height);
                   idx[r][c] = 6;
                   break;
                case 7:
                   sub_img[r][c] = img.getSubimage(0,  height * 2, width, height);
                   idx[r][c] = 7;
                   break;
                case 8:
                   sub_img[r][c] = img.getSubimage(width,  height * 2, width, height);
                   idx[r][c] = 8;
                   break;
                case 9:     
             	   sub_img[r][c] = img.getSubimage(width*2,  height * 2, width, height);
                    idx[r][c] = 9;
                   break;
                case 10:
                   sub_img[r][c] = img.getSubimage(0,  height * 3, width, height);
                   idx[r][c] = 10;
                   break;
                case 11:
                   sub_img[r][c] = img.getSubimage(width,  height * 3, width, height);
                   idx[r][c] = 11;
                   break;
                case 12:      
                   idx[r][c] = 12;
                   B_r = r;
                   B_c = c;
                   break;
                }
                R_index++;
             }
          }
     
   }
   
   
   
   public void paint(Graphics g){// Swing 의 JFrame을 상속한 클래스에서 paint()메소드를 오버라이드합니다
      if (img != null){			// paint()는 setVisible()을 수행할 때 실행됩니다.
         // drawImage(객체, x 좌표, y 좌표, width, height, Observer)
         g.drawImage(sub_img[0][0], 0, 0, 73, 79, this);
         g.drawImage(sub_img[0][1], 74, 0, 73, 79, this);
         g.drawImage(sub_img[0][2], 148, 0, 73, 79, this);
         
         
         g.drawImage(sub_img[1][0], 0, 80, 73, 79, this);
         g.drawImage(sub_img[1][1], 74, 80, 73, 79, this);
         g.drawImage(sub_img[1][2], 148, 80, 73, 79, this);
         
         
         g.drawImage(sub_img[2][0], 0, 160, 73, 79, this);
         g.drawImage(sub_img[2][1], 74, 160, 73, 79, this);
         g.drawImage(sub_img[2][2], 148, 160, 73, 79, this);
         
         g.drawImage(sub_img[3][0], 0, 240, 73, 79, this);
         g.drawImage(sub_img[3][1], 74, 240, 73, 79, this);
         g.drawImage(sub_img[3][2], 148, 240, 73, 79, this);
      }
      // 종료 조건 : 이미지가 맞으면 "게임승리 출력합니다."; 
      if (idx[0][0] == 1 && idx[0][1] == 2 && idx[0][2] == 3 &&
    	  idx[1][0] == 4 && idx[1][1] == 5 && idx[1][2] == 6 &&
    	  idx[2][0] == 7 && idx[2][1] == 8 && idx[2][2] == 9&&
    	  idx[3][0] == 10 && idx[3][1] == 11 && idx[3][2] == 12) {
    	  	g.drawString("게임 승리!", 400, 400);
    	  }
      
   }
// 마우스이벤트 오버라이딩 함수
   public void mouseClicked(MouseEvent e) {}
   public void mouseEntered(MouseEvent e) {}
   public void mouseExited(MouseEvent e) {}
   public void mouseReleased(MouseEvent e) {}
   public void mousePressed(MouseEvent e) {// 마우스 클릭 이벤트 처리
	      int inX = e.getX();      // 마우스 x 좌표
	      int inY = e.getY();      // 마우스 y 좌표
	      if (inX > 0 && inX < 74 && inY > 0 && inY < 80){      // [0][0]
	         Change_Image(0,0,1);   // 이미지 변경 함수 
	      }
	      else if(inX > 74 && inX < 148 && inY > 0 && inY < 80){   // [0][1]
	         Change_Image(0,1,2);   // 이미지 변경 함수
	      }
	      else if(inX > 148 && inX < 222 && inY > 0 && inY < 80){   // [0][2]
	         Change_Image(0,2,3);   // 이미지 변경 함수
	      }
	      else if(inX > 0 && inX < 74 && inY > 80 && inY < 160){   // [1][0]
	         Change_Image(1,0,4);   // 이미지 변경 함수
	      }
	      else if(inX > 74 && inX < 148  && inY > 80 && inY < 160){   // [1][1]
	         Change_Image(1,1,5);   // 이미지 변경 함수
	      }
	      else if(inX > 148 && inX < 222 && inY > 80 && inY < 160){   // [1][2]
	         Change_Image(1,2,6);   // 이미지 변경 함수
	      }
	      else if(inX > 0 && inX < 74 && inY > 160 && inY < 240){   // [2][0]
	         Change_Image(2,0,7);   // 이미지 변경 함수
	      }
	      else if(inX > 74 && inX < 148  && inY > 160 && inY < 240){   // [2][1]
	         Change_Image(2,1,8);   // 이미지 변경 함수
	      }
	      else if(inX > 148 && inX < 222 && inY > 160 && inY < 240){   // [2][2]
	         Change_Image(2,2,9);   // 이미지 변경 함수
	      }
	      
	      else if(inX > 0 && inX < 74 && inY > 240 && inY < 320){   // [2][0]
	          Change_Image(3,0,10);   // 이미지 변경 함수
	       }
	       else if(inX > 74 && inX < 148  && inY > 240 && inY < 320){   // [2][1]
	          Change_Image(3,1,11);   // 이미지 변경 함수
	       }
	       else if(inX > 148 && inX < 222 && inY > 240 && inY < 320){   // [2][2]
	          Change_Image(3,2,12);   // 이미지 변경 함수
	       }
	      repaint();         // paint()메소드 실행
   }
   public boolean Correct_clicked(int P_index){ // 조각을 상하좌우에 빈칸이 있을 때 이동할 수 있게 합니다.
	   switch (P_index){
	      case 1:
	         if (idx[0][1] == 12 || idx[1][0] == 12){return true;}break;
	      case 2:
	         if (idx[0][0] == 12 || idx[0][2] == 12 || idx[1][1] == 12){return true;}break;
	      case 3:
	         if (idx[0][1] == 12 || idx[1][2] == 12){return true;}break;
	      case 4:
	         if (idx[0][0] == 12 || idx[1][1] == 12 || idx[2][0] == 12){return true;}break;
	      case 5:
	         if (idx[0][1] == 12 || idx[1][0] == 12 || idx[1][2] == 12 || idx[2][1] == 12){return true;}break;
	      case 6:
	         if (idx[0][2] == 12 || idx[1][1] == 12 || idx[2][2] == 12){return true;}break;
	      case 7:
	         if (idx[1][0] == 12 || idx[2][1] == 12||idx[3][0] == 12){return true;}break;
	      case 8:
	         if (idx[1][1] == 12 || idx[2][0] == 12 || idx[2][2] == 12||idx[3][1] == 12){return true;}break;
	      case 9:
	         if (idx[1][2] == 12 || idx[2][1] == 12|| idx[3][2] == 12){return true;}break;
	      case 10:
	        if (idx[2][0]==12||idx[3][1]==12) {return true;}break;
	      case 11:
	         if(idx[3][0]==12||idx[2][1]==12||idx[3][2]==12) {return true;}break;
	      case 12:
	         if(idx[3][1]==12||idx[2][2]==12) {return true;}break;
	      default:
	         System.out.println("조각 배열 오류"); break;
	      }
	      return false;
   }
   // 빈 칸 이미지 반환
   public BufferedImage Make_White(){
      BufferedImage m_white = null;
      try {
         m_white = ImageIO.read(new File("images/white.png")); // 이미지를 불러옵니다.
      } catch (IOException e) {
         e.printStackTrace();
      }
      return m_white; // 이미지 반환
   }
   // 조각 이미지 교환(이동)처리 함수(2차원 배열 x, 2차원 배열 y, 조각 위치 인덱스)
   public void Change_Image(int x, int y, int index){
      if (Correct_clicked(index)){   // 클릭한 조각과 인접한 곳에 빈 칸이 있을 경우
         sub_img[B_r][B_c] = sub_img[x][y];   
         sub_img[x][y] = Make_White();   // 이미지 이동 및 빈 칸 이미지 저장
         idx[B_r][B_c] = idx[x][y];
         idx[x][y] = 12;// 인덱스 변환
         B_r = x;            
         B_c = y; // 빈 칸 인덱스를 변경
         M_count++; // 조각 옮긴 수를 하나 올려줍니다.
      }
   }
}
public class makePuzzle {
   public static void main(String[] args) {
      new make_Puzzle(); // make_Puzzle 생성
   }

}

실행 결과


어려웠던점 / 배운 점

  1. 처음에 image객체를 이용하려 하지 않고 초기 화면을 for문으로만 작성하니 이미지 교환하기가 어려워서 생각해보니까 이미지 객체를 선언해주어야 한다고 생각하였습니다. clipping을 사용하려 그림을 그리려고 노력하였으나 clipping을 사용하면 그림이 자꾸 중첩하여 다른 방법을 찾아본 결과 BufferedImage 클래스를 사용하여 getSubimage로 그림을 잘라주면 한번에 창에 출력되었습니다. 그런데 BufferedImage 클래스를 사용하면 ImageIO.read(new File(""))으로 그림을 받아와야 했습니다.
  2. Random class를 이용하여 초기화면의 이미지를 랜덤으로 세팅하였습니다.배열에 담고 랜덤으로 좌표 12개를 받은 후에 그 랜덤 좌표를 이미지 객체를 잘라서 붙여주면 되겠다는 생각을 했습니다.
  3. 마우스 이벤트를 이용하여 mousePressed()함수로 마우스를 클릭하면 repaint() 메소드를 호출하여(강제로 컴포넌트의 다시 그리기 지시하는 메소드)를 그림을 빈칸으로 이동하게 하였습니다. repaint()가 호출되면 -> paint() 호출되기 때문에 그림을 재배치할 수 있었습니다.
  4. 처음에 빈칸을 정말 빈칸으로 활용하였지만 빈칸 img를 이용하여 빈칸을 채워주면 칸을 이동할 때 빈칸 이미지에 클릭한 이미지를 채워주고 클릭한 이미지를 빈칸이미지로 채워주면 된다고 생각하였습니다.

후기

처음에 문제를 보고 막막했지만 한 틀로 보는게 아닌 랜덤은 어떻게 받을 것인가? 재배치는 어떤 식으로 할 것인가? 이미지를 어떻게 자를것인가?처럼 나누어서 문제를 하나하나 해결해 나가다보니 문제를 해결할 수 있었습니다. 앞으로도 코드를 짤 때 한번에 전체를 생각 하는게 아닌 부분부분 나누어 생각할 수 있게 공부를 하는게 좋을 것 같다고 생각하였습니다.

0개의 댓글