- [Java] 75. Swing: 버블버블 게임 만들기(6)2024년 05월 07일
- Song hyun
- 작성자
- 2024.05.07.:49
728x90반응형[Java] 75. Swing: 버블버블 게임 만들기(6)
이전 글: https://whatsthatsound.tistory.com/132
https://whatsthatsound.tistory.com/133
https://whatsthatsound.tistory.com/134
https://whatsthatsound.tistory.com/138
https://whatsthatsound.tistory.com/139
[Java] 74. Swing: 버블버블 게임 만들기(5)
[Java] 74. Swing: 버블버블 게임 만들기(5)이전 글: https://whatsthatsound.tistory.com/132https://whatsthatsound.tistory.com/133https://whatsthatsound.tistory.com/134https://whatsthatsound.tistory.com/138 [Java] 73. Swing: 버블버블 게임
whatsthatsound.tistory.com
[Java] 73. Swing: 버블버블 게임 만들기(4)
[Java] 73. Swing: 버블버블 게임 만들기(3)이전 글: https://whatsthatsound.tistory.com/132https://whatsthatsound.tistory.com/133https://whatsthatsound.tistory.com/134 [java] 72. Swing: 버블버블 게임 만들기(3)[Java] 72. Swing: 버블버
whatsthatsound.tistory.com
[java] 72. Swing: 버블버블 게임 만들기(3)
[Java] 72. Swing: 버블버블 게임 만들기(3)이전 글: https://whatsthatsound.tistory.com/132https://whatsthatsound.tistory.com/133 [Java] 71. Swing: 버블버블 게임 만들기(2)[Java] 71. Swing: 버블버블 게임 만들기(2)이전 글: htt
whatsthatsound.tistory.com
[Java] 71. Swing: 버블버블 게임 만들기(2)
[Java] 71. Swing: 버블버블 게임 만들기(2)이전 글: https://whatsthatsound.tistory.com/132 [Java] 70. Swing: 버블버블 게임 만들기(1)[Java] 70. Swing: 버블버블 게임 만들기(1) 자바의 GUI Swing을 사용해 버블버블
whatsthatsound.tistory.com
[Java] 70. Swin 버블버블 게임 만들기(1)
[Java] 70. Swing: 버블버블 게임 만들기(1) 자바의 GUI Swing을 사용해 버블버블 게임을 구현해보자. (1) Moveable 인터페이스 구현하기(2) Player 클래스 및 메서드 구현하기(3) BubbleFrame 내에서 배경 및
whatsthatsound.tistory.com
1. 목차
자바의 GUI Swing을 사용해 이전의 코드를 보완해보자.
(1) 물방울 벽 감지 기능 추가
(2) player-bubble 간의 코드 리팩토링
(3) self 참조를 통해 player 클래스 내부에서 add 메서드 끌어와서 사용하기
*셀프 참조(Self Context):
**main 함수를 가지고 있는 클래스는 하위에 생성된 모든 객체들의 주소값을 알고 있다!!
2. 구현해야 할 동작들
3. 코드 블록
(1) Bubble : 버블 클래스 및 메서드 생성
+BackgroundBubbleService
(2) BackgroundPlayerService: 플레이어의 움직임 관찰 & 제어 쓰레드 생성!
(3) BubbleFrame : 프레임 / 키 이벤트 / 시범 실행
(4) Player : player 객체 및 메서드 생성 (moveable 구현), 움직임 쓰레드 구현(Run-Start)
(5) Moveable : 인터페이스
package BubbleBubble.test.ex09; import javax.swing.ImageIcon; import javax.swing.JLabel; public class Bubble extends JLabel implements Moveable { private BubbleFrame mContext; // 의존성 컴포지션 관계 private Player player; private BackgroundBubbleService backgroundBubbleService; private int x; private int y; // 움직임 상태 private boolean left; private boolean right; private boolean up; // 적군을 맞춘 상태 private int state; // 0. 기본 물방울 1. 적을 가둔 상태 물방울 private ImageIcon bubble; // 기본 물방울 private ImageIcon bubbled; // 적을 가둔 물방울 private ImageIcon bomb; // 물방울 팡! // 연관 관계, 의존성 컴포지션 관계, 생성자 의존 주입(Dependency Injection) public Bubble(BubbleFrame mContext) { this.mContext = mContext; this.player = mContext.getPlayer(); initData(); setInitLayout(); } // Getter, Setter 메서드 public Player getPlayer() { return player; } public void setPlayer(Player player) { this.player = player; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public boolean isLeft() { return left; } public void setLeft(boolean left) { this.left = left; } public boolean isRight() { return right; } public void setRight(boolean right) { this.right = right; } public boolean isUp() { return up; } public void setUp(boolean up) { this.up = up; } public int getState() { return state; } public void setState(int state) { this.state = state; } public ImageIcon getBubble() { return bubble; } public void setBubble(ImageIcon bubble) { this.bubble = bubble; } public ImageIcon getBubbled() { return bubbled; } public void setBubbled(ImageIcon bubbled) { this.bubbled = bubbled; } public ImageIcon getBomb() { return bomb; } public void setBomb(ImageIcon bomb) { this.bomb = bomb; } private void initData() { bubble = new ImageIcon("img/bubble.png"); bubbled = new ImageIcon("img/bubbled.png"); bomb = new ImageIcon("img/bomb.png"); backgroundBubbleService = new BackgroundBubbleService(this); left = false; right = false; up = false; state = 0; } private void setInitLayout() { x = player.getX(); y = player.getY(); setIcon(bubble); setSize(50, 50); setLocation(x, y); } // 공통으로 사용하는 부분을 메서드로 만들어보자. // 이 메서드는 내부에서만 사용할 예정. @Override public void left() { left = true; for (int i = 0; i < 400; i++) { x--; setLocation(x, y); // 만약 왼쪽 벽에 부딪혔다면 --> up() if (backgroundBubbleService.leftWall() == true) { // 부딪힘 up(); break; } try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } up(); // TODO Auto-generated method stub } @Override public void right() { right = true; for (int i = 0; i < 400; i++) { x++; setLocation(x, y); if (backgroundBubbleService.rightWall() == true) { up(); break; } try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } up(); // TODO Auto-generated method stub } @Override public void up() { up = true; while (true) { y--; setLocation(x, y); if (backgroundBubbleService.topWall()) { break; } try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } clearBubble(); } // 외부 호출 안 될 메서드 private void clearBubble() { // 3초 뒤에 터짐 try { Thread.sleep(3000); setIcon(bomb); // 메모리에서 해제 처리 Thread.sleep(500); setIcon(null); // mContext.remove(this); // 컴포넌트 제거 -> 다시 그리지 않는다. // JFrame 내에 remove 메서드가 있다. } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package BubbleBubble.test.ex08; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class BackgroundBubbleService { private BufferedImage image; private Bubble bubble; // 연관 관계(생성자 의존 주입) // 생성자 public BackgroundBubbleService(Bubble bubble) { this.bubble = bubble; try { image = ImageIO.read(new File("img/backgroundMapService.png")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 왼쪽 벽에 부딪혔는지 public boolean leftWall() { Color leftColor = new Color(image.getRGB(bubble.getX()+10, bubble.getY()+25)); // 255 0 0 <--- 빨간색 (왼쪽 벽 확인) if(leftColor.getRed()==255 && leftColor.getGreen()==0 && leftColor.getBlue()==0) { return true; } return false; } // 오른쪽 벽에 부딪혔는지 public boolean rightWall() { Color rightColor = new Color(image.getRGB(bubble.getX()+50+10,bubble.getY()+25)); if(rightColor.getRed()==255 && rightColor.getGreen()==0 && rightColor.getBlue()==0) { return true; } return false; } // 천장 벽에 부딪혔는지 public boolean topWall() { Color topColor = new Color(image.getRGB(bubble.getX(), bubble.getY())); if(topColor.getRed()==255 && topColor.getGreen()==0 && topColor.getBlue()==0) { return true; } return false; } }
package BubbleBubble.test.ex08; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; /* * 현재 메인 쓰레드는 너~무 바빠! * 그래서, 백그라운드에서 계속 플레이어의 움직임을 관찰할 예정. */ public class BackgroundPlayerService implements Runnable { private BufferedImage image; private Player player; // 생성자 의존 설계 (Dependency Injection) public BackgroundPlayerService(Player player) { this.player = player; try { image = ImageIO.read(new File("img/backgroundMapService.png")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { // 색상 확인 todo(보정값 필요) while (true) { Color leftColor = new Color(image.getRGB(player.getX() + 10+5, player.getY() + 25)); Color rightColor = new Color(image.getRGB(player.getX() + 50 +10, player.getY() + 25)); // Color bottomColor = new Color(image.getRGB(player.getX(), player.getY())); // 흰색이면 바닥 RGB 값이 == 255, 255, 255 // 바닥인 경우는 --> 255, 0, 0 (=바닥) // 바닥인 경우는 --> 0, 0, 255 (=바닥) int bottomColorLeft = image.getRGB(player.getX() + 20, player.getY() + 50+5); int bottomColorRight = image.getRGB(player.getX() - 40, player.getY() + 50 + 5); // 하얀색 --> -1 if ((bottomColorLeft + bottomColorRight) != -2) { // 여기는 멈춰야 한다. (즉, 빨간 바닥 혹은 파란 바닥) player.setDown(false); } else { // 점프하는 순간에는 다운되면 안 된다. // 플레이어가 올라가는 상황이 아니라면, // 플레이어가 내려가는 상황이 아니라면, --> Down 호출 if (!player.isUp() && !player.isDown()) { player.down(); } } // 왼쪽에 충돌함 if (leftColor.getRed() == 255 && leftColor.getGreen() == 0 && leftColor.getBlue() == 0) { System.out.println("왼쪽 벽에 충돌함."); player.setLeftWallCrash(true); player.setLeft(false); } else if (rightColor.getRed() == 255 && rightColor.getGreen() == 0 && rightColor.getBlue() == 0) { System.out.println("오른쪽 벽에 충돌함."); player.setRightWallCrash(true); player.setRight(false); } else { player.setLeftWallCrash(false); player.setRightWallCrash(false); } // 위 두 조건이 아니면 player 마음대로 움직일 수 있다. try { Thread.sleep(3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
package BubbleBubble.test.ex09; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.ImageIcon; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; public class BubbleFrame extends JFrame { // 컨텍스트를 생성하는 방법 (셀프 참조) BubbleFrame mContext = this; private JLabel backgroundMap; private Player player; public BubbleFrame() { initData(); setInitLayout(); addEventListener(); // Player 백그라운드 서비스 시작 new Thread(new BackgroundPlayerService(player)).start(); } private void initData() { // todo-이미지 변경 backgroundMap = new JLabel(new ImageIcon("img/backgroundMap.png")); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Frame --> root Panel setContentPane(backgroundMap); setSize(1000, 640); // mContext --> 참조 타입() --> 주소값의 크기는 기본 4Byte이다. player = new Player(mContext); } private void setInitLayout() { // 좌표값으로 배치 setLayout(null); setResizable(false); // 프레임 조절 불가 setLocationRelativeTo(null); // JFrame을 여러분 모니터 가운데에 자동 배치 setVisible(true); add(player); } private void addEventListener() { this.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { System.out.println("key code : " + e.getKeyCode()); switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: // 왼쪽으로 방향키를 누르고 있다면, // 키보드 이벤트가 계속 왼쪽으로 간다. <- <- <- // 왼쪽 상태가 아니라면,? if(!player.isLeft() && !player.isLeftWallCrash()) { player.left(); } // 왼쪽 벽에 충돌한 게 아니라면,? break; case KeyEvent.VK_RIGHT: if(!player.isRight() && !player.isRightWallCrash()) { player.right(); } break; case KeyEvent.VK_UP: player.up(); break; case KeyEvent.VK_SPACE: player.attack(); // 프레임에 컴포넌트를 add 하는 동작은 누구? // -> JFrame의 add() 메서드 // 버블 실행 시에 끊김 현상이 발생하는 이유는 왜일까? default: break; } } // end of key pressed @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: // 왼쪽으로 가는 거 멈춰! player.setLeft(false); break; case KeyEvent.VK_RIGHT: // 오른쪽으로 가는 거 멈춰! player.setRight(false); break; case KeyEvent.VK_UP: player.setUp(false); break; default: break; } } // end of keyReleased }); // end of keyAdapter } // getter 메서드 public Player getPlayer() { return this.player; } // 코드 테스트 public static void main(String[] args) { // main 함수를 가지고 있는 클래스는 하위에 생성된 모든 객체들의 주소값을 알고 있다!! new BubbleFrame(); } }
package BubbleBubble.test.ex09; import javax.swing.ImageIcon; import javax.swing.JLabel; public class Player extends JLabel implements Moveable { BubbleFrame mContext; private int x; private int y; private ImageIcon playerR, playerL; // 움직임의 상태 private boolean left; private boolean right; private boolean up; private boolean down; // 벽에 충돌한 상태 private boolean leftWallCrash; private boolean rightWallCrash; // 플레이어 속도 상태 private final int SPEED = 4; private final int JUMPSPEED = 2; // enum 타입의 활용 PlayerWay playerWay; // get, set // setter 메서드 public void setLeft(boolean left) { this.left = left; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public ImageIcon getPlayerR() { return playerR; } public void setPlayerR(ImageIcon playerR) { this.playerR = playerR; } public ImageIcon getPlayerL() { return playerL; } public void setPlayerL(ImageIcon playerL) { this.playerL = playerL; } public boolean isDown() { return down; } public void setDown(boolean down) { this.down = down; } public boolean isLeftWallCrash() { return leftWallCrash; } public void setLeftWallCrash(boolean leftWallCrash) { this.leftWallCrash = leftWallCrash; } public boolean isRightWallCrash() { return rightWallCrash; } public void setRightWallCrash(boolean rightWallCrash) { this.rightWallCrash = rightWallCrash; } public boolean isLeft() { return left; } public boolean isRight() { return right; } public boolean isUp() { return up; } public int getSPEED() { return SPEED; } public int getJUMPSPEED() { return JUMPSPEED; } public void setRight(boolean right) { this.right = right; } public void setUp(boolean up) { this.up = up; } public Player(BubbleFrame mContext) { // 부모 메서드가 들어온다. initData(); setInitLayout(); this.mContext=mContext; } private void initData() { playerR = new ImageIcon("img/playerR.png"); playerL = new ImageIcon("img/playerL.png"); // 처음 실행 시 초기 값 셋팅 x = 450; y = 540; leftWallCrash = false; rightWallCrash = false; playerWay = PlayerWay.RIGHT; } private void setInitLayout() { // 플레이어가 가만히 멈춘 상태 left = false; right = false; up = false; down = false; setIcon(playerR); this.setSize(50, 50); setLocation(x, y); } @Override public void left() { playerWay = PlayerWay.LEFT; left = true; setIcon(playerL); new Thread(new Runnable() { @Override public void run() { while (left) { x = x - SPEED; setLocation(x, y); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // end of while } // end of run }).start(); // end of Thread } @Override public void right() { playerWay = PlayerWay.RIGHT; right = true; setIcon(playerR); new Thread(new Runnable() { @Override public void run() { while (right) { x = x + SPEED; setLocation(x, y); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // end of while } // end of run }).start(); // end of Rannable } // end of right public void attack() { // 일 작업자에게 위임시키기 // 람다 표현식 -- > 말 그대로 표현식 / 타입 추론 가능 new Thread(() -> { // run() 내부에 들어오는 식을 작성해 주면 된다. Bubble bubble = new Bubble(mContext); // mContext를 통해 JFrame 메서드를 호출할 수 있다. mContext.add(bubble); if(playerWay == PlayerWay.LEFT) { // 버블을 왼쪽으로 쏘기 bubble.left(); } else { // 버블을 오른쪽으로 쏘기 bubble.right(); } }).start(); } @Override public void up() { System.out.println("점프"); up = true; new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 130 / JUMPSPEED; i++) { y = y - JUMPSPEED; setLocation(x, y); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } // end of for up = false; down(); } // end of run }).start(); // 객체의 상태값을 잘 조절해야 한다. } @Override public void down() { System.out.println("다운"); down = true; new Thread(new Runnable() { public void run() { while (down) { y = y + SPEED; setLocation(x, y); try { Thread.sleep(3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } down = false; } }).start(); } }
package BubbleBubble.test.ex08; public interface Moveable { // public abstract 생략 가능 public abstract void left(); public abstract void right(); public abstract void up(); // 인터페이스 추가 기능 default 사용해보기 // 인터페이스에 모든 메서드는 추상 메서드이어야 한다. // 단, default 메서드를 제외하고. default void down() {}; // 마지막의 default 는 세미콜론을 추가해야 한다. }
728x90반응형'Java' 카테고리의 다른 글
[Java] 77. 내부 클래스 (0) 2024.05.14 [Java] 76. Thread 메서드: wait/notify (0) 2024.05.13 [Java] 74. Swing: 버블버블 게임 만들기(5) (0) 2024.05.03 [Java] 73. Swing: 버블버블 게임 만들기(4) (0) 2024.05.03 [java] 72. Swing: 버블버블 게임 만들기(3) (0) 2024.05.02 다음글이전글이전 글이 없습니다.댓글