본문 바로가기

프로그래밍/libGDX

[libGDX] 입력 처리

입력을 다루는 방법들

키보드, 마우스, 터치, 게임패드등 입력장치를 통해 사용자 입력이 들어왔을때 이를 다루는 방법은 크게 2가지가 있습니다. 하나는 Polling이고, 다른 하나는 Listener 입니다. 두가지 방법은 각자의 장점과 단점을 가지고 있어서 상황에 따라 어떤 방법을 사용하는게 좋을지 선택하는게 중요합니다.


Polling

Polling을 이용하면 원하는 부분에서 현재 input의 상태를 알 수 있습니다. 현재 어떤 키가 눌려있는지, 터치 또는 클릭되고 있는 좌표가 어디인지 등을 알 수 있습니다. Listener을 이용하는 것에 비해 쉽고 빠르게 사용할 수 있기 때문에 아케이드 게임같은 장르에서 유용합니다. 

주의: 하지만 Polling을 사용하면 입력 이벤트를 놓칠 수 있습니다. 가령 플레이어가 한 키를 20번 연타한다고 했을때, Polling으로 갖어온 이벤트는 20번이 아니라 18, 15번 등, 더 적을 수 있습니다. 왜냐하면 Polling을 polling하는 순간의 입력 상태만을 갖어오기 때문에 polling과 polling 사이에 상태가 빠르게 변하면 그것을 잡아낼 수 없기 때문입니다.

키보드 Polling

현재 키보의 특정 키가 눌리고 있는지 확인하고 싶으면 다음과 같이 하면 됩니다.

isAPressed = Gdx.input.isKeyPressed(Keys.A);

이렇게 하면 A키가 눌리고 있는지 알 수 있습니다. isKeyPressed가 인자로 받는 Keys 값들은 링크에서 확인할 수 있습니다.


마우스 / 터치 Polling

현재 터치가 되고 있는지 확인하고 싶으면 다음과 같이 하면 됩니다.

boolean isTouched = Gdx.input.isTouched();

터치 polling은 멀티터치를 지원합니다. n번째 터치가 되고 있는지 확인하려면 다음과 같이 하면 됩니다.

boolean firstFingerTouching = Gdx.input.isTouched(0);

boolean secondFingerTouching = Gdx.input.isTouched(1);

boolean thirdFingerTouching = Gdx.input.isTouched(2);


  1. 첫번째 손가락이 터치를 했다면 0 -> true가 됩니다.
  2. 그 다음 두번째 손가락이 터치를 했다면 1 -> true가 됩니다.
  3. 그 다음 세번째 손가라이 터치를 했다면 2 -> true 가 됩니다.
  4. 그 다음 두번째 손가락을 띄었다면 1 -> false가 됩니다. 여기서 중요한 것은 이제 2개의 손가락이 터치되고 있지만 true인 터치 번호들은 0, 2라는 점입니다.

터치의 상태가 변했는지 알고 싶으면 다음과 같이 하면 됩니다.

boolean justTouched = Gdx.input.justTouched();

이 메쏘드는 polling 중간에 입력 상태가 빠르게 바뀌어도 확인할 수 있습니다. 하지만 어떻게 바뀐것인지는 알 수 없습니다.

현재 터치하고 있는 좌표를 알고 싶으면 다음과 같이 하면 됩니다.

int firstX = Gdx.input.getX();

int firstY = Gdx.input.getY();

int secondX = Gdx.input.getX(1);

int secondY = Gdx.input.getY(1);

현재 마우스 버튼이 눌리고 있는지 알고 싶으면 다음과 같이 하면 됩니다.

boolean leftPressed = Gdx.input.isButtonPressed(Input.Buttons.LEFT);

isButtonPressed가 인자로 받는 Buttons는 링크에서 확인할 수 있습니다.


Event Listener

일반적으로 polling보다 listener를 이용하는 것을 추천합니다. 꼭 polling을 사용해야만 하는 경우가 아니라면 listener을 사용하는 것이 좋습니다.

Listener을 사용하면 사용자 입력을 놓치지 않습니다. 또한 MVC 모델과 같이 Controller 부분을 완전히 분리할 수 있기 때문에 코드 관리가 쉬워지고 플랫폼에 따른 기능 세분화가 편리해집니다. 플랫폼에 따라 다른 기능을 수행하도록 하고자 할때 polling과의 코드를 비교해 보겠습니다.

Polling을 사용할때
if (Gdx.app.getType()) {
   case Android:
      // android specific code
      break;
   case Desktop:
      // desktop specific code
   case WebGl:
      // HTML5 specific code
      break;
   default;
      // Other platforms specific code
}
// 이 코드를 input을 헨들링할때마다 작성해야 한다.

Listener을 사용할때
if (Gdx.app.getType()) {
   case Android:
      Gdx.input.setInputProcessor(new AndroidInput());
      break;
   case Desktop:
      Gdx.input.setInputProcessor(new DesktopInput());
      break;
   cast Webgl:
      Gdx.input.setInputProcessor(new WebglInput());
      break;
   default:
      Gdx.input.setInputProcessor(new DefaultInput());
}
// 이 코드는 처음 한번만 사용되고, 
// 플랫폼마다 input 클래스를 하나씩 만들면 된다.


Input Processor

Libgdx는 Input에 배한 Listener 인터페이스가 제공됩니다. 이 인터페이스는 InputProcessor라는 이름으로 주어집니다. InputProcessor에서 주어지는 메쏘드들은 링크에서 확인할 수 있습니다. keyDown(), keyUp(), keyType()등과 같은 키보드 이벤트 관련 메쏘드들과 touchDown(), touchUp(), touchDragged(), touchMoved(), scrolled()와 같은 터치 및 마우스 이벤트 관련 메쏘드들이 주어집니다. 이 인터페이스를 이용하여 커스텀 InputProcessor 클래스를 만든 후 setInputProcessor를 이용하여 등록하면 나중에 입력 이벤트가 들어오면 만들어둔 InputProcessor에서 이벤트가 처리됩니다.

Gdx.input.setInputProcessor(new MyInputProcessor());

InputMultiplexer

하나의 InputProcessor만 사용할 수 있는 것은 아닙니다. 다수의 InputProcessor를 사용하고 싶으면 InputMultiplexer를 이용하면 됩니다.

InputMultiplexer multiplexer = new InputMultiplexer();

multiplexer.addProcessor(new MyUiInputProcessor());

multiplexer.addProcessor(new MyGameInputProcessor());

Gdx.input.setInputProcessor(multiplexer);


Scene2d와 Event Listener

위에서 InputProcessor를 이용한 Event Listener을 열심히 설명했지만, Libgdx의 Scene2d를 사용한다면 더욱 하이레벨의 Event Listener을 이용할 수 있습니다. Scene2d의 핵심 클래스는 Stage를 보면 InputAdapter클래스를 상속받고 있고, InputAdapter는 InputProcessor 인터페이스를 구현하고 있는것을 볼 수 있습니다. 그래서 다음과 같이 사용할 수 있습니다.

Stage stage = new Stage();

Gdx.input.setInputProcessor(stage);

그러면 stage에서 입력 이벤트가 처리됩니다. stage에서 처리되는 방식은 일반적인 scene graph 프레임워크에서 입력 이벤트가 처리되는 방식과 비슷합니다.

Actor 클래스는 addListener(Event Listener listener) 라는 메쏘드를 가지고 있습니다. 이것을 이용하여 listener를 등록하면 입력 이벤트가 Actor로 들어왔을때 해당 listener가 이벤트를 처리하게 됩니다. 터치나 클릭같은 이벤트는 Actor의 hit(float x, float y, boolean touchable) 메쏘드를 이벤트를 이용하여 이벤트의 좌표가 어떤 Actor에 해당하는 것인지 확인후 그 Actor에게 이벤트가 들어갑니다.

가령 어떤 버튼을 만들고 버튼을 클릭했을때 클릭된 좌표를 출력하고 싶으면 다음과 같이 하면 됩니다.

Button button = new Button(skin);
button.addListener(new ClickListener() {
   clicked(InputEvent event, float x, float y) {
      System.out.println("x: " + x);
      System.out.println("y: " + y);
   }
});

여기서는 ClickListener을 사용했습니다. Libgdx에서는 상황에 맞게 사용할 수 있는 다양한 입력 관련 listener들을 제공하고 있습니다.

  • InputListener: 가장 기본적인 입력 리스너입니다. keyDown(), touchDown()와 같이 InputProcessor에서 제공하는 메쏘드들을 그대로 제공합니다.
  • ClickListener: 클릭을 감지하는 리스너입니다. 버튼에 사용하면 유용합니다.
  • ChangeListener: 변화를 감지하는 리스너입니다. 슬라이더와 같이 입력에 따라 변하는 부분에 사용하면 유용합니다.
  • FocusListener: 키보드 포커스의 변화를 감지하는 리스너입니다. 입력 필드가 여러개일때 키보드 포커스가 바뀔때 마다 뭔가 할때 유용하게 사용할 수 있습니다.
  • DragListener: 드래그를 감지하는 리스터입니다. InputListener에서 TouchDrag()를 지원하긴 하지만 이 클래스는 dragStart와 dragStop과 같이 drag에 관련된 유용한 메쏘드들을 더 제공합니다.
  • ActorGestureListener: 터치 제스처를 감지할 수 있습니다. 핀치 줌, 롱 터치, 패닝, 플링 등을 처리할 수 있습니다.

이와 같이 Libgdx에서는 다양한 입력 클래스들을 제공하고 있습니다.

주의: Actor에서 키보드 입력을 받으려면 stage의 setKeyboardFocus를 이용하여 Actor에게 포커스를 줘야만 합니다.


마치면서 

Libgdx는 유용한 입력 관련 클래스들을 많이 제공하고 있습니다. 특히나 핀치 줌, 패닝과 같은 하이레벨 터치 제스처까지 지원하고 있으니 할말 다 했다고 할 수 있죠. 이러한 강력한 입력 클래스들을 잘 활용하면 플랫폼마다 최적화된 환경을 제공할 수 있습니다. 하이레벨 클래스들을 이용하면 터치좌표 일일히 따져가면서 핀치 줌같은 기능을 구현할 필요가 없는것이지요! 또한 게임패드입력 처리까지 처리해서 거의 완벽한 입력 시스템을 갖추고 있다고 할 수 있습니다.