[자바기초] 쓰레드와 JPanel을 활용한 몬스터 피하기


package com.ljh;

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class MonsterGameFrame extends JFrame { 
 // 아바타로 사용할 문자열은 "@", 괴물로 사용할 문자열은 "M", 종료키는 'q', 괴물은 200ms 주기로 움직인다
 private JPanel gamePanel = new GamePanel("@", "M", 'q', 200); // 게임 패널, 컨텐트팬으로 사용한다.

 public MonsterGameFrame() {
  setTitle("Open Challenge 13");
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  setContentPane(gamePanel); // GamePanel을 컨텐트팬으로 사용한다.
 
  setSize(300,300);
  setVisible(true);

  gamePanel.setFocusable(true);  
  gamePanel.requestFocus(); // GamePanel이 키를 입력받을 수 있도록 포커스를 설정한다.
 }

 // 게임이 진행되는 패널. 배치관리자를 null로 설정
 class GamePanel extends JPanel {
  private String avatarChar;
  private String monsterChar;
  private char quitChar;
  private long monsterDelay;
  private JLabel avatar;// 아바타를 위한 레이블
  private JLabel monster; // 괴물을 위한 레이블
  final int AVATAR_MOVE = 10; // 아바타가 한번에 움직이는 픽셀 수

  public GamePanel(String avatarChar, String monsterChar, char quitChar, long monsterDelay) {
   this.avatarChar = avatarChar;
   this.monsterChar = monsterChar;
   this.quitChar = quitChar;
   this.monsterDelay = monsterDelay;
   
   // 아바타와 괴물 레이블 생성
   avatar = new JLabel(avatarChar); 
   monster = new JLabel(monsterChar);
   
   setLayout(null); // 아타바 레이블과 괴물 레이블의 위치를 마음대로 지정할 수 있도록 null로 설정
   addKeyListener(new MyKeyListener()); // 키 리스너 등록
   
   // 아바타 레이블의 위치와 크기 설정 및 팬에 부착
   avatar.setLocation(50,50);
   avatar.setSize(15,15);
   avatar.setForeground(Color.RED);
   add(avatar);
   
   // 괴물 레이블의 위치와 크기 설정 및 팬에 부착
   monster.setLocation(200,5);
   monster.setSize(15,15);
   add(monster);
   
   // 괴물을 움직이는 스레드 생성 및 시작 
   MonsterThread th = new MonsterThread(monster, avatar, monsterDelay);
   th.start();
   
   // 이 패널을 마우스로 클릭하면 강제로 포커스를 이 패널로 가져와서 키 입력을 처리할 수 있게 한다.
   this.addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
     JPanel panel = (JPanel)e.getSource();
     panel.requestFocus();
    }
   });

  }
  
  // GamePanel에 등록된 키 리스너. 상, 하, 좌, 우 키와 'q' 키 처리
  class MyKeyListener extends KeyAdapter {
   @Override
   public void keyPressed(KeyEvent e) {
    if(e.getKeyChar() == quitChar) // 종료 키는 유니코드 키
     System.exit(0); // 종료 키가 입력되면 프로그램 종료
    
    int keyCode = e.getKeyCode(); // 상, 하, 좌, 우 키는 유니코드 키가 아님
    
    // 키에 따라 아바타 레이블을 AVATAR_MOVE 픽셀 만큼 움직인다.
    switch(keyCode) {
    case KeyEvent.VK_UP: 
     avatar.setLocation(avatar.getX(), avatar.getY()-AVATAR_MOVE); 
     break;
    case KeyEvent.VK_DOWN: 
     avatar.setLocation(avatar.getX(), avatar.getY()+AVATAR_MOVE); 
     break;
    case KeyEvent.VK_LEFT: 
     avatar.setLocation(avatar.getX()-AVATAR_MOVE, avatar.getY()); 
     break;
    case KeyEvent.VK_RIGHT: 
     avatar.setLocation(avatar.getX()+AVATAR_MOVE, avatar.getY()); 
     break;
    }
    
    avatar.getParent().repaint(); // 아바타의 위치가 변경되었으므로 다시 그리기
    // 아바타가 있는 패널에는 이전의 위치에 있었던 아바타를 지워야 하기 때문에
    // 아바타의 부모 패널에게 다시그리기를 지시함
   }
  }
 }

 // 괴물 레이블을 움직이는 200ms 당 5 픽셀을 움직이면서 끊임없이 아바타를 추적하는 스레드
 // 생성자는 두 개의 레이블 컴포넌트를 전달받는다. 첫번째 from이 두번째 to 레이블을 추적한다.
 class MonsterThread extends Thread {
  private JLabel from; // 좇아가는 레이블. 괴물 레이블
  private JLabel to; // 도망가는 레이블. 아바타 레이블
  private long monsterDelay;
  private final int MONSTER_MOVE = 5; // from 레이블이 한번에 이동하는 거리
  
  public MonsterThread(JLabel from, JLabel to, long monsterDelay) {
   this.from = from;
   this.to = to;
   this.monsterDelay = monsterDelay;
  }
  
  @Override
  public void run() {
   int x=from.getX(),y=from.getY(); // 현재 괴물의 위치
   
   while(true) {
    if(to.getX() < from.getX()) // 아바타가 괴물의 왼쪽이 있는 경우 
     x = from.getX() - MONSTER_MOVE;
    else       // 아바타가 괴물의 오른쪽이 있는 경우
     x = from.getX() + MONSTER_MOVE;
    
    if(to.getY() < from.getY()) // 아바타가 괴물의 위쪽이 있는 경우
     y = from.getY() - MONSTER_MOVE;
    else       // 아바타가 괴물의 아래쪽이 있는 경우
     y = from.getY() + MONSTER_MOVE;   
    
    // 괴물의 위치 수정
    from.setLocation(x, y);
    
    // 괴물의 위치가 변경되었기 때문에 괴물을 포함하는 패널을 다시 그리도록 함
    from.getParent().repaint();
    
    // 200ms 동안 잠을 잔다.
    try {
     sleep(monsterDelay);
    }catch(InterruptedException e) {
     return;
    }
   }
  }
 }
 
 public static void main(String [] args) {
  new MonsterGameFrame();
 }
}

댓글

이 블로그의 인기 게시물

[자바기초] jxl을 이용하여 자바에서 엑셀파일 읽고,쓰기

[자바기초] Vector, Iterator를 이용해서 정수 삽입후 모든 정수 출력 및 합산

[자바기초] HashMap으로 [학생 이름, Student 객체]를 이용하여 저장, 출력