한 번에 하나의 키 입력만 처리되므로, 'R'을 계속 눌러도 다른 키(U)를 누르면 R의 입력이 중단된다.
메인 스레드만 있기 때문에 키 전달을 여러 개 할 수 없고, 모든 루프가 완료 되어야 repaint 가 된다.
try {
Thread.sleep(2000); // 2초 멈춰라
} catch (InterruptedException e) {
e.printStackTrace();
}
따라서 RRRRR 5번 입력한다면 2*5 초인 10초만큼 멈춘 뒤 한번에 5칸 만큼 이동을 하게 될 것이다.
따라서 움직이는 코드에 스레드를 추가한다.
@Override
public void right() {
new Thread().start();
setIcon(playerR);
x = x+10;
setLocation(x, y);
}
스레드에는 러너블이라는 익명 클래스가 필요하다.


@Override
public void right() {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();
setIcon(playerR);
x = x+10;
setLocation(x, y);
}
위의 코드를 람다식으로 적을 수 있다.
@Override
public void right() {
new Thread(()-> {
}).start();
setIcon(playerR);
x = x+10;
setLocation(x, y);
}
@Override
public void left() {
System.out.println("L 스레드 생성");
new Thread(()-> {
setIcon(playerL);
x = x-10;
setLocation(x, y);
System.out.println("L 스레드 종료");
}).start();
}

길게 누르고 있을 때, 누를 때 마다 매번 스레드를 생성하는 것 보다 L 키를 뗐을 때 스레드를 종료하는 것이 더 좋지 않을까?
아래처럼 코드를 작성하면 버튼을 1번만 눌렀을 때 while 을 통해 캐릭터가 계속해서 움직이게 된다.
(그러나 여전히 키보드를 누를 때 마다 스레드가 생성되고 있음)
@Override
public void left() {
System.out.println("L 스레드 생성");
new Thread(()-> {
while (true) {
setIcon(playerL);
x = x-10;
setLocation(x, y);
try {
Thread.sleep(10); // 0.01초 간격
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}

따라서 움직임 상태를 관리하는 boolean 을 통해 관리한다.
public class Player extends JLabel implements Moveable {
// 위치 상태
private int x;
private int y;
// 움직임 상태
private boolean left;
private boolean right;
private boolean up;
private boolean down;
private ImageIcon playerR, playerL;
public Player() {
initObject();
initSetting();
}
private void initObject() {
playerR = new ImageIcon("image/playerR.png");
playerL = new ImageIcon("image/playerL.png");
}
private void initSetting() {
x = 55;
y = 535;
left = false;
right = false;
up = false;
down = false;
this.setIcon(playerR);
setSize(50, 50);
setLocation(x, y);
}
@Override
public void right() {
System.out.println('R');
right=true;
new Thread(()-> {
while (right) {
setIcon(playerR);
x = x+10;
setLocation(x, y);
try {
Thread.sleep(10); // 0.01초 간격
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
BubbleFrame 클래스에서 움직임 상태를 확인하여 동작 시킨다.
private void initListener() {
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
System.out.println(e.getKeyCode());
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if(!player.isLeft()) {
player.left();
}
break;
case KeyEvent.VK_RIGHT:
if(!player.isRight()) {
player.right();
}
break;
case KeyEvent.VK_UP:
player.up();
break;
}
}
});
}

키를 누르면 해당 방향의 상태가
true
로 바뀌며 새로운 스레드가 생성되지 않고, 지속적으로 캐릭터가 움직이게 된다. 하지만 다른 방향의 키를 누르는 순간, right
와 left
둘 다 true
상태가 되면서 두 개의 스레드가 동시에 실행된다. 이로 인해 캐릭터가 좌우로 빠르게 번갈아 움직이는 충돌 현상이 발생하게 된다.이를 방지하기 위해, 키보드 입력이 끝났을 때 해당 방향의 상태를
false
로 변경하여 스레드를 종료시켜야 한다."private void initListener() {
addKeyListener(new KeyAdapter() {
// 키보드 이벤트 핸들러
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if (!player.isLeft()) {
player.left();
}
break;
case KeyEvent.VK_RIGHT:
if (!player.isRight()) {
player.right();
}
break;
case KeyEvent.VK_UP:
player.up();
break;
}
}
// 키보드 해제 이벤트 핸들러
@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;
}
}
});
}
키보드 해제 이벤트 핸들러를 통해 입력이 있을 때 마다 쓰레드가 생성되지 않고,
쓰레드 간의 충돌이 발생하지 않는 움직임을 만들어 낼 수 있다.

Share article