Whack A Mole (Toy Project)

강태석·2022년 9월 4일
1

프로젝트

목록 보기
1/3
post-thumbnail

🧩토이 프로젝트

Whack A Mole 🙈


👉 프로젝트 선택과정

과정에서의 첫 프로젝트인 만큼 욕심이 났다. (내가 제일 잘하고 싶다! 🤓)
아무거나 만들어보라는 프로젝트 미션을 받자마자 어떤게 있을까 구글링을 하고, 유튜브를 뒤져서 한 외국인 개발자가 자바 스윙으로 구현한 2D게임을 하나부터 열까지 강의하는 채널을 찾게 되었다.
2D게임 유튜브 강의 채널
이거다 싶어서 열심히 따라서 코드를 작성하고, 내껄로 만드려고 하나 하나 열심히 해석하며 주석을 달았다.
과유불급.. 욕심이 너무 컷던걸까? 😱
3일쯤 됐을때 진도가 너무 안나가고 이해하기 힘든 코드가 많다는걸 느꼈고, 결국 다 갈아엎게 되었다..
내가 하나부터 열까지 만들 수 있는걸 만들어보자 다시 마음을 잡고 생각을 하던 중
💡 빙고! 두더지 게임이 떠올랐다! 💡
두더지 게임은 내가 만들 수 있을거 같아 대충 구상을 하고 바로 이클립스를 켰다.


✍ 프로젝트 구상

구상은 간단했다.
먼저 디자인은 자바 스윙을 이용하여 프레임을 만들고,
그 프레임을 위, 아래로 게임 시작 등 버튼들과 점수, 시간을 나타내 주는 공간과 두더지들이 활동하는 공간으로 나눈 후 기능을 넣어주면 되겠다고 생각했다.
두더지들은 버튼 객체로 만들어 이벤트를 부여해주고 나와있을때와 들어가있을때를 구분해줄 boolean형 변수를 넣어주면 게임을 쉽게 구현 할 수 있을거라 생각했다.
오락실에 있는 두더지 게임 머신과 똑같이 만들어야지~ 😋


💻 프로젝트 구현

// 요소 선언
JPanel p_center; // 센터 영역
JPanel p_north; // 북쪽 영역
GamePanel gamePanel; // 게임 패널
ScorePanel scorePanel; // 유저에게 점수 및 시간 등을 보여줄 패널

먼저 구상했던 대로 JFrame을 하나 만들어주고 영역을 나눠
게임패널과 스코어패널 두 공간을 만들어 주었다.

👍 나눈 공간에 각 컴포넌트들을 넣어 디자인을 완성해 보았다. 👍

🔨 이제 기능을 넣을 차례다.

먼저 Window (JFrame) 클래스에 isRunning 변수를 이용해 게임이 실행중일 때와 실행중이지 않을때를 구분해주었다.

// Window 클래스
static boolean isRunning = false; // 게임이 실행중인지 아닌지

두더지 클래스의 isUp 불린형 변수를 이용하여 두더지가 나와있는지, 들어가있는지를 판단해 각 기능을 실행해 줄 것이다.

// Mole (두더지) 클래스
public boolean isUp = false; // 머리가 나와있는지 안나와있는지를 불린값으로

이제 두더지들을 관리하기 쉽게 moles라는 List에 넣고,
GamePanel에서 두더지쓰레드를 실행시켜 줄 것이다.
두더지쓰레드에선 두더지들중 랜덤한 녀석을 뽑아 고개를 들게하고
Thread.sleep()을 이용해 고개를 든 후 몇초 후에 다시 숨도록 구현해 주었다.
( 이때 두더지가 고개를 들 때 isUp = true , 들어갈때 isUp = false 가 되는것이다 )

@Override
public void run() { // 두더지 활동 쓰레드
		
	while(Window.isRunning) {
		//System.out.println("두더지쓰레드 런");
		//System.out.println(moles.size());
		int ranNum = random.nextInt(moles.size()); // 0 ~ moles.size 까지의 랜덤값 추출
		moles.get(ranNum).isUp = true; // 두더지 빼꼼
		if(moles.get(ranNum).isUp == true) { // 두더지가 빼꼼일때
			// 두더지가 머리를 들었을때 블랙으로 변경 (들어가면 다시 초록)
			moles.get(ranNum).setBackground(Color.WHITE);
			moles.get(ranNum).setIcon(Util.getInstance().createIcon("/images/mole.png", getIconSize(), getIconSize()));
			//System.out.println("아이콘 사이즈 : "+getIconSize());
			try {
				
				moleThread.sleep(upSpeed * 300); // ?초 쉬기 ( 0.3초 의 배수 )
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			moles.get(ranNum).isUp = false; // 두더지 들어가기
			moles.get(ranNum).setBackground(Color.WHITE); 
			moles.get(ranNum).setIcon(Util.getInstance().createIcon("/images/hole.png", getIconSize(), getIconSize())); // 두더지가 들어갔으니 아이콘 변경
		}
	}
		
}

위 코드는 프로젝트 완성 후의 코드라 Icon이 들어가게 되어있는데,
완성 전, 프로젝트 진행중 이었을땐 두더지 객체의 배경색만 바꾸어 테스트를 해보았다.

두더지가 아주 잘 활동하는걸 볼 수 있다. 🤟

자세히 보면 스코어패널의 시간이 흐르고있는 걸 볼 수 있는데
흐르는 시간은 아래와 같이 구현해 봤다. ⏰

public void ticktock(int zero_sc, int sc, int min) { // 시계 만들기
	scorePanel.time_field.setText(min+" : "+sc+" : "+zero_sc);
}

@Override
public void run() {
	int zero_sc = 0;
	int sc = 0;
	int min = 0;
	while(isRunning) {
		// 무한루프
		try {
			
			/* ---------------- 타이머 코드 시작 ----------------*/
			Thread.sleep(10);
			ticktock(zero_sc, sc, min);
			zero_sc++;
			if(zero_sc > 60) {
				zero_sc = 0;
				sc++;
			}
			if(sc > 60) {
				sc = 0;
				min++;
			}
			/* ---------------- 타이머 코드 종료 ----------------*/
			//scorePanel.repaint();
			//System.out.println("쓰레드 러닝");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

추가로 개발을 하며 언제 게임이 종료되어야 할까 고민을 해봤는데,
끝나는 시간을 정해둘까 하다가 목숨을 넣으면 재밋겠다 싶어
기본값 5의 목숨을 넣어줬다. 🩸

public Life() {
	// 라이프 클래스 기본 셋팅
	this.setIcon(Util.getInstance().createIcon("/images/heart.png", 100, 100));
	this.setPreferredSize(new Dimension(100, 100));
	this.setHorizontalAlignment(JLabel.CENTER);
}

두더지의 isUp이 false일때 클릭했다면 목숨을 하나 깎고, 점수도 10점 깎이도록 true일 땐 점수가 10점 오르도록 구현 해봤다.

@Override
public void actionPerformed(ActionEvent e) { // 두더지 누르면
	//System.out.println(moleNum+"번째 두더지야");
	if(Window.isRunning) { // 게임이 실행중일때만!!
		if(isUp) {
			System.out.println("잡았다!");
			
			scorePanel.score+=10;	// 스코어패널의 점수 10점씩 누적
			scorePanel.score_field.setText("점수 : "+scorePanel.score); // 스코어패널의 스코어필드 새로고침
			
			isUp = false;
			setBackground(Color.WHITE);
			this.setIcon(Util.getInstance().createIcon("/images/hole.png", GamePanel.getIconSize(), GamePanel.getIconSize()));
			//System.out.println(GamePanel.tileAmount);
		}else {
			System.out.println("나 아니라구!");
			
			scorePanel.score-=10;	// 스코어패널의 점수 10점씩 감소
			scorePanel.score_field.setText("점수 : "+scorePanel.score); // 스코어패널의 스코어필드 새로고침
			
			if(scorePanel.lifes.size() > 1) { // 만약 라이프가 있다면
				// 목숨 1개 감소
				scorePanel.deleteOneLife();
				//System.out.println("라이프 수 : "+scorePanel.lifes.size());
				// 새로고침
				scorePanel.revalidate();
				scorePanel.repaint();
			}else { // 라이프 소진시
				Window.isRunning = false; // 게임 종료
				JOptionPane.showMessageDialog(window, "게임 오버!!!");
			}
		}
	}
}

이제 거의 다 완성 했다!
마지막으로 난이도를 두더지들의 수와 나오고 들어가는 속도를
늘리고 줄이며 유저가 직접 조절할 수 있게 만들어 봤다. 👩‍🔧

/* -------------------------------- 두더지 설정버튼 눌렀을 때 -------------------------------- */
if(e.getSource()==scorePanel.bt_setLevel) {
	System.out.println("두더지 설정 눌렀어요");
	if(!isRunning) {
		
		// 두더지 마릿수 입력받기
		String getNum = JOptionPane.showInputDialog("두더지 수를 입력하세요. (1~9)");
		if(getNum!=null) {
			boolean isNum = Pattern.matches("^[1-9]*$", getNum); // 숫자 0~9까지의 정규표현식
			if(isNum) { // 숫자가 맞다면
				
				try {
					if(Integer.parseInt(getNum)<10) {
						gamePanel.tileAmount = Integer.parseInt(getNum); // 타일갯수에 받은 수 대입
					}else {
						JOptionPane.showMessageDialog(this, "1~9의 숫자만 입력하세요.");
					}
				} catch (NumberFormatException e1) {
					JOptionPane.showMessageDialog(this, "수를 입력해주세요.");
				}
				gamePanel.deleteMoles(); // 두더지수를 바꿨기 때문에 존재하는 두더지들 모두 지우고
				gamePanel.createMoles(this); // 받은 두더지수 만큼 두더지를 다시 생성 해준다
				
				// 패널 새로고침
				gamePanel.revalidate();  
				gamePanel.repaint();
			}else {
				JOptionPane.showMessageDialog(this, "1~9의 숫자만 입력하세요.");
			}
		}
		
	}else {
		JOptionPane.showMessageDialog(this, "게임이 실행중입니다.");
	}
}
/* -------------------------------- 두더지 설정버튼 종료 -------------------------------- */

/* -------------------------------- 속도 설정버튼 눌렀을 때 -------------------------------- */
if(e.getSource()==scorePanel.bt_setSpeed) {
	System.out.println("속도설정 눌렀니?");
	if(!isRunning) {
		
		// 속도 입력 받기
		String getSpeed = JOptionPane.showInputDialog("속도를 입력하세요. (1 = 0.3초)");
		if(getSpeed!=null) {
			boolean isNum = Pattern.matches("^[1-9]*$", getSpeed); // 숫자 0~9까지의 정규표현식
			if(isNum) { // 숫자가 맞다면
				try {
					if(Integer.parseInt(getSpeed)<10) {
						gamePanel.changeSpeed(Integer.parseInt(getSpeed));
					}else {
						JOptionPane.showMessageDialog(this, "1~9의 숫자만 입력하세요.");
					}
				} catch (NumberFormatException e1) {
					JOptionPane.showMessageDialog(this, "수를 입력해주세요.");
				}
				
			}else {
				JOptionPane.showMessageDialog(this, "1~9의 숫자만 입력하세요.");
			}
		}
		
	}else {
		JOptionPane.showMessageDialog(this, "게임이 실행중입니다.");
	}
}
/* -------------------------------- 속도 설정버튼 종료 -------------------------------- */

🎈 알고리즘 순서도


🎥 프로젝트 시연


💗 프로젝트를 마치며

정말 너무 뜻깊은 시간이었다.
사실 요즘 하루 공부량이 너무 많다보니 프로젝트까지 신경쓰기가 정말 힘들었는데
눕기전에 꾹 참고 책상앞에 앉아 프로젝트를 시작하면 또 너무 재밋어서 시간 가는줄 모르고 했던 것 같다.
논리적으로 생각하고 코드를 작성하고 실행시켰을때 한번에 생각했던 대로 실행 되었을때의 쾌감은 잊지 못할 것 같다. 이맛에 개발을 하는거 같다 ㅎㅎ
스윙으로 재밋게 토이프로젝트를 진행 함으로써 자바에 대한 이해도도 더욱 깊어졌고 무엇보다 재밋는 프로젝트를 만족스럽게 완성시켜서 너무 뿌듯했다.
덜덜 떨면서 발표도 해보고..
내 프로젝트를 칭찬 받았을때 이렇게 기분좋은지 처음 알았다.
한단계 성장한 기분이다.
앞으로 개발공부를 더 열심히 할 수 있을 것 같다😁

0개의 댓글