티스토리 뷰

 

팀원과 다음의 단계로 알낳기 게임 구현을 진행했다.

1. chicken 버튼, egg 버튼 생성

2. chicken:  addKeyListener로 왼쪽키, 오른쪽키에 따라 움직이게 만들기

3. chicken: addActionListener로 클릭 당했을 때 egg 생성하기

4. egg: 아래로 떨어지기

5. egg: 바닥에 닿으면 이미지 변경(병아리로)

6. egg: 클릭 시 이미지 바뀌기(보석으로)

(7. egg: 병아리 오른쪽으로 이동)

 

1번

Design탭에서 만든 버튼에 image만 넣어서 해결했다.

 

2번

addKeyListener의 keyPressed 메소드에 if-else문에 `KeyEvent.VK_RIGHT`, `KeyEvent.VK_LEFT`를 넣어 해결했다.

 

3번

치킨을 클릭 시 egg 생성하므로 

JButton egg = new Egg(chickenbtn.getLocation().x, chickenbtn.getLocation().y);

 

Egg 클래스의 생성자 메소드를 호출하여 new하도록 만들었다. Egg(int x, int y) 안에는 Egg() 기본 생성자가 있다. Egg()로 생성되고 나면 이후 egg만의 행동을 할 수 있도록 생성자 메소드를 짰다.

 

그런데 조금 우여곡절이 있었다. addActionListener 안의 actionPerformed 메소드에서 new Egg 달걀 생성하는 줄을 작성해도 실행해보면 보이지 않는 문제가 있었다. 이에 코파일럿에게 물어보니 contentPane.repaint(); 를 추가해야 한다고 말해줬다. 그리하여 추가해보니 보이게 되어 놀랐다. 이 코드는 시스템에게 화면 갱신을 요청하는 코드라고 한다. 암튼 그 코드를 추가하니 닭 뒤에 egg버튼이 생성되어서 기뻤다.

 

4번

처음 팀원과 egg의 본질에 대해 짚고 넘어갔었다. egg는 클릭되면 이미지가 바뀌고(1), 땅에 닿으면 병아리로 교체(2)된다.

그렇다면 이는 egg 클래스에서 구현되는 게 맞다고 생각했다.

그렇기에 Egg 클래스의 run() 메소드에서 윈도우 바닥에 닿을 때까지 내려가게 하는 코드를 작성했다. while(에그의 y값이 윈도우 아래에 닿을 때까지) setLocation값을 y+=50 하였다. (여기서 왜 떨어지는데 +를 하는지 궁금했는데 swing은 왼쪽 위(0,0) 가 원점이고, 아래로 갈수록 y가 커진다고 해서 궁금증이 해결됐다.) 

 

5번

이후 while문을 빠져나오면 바닥에 닿았다는 의미이므로 병아리 이미지로 변경해야한다. 그리하여 while문 빠져나오고 바로 아래에

this.setIcon(new ImageIcon(View.class.getResource("/image/egg3.png")));

 

이 코드를 넣었다.

 

6번

여기서도 위기가 있었다. 눌리면 보석으로 이미지 교체돼야 하니 Egg(int x, int y) 생성자 안에서 코드를 작성하는데(사실 이것도 어디에 쓸지 조금 고민이 됐었다.)

this.setIcon(new ImageIcon(View.class.getResource("/image/egg2.png"))); 이걸 쓸 수 없었다. 오류가 났다. 

this.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				// 여기서 에러가 났었다.
				/*Egg.*/this.setIcon(new ImageIcon(View.class.getResource("/image/egg2.png")));
			}
			 
		});

 

찾아보니 ActionListener라는 익명 객체 클래스의 this인지, Egg 클래스의 this를 말하는 것인지 분명히 해야 한다고 한다. 

그냥 this.setIcon() 을 쓰려 하면 당연히 더 내부인 ActionListener의 setIcon()을 찾으려다 에러가 생기는 것이다. (왜냐면 ActionListener는 setIcon() 메소드를 안 갖고 있기 때문에) 그렇기에 명확히 표현해주기 위해서 "Egg 클래스의 this야" 의미로 Egg.this.setIcon() 을 써야 하는 것이다.

 

이러한 과정 끝에 완성된 코드:

(1) View

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import javax.swing.ImageIcon;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import java.awt.Color;
import javax.swing.UIManager;

public class View extends JFrame {

	private static final long serialVersionUID = 1L;
	private JPanel contentPane;
	private JButton eggbtn;
	private JButton chickenbtn;


	/**
	 * Create the frame.
	 */
	public View() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 800, 475);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));

		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		chickenbtn = new JButton("New button");
		chickenbtn.setBackground(new Color(192, 192, 192));
		
		//치킨 오른쪽 키, 왼쪽 키 누르면 해당 방향대로 이동
		chickenbtn.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
			
				int keyCode = e.getKeyCode();
				int x = chickenbtn.getLocation().x;
				int y = chickenbtn.getLocation().y;
				
				if(keyCode == KeyEvent.VK_RIGHT) {
					chickenbtn.setLocation(x+20, y);
				}
				
				else if(keyCode == KeyEvent.VK_LEFT) {
					chickenbtn.setLocation(x-20, y);
				}
				
			}
		});
		
		//닭 눌릴 때 egg 생성(기본 생성자 this())->actionListener로 눌리면 egg2 이미지로 변경(egg(int x, int y))
		chickenbtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.out.println("닭이 눌림!!!");
				
				JButton egg = new Egg(chickenbtn.getLocation().x, chickenbtn.getLocation().y);
				
				contentPane.add(egg);
				//contentPane.repaint();
				Thread t = new Thread((Runnable)egg);
				//Egg 클래스의 run() 메소드 실행
				//egg 떨어지고 바닥에 닿으면 병아리로, 이후 오른쪽으로 이동
				t.start();
				
			}
		});
		
		
		chickenbtn.setBorderPainted(false);
		chickenbtn.setContentAreaFilled(false);
		chickenbtn.setFocusPainted(false);
		chickenbtn.setIcon(new ImageIcon(View.class.getResource("/image/chicken.png")));
		chickenbtn.setBounds(12, 29, 124, 135);
		contentPane.add(chickenbtn);
		
		
	}
}

 

(2) Egg

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;

//egg의 본질 : 땅에 닿으면 삐약이로 교체
//egg의 본질 : 클릭되면 이미지 바뀜
//chicken의 본질 : 클릭 당하면 알을 낳는다.
//chicken의 본질 : 좌우 움직인다.

public class Egg extends JButton implements Runnable, Behavior{
	
	private Thread th = new Thread(this);
	
	//egg 생성
	public Egg() {
		System.out.println("Egg 생성");
		
		this.setBounds(12, 29, 124, 135);
		
		this.setBorderPainted(false);
		this.setContentAreaFilled(false);
		this.setFocusPainted(false);
		
		this.setIcon(new ImageIcon(View.class.getResource("/image/egg1.png")));
	}
	
	
	public Egg(int x, int y) {
		this();
		this.setBounds(x, y, 50, 50);
		
		//눌리면 egg2(보석)으로 변경
		this.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				Egg.this.setIcon(new ImageIcon(View.class.getResource("/image/egg2.png")));
			}
			 
		});
	}
	
	@Override
	public void run() {
		int x = this.getLocation().x;
		int y = this.getLocation().y;
		// TODO Auto-generated method stub
		//바닥에 닿을 때까지 내려가면 된다. 
		while(y<375) {
		
			this.setLocation(x, y+=50);//왜 떨어지는데 +를 하나요??
			try {
				Thread.sleep(300);
			} catch(InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("EGG 떨어져용~~~~");
			
		}
		
		//바닥에 닿으면 병아리로 이미지 변경
		this.setIcon(new ImageIcon(View.class.getResource("/image/egg3.png")));
		
		this.setBorderPainted(false);
		this.setContentAreaFilled(false);
		this.setFocusPainted(false);
		
		//egg 오른쪽으로 이동
		while(x<800) {
			this.setLocation(x+=50, y);
			try {
				Thread.sleep(300);
			} catch(InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("Egg 오른쪽으로 이동");
		}
		
	}

	@Override
	public void moveDown() {
		// TODO Auto-generated method stub
		int x = this.getLocation().x;
		
	}

	@Override
	public void moveRight() {
		// TODO Auto-generated method stub
		
	}

}

 

(3) Main

import java.awt.EventQueue;

public class Main {
//앱 실행
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					View frame = new View();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

 

 

 

 

소감

처음에는 GUI 기본에, SOLID원칙과 MVC 패턴을 적용시켜봐야지! 했으나 막상 생각한 것을 GUI로 구현하려 해보니 이조차도 쉽지 않음을 느꼈다. 

 

현재의 문제점

1. new 생성 직접 하는 것 -> EggFactory 클래스 추가(생성 전담)

2. MVC 패턴 적용 안돼있음 -> Model은 치킨, 에그이고, View도 있으니 Controller 클래스 추가

3. 행동 추가 시 기존 코드 수정해야하는 OCP 원칙 위반 사항 -> Behavior 인터페이스 추가해서 moveRight, moveLeft, moveDown 클래스 만들어 전략 패턴 적용

 

원래는 위의 것들을 해보려 했는데.... 많이 아쉽다. 그리고 사실 GUI+THREAD니까 이 코드에서 THREAD 관련 내용도 정리해보자면...

 

Egg 클래스에

private Thread th = new Thread(this);

 

이 코드가 있는데 처음에는 egg가 runnable 인터페이스를 implements하고 있는데 위의 코드가 왜 필요한지 몰랐었다. Runnable 자체는 thread가 어떤 코드를 실행할지(run() 메소드 내부 정의해서) 정의해주는 역할이고, Thread가 run()을 실제로 수행해주는 역할을 하는 것이다. 따라서 this 버튼이 가진 run()을 실행하는 Thread 객체 th를 만들어서 th.start()를 해주면 run() 실행하고, 에그가 움직이게 된다. 

 

Runnable run() 정의 -> Thread(this) th 생성 -> start() 실행 -> run() 실행 -> egg 움직임

(runnable은 실행 코드 갖는 인터페이스, 실행해주는 thread 필요 -> new Thread(this)로 연결-> start() -> run() )

 

마지막으로 이 실습으로 좋았던 점은,

GUI는 눈에 바로 바로 보이기 때문에 팀원과 함께 만들어보며 위기(?)를 헤쳐나갈 때마다 눈으로 바로 보이니까 쾌감이 있었다.

(그럼에도 불구하고... 배운 걸 적용해서 MVC 패턴, SOLID 패턴을 적용해보고 싶었는데 그러지 못한 게 매우 아쉽다.)

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함