한 번에 하나의 키 입력만 처리되므로, '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