사용자가 키보드 방향키를 사용해 □ 를 이동시키고
몬스터로 지정한 ⫸ 기호가 사용자를 따라오게 만든다.
setSize(400, 400);
setVisible(true);
작성하지 않는다면 화면에 보이는게 없을 것이다.
setContentPane(gamePanel);
을 해주어 게임이 진행되는 패널을 달아줘야 한다.
extends KeyAdapter 사용
implements KeyListener을 해줄 수도 있다. 하지만 내가 사용할 것은 KeyPressed만 이지만 아래 있는 걸 모두 재정의 해야하므로..
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
super.keyReleased(e);
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
super.keyTyped(e);
}
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
굳이 모두 재정의 하지 않아도 되는 KeyAdapter를 extends 해주었다.
-.getParent().repaint();
움직이는 것처럼 보이기 위해서는 아이콘이 새 좌표로 움직였다면 이전에 그려져있던 아이콘이 지워져야한다. 따라서 부모 패널에게 다시 그리라는 지시를 해야한다.
잡았을 때의 출력
if (x > to.getX()- 10 && x < to.getX() + 10) {
if (y > to.getY() - 10 && y < to.getY() + 10) {
JOptionPane.showMessageDialog(null, "몬스터가 이겼습니다");
}
} else {
// 200ms 동안 잠을 잔다
try {
sleep(monsterDelay);
} catch (InterruptedException e) {
return;
}
}
잡았다 = 몬스터와 사용자의 아이콘이 겹쳤다.
하지만 겹쳤다고 해서 "몬스터 위치 == 사용자 위치"로 설정한다면 게임이 계속 끝나지 않을 수도 있다. 사람의 눈에는 두 아이콘이 동일 좌표에 있는 것 처럼 보여도, 실제로는 1픽셀 차이로 겹치지 않았을 수 있기 때문이다.
따라서 약간의 오차범위를 주는게 원하는 대로 출력을 볼 수 있는 방법일 수 있다.
// 아바타로 사용할 문자열은 □
// 괴물로 사용할 문자열은⫸
// 종료키는 q
// 괴물은 200ms 주기로 움직이기
public class MonsterGameFrame extends JFrame {
// 게임 패널, 컨탠트팬으로 사용
private JPanel gamePanel = new GamePanel("□", "⫸", 'q', 200);
// 생성자 작성
public MonsterGameFrame() {
setTitle("Monster Game");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 게임 판넬의 객체화된 주소가 들어간다
setContentPane(gamePanel);
setSize(400, 400);
setVisible(true);
// 움직이며 찍는 것 (= 포커스)
// 게임 판넬의 포커스를 활성 시켜라
// = 마우스로 판넬에 이벤트를 주면 활성화된다
gamePanel.setFocusable(true);
// 키보드로 포커스를 입력 받을 수 있도록 설정
gamePanel.requestFocus();
}
// 게임이 진행되는 패널
// 배치관리자는 null로 설정한다
public class GamePanel extends JPanel {
// 사용자의 아이콘
private String avatarChar;
// 몬스터 아이콘
private String monsterChar;
// 종료키
private char quitChar;
// 몬스터가 움직이는 시간 간격
private long monsterDelay;
// 레이블 생성할 변수
private JLabel avatar;
private JLabel monster;
// 아바타가 한번에 움직일 수 있는 픽셀 수
private static final int AVATAR_MOVE = 10;
// GamePanal("□", "⫸", 'q', 200)
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);
// 아바타와 괴불의 레이블 위치를 마음대로 지정할 수 있도록 null로 설정
setLayout(null);
// 키 리스너 등록
// MyKeyListener을 감자할 것이다.
// 키보드로 아바타가 움직임이는 것을 감지할 것이다
addKeyListener(new MyKeyListener());
// 아바타 레이블의 위치설정
avatar.setLocation(50, 150);
// 아바타 크기 설정
avatar.setSize(15, 15);
// 아바타 색 설정
avatar.setForeground(Color.blue);
// 판넬에 부착
add(avatar);
// 괴물 레이블의 위치 설정
monster.setLocation(200, 5);
// 괴물 크기 설정
monster.setSize(15, 15);
// 판넬에 부착
add(monster);
// 괴물은 알아서 스레드가 움지겨줘야 하기 때문에
// 움직이게 할 스레드 생성
MonsterThread mt = new MonsterThread(monster, avatar, monsterDelay);
mt.start();
}
// 키보드의 움직임을 감지하는 이벤트 리스너 구현
// 게임 패널에 등록된 키 리스너
// "상 하 좌 우" 키 등록과, '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:
// 아바타의 x좌표 y좌표를 얻어와서
// -10씩 움직인다(위로 움직이면 y 좌표값이 - 되니까)
avatar.setLocation(avatar.getX(), avatar.getY() - AVATAR_MOVE);
break;
// 아래 화살표키
case KeyEvent.VK_DOWN:
// 아래로 가면 y좌표가 +되어 움직이니까
avatar.setLocation(avatar.getX(), avatar.getY() + AVATAR_MOVE);
break;
// 왼쪽 화살표키
case KeyEvent.VK_LEFT:
// 왼쪽으로 가면 x좌표가 -되어 움직이니까
avatar.setLocation(avatar.getX() - AVATAR_MOVE, avatar.getY());
break;
// 오른쪽 화살표키
case KeyEvent.VK_RIGHT:
// 오른쪽으로 가면 x좌표가 +되어 움직이니까
avatar.setLocation(avatar.getX() + AVATAR_MOVE, avatar.getY());
break;
} // end switch
// 아바타의 위치가 변경되었으니 다시 찍어준다(그려준다)
// 이전에 위치하던 아바타를 지우고 다니 나타나게 해야 움직이는 것처럼 보이니까
// 아바타의 부모 패널에게 다시 그리기를 지시함
avatar.getParent().repaint();
} // end KeyPressed
}// end MyKeyListenr
}
// 괴물 작업 스레드 설정
class MonsterThread extends Thread {
// 좇아가는 괴물 레이블
private JLabel from;
// 도망가는 아바타 레이블
private JLabel to;
// 움직임 제어
private long monsterDelay;
// form 레이블이 한번에 움직일 수 있는 픽셀 수를 지정
private static final int MONSTER_MOVE = 5;
// new MonsterThread(monster, avatar, monsterDelay)
public MonsterThread(JLabel monstar, JLabel avatar, long monsterDelay) {
this.from = monstar;
this.to = avatar;
this.monsterDelay = monsterDelay;
}
@Override
public void run() {
// 현재 괴물의 위치
int x = from.getX();
int y = from.getY();
boolean flag = true;
while (flag) {
// 아바타가 괴물의 왼쪽에 있는 경우
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;
}
// 몬스터가 이용자를 잡으면 출력되도록함
// 만약 같은 위치에 있을 경우
// 오차범위를 줘야한다
if (x > to.getX()- 10 && x < to.getX() + 10) {
if (y > to.getY() - 10 && y < to.getY() + 10) {
JOptionPane.showMessageDialog(null, "몬스터가 이겼습니다");
}
} else {
// 200ms 동안 잠을 잔다
try {
sleep(monsterDelay);
} catch (InterruptedException e) {
return;
}
}
try {
sleep(monsterDelay);
} catch (InterruptedException e) {
return;
}
// 괴물의 위치 수정
from.setLocation(x, y);
// 괴물의 위치가 변경되었으니
// 다시 그린다
from.getParent().repaint();
}
}
}
public static void main(String[] args) {
new MonsterGameFrame();
}
}
실행 화면
1) 시작화면
2) 포인터들이 겹치면 팝업 메시지 출력