티스토리 뷰

SFML

[SFML] 이벤트 다루어보기

권벡터 2025. 1. 10. 00:00

이 포스트에서 알 수 있는 것

  • sf::Event
  • sf::Event::getIf<T>
  • sf::WindowBase::handleEvents
  • sf::Event::Closed
  • sf::Event::Resized
  • sf::Event::FocusLost
  • sf::Event::TextEntered
  • sf::Event::KeyPressed
  • sf::Event::KeyReleased
  • sf::Event::MouseWheelScrolled
  • sf::Event::MouseButtonPressed
  • sf::Event::MouseButtonReleased
  • sf::Event::MouseMoved
  • sf::Event::MouseMovedRaw
  • sf::Event::MouseEntered
  • sf::Event::MouseLeft
  • sf::Event::JoystickMoved
  • sf::Event::JoystickConnected
  • sf::Event::JoystickDisconnected

1. 기본적인 SFML 이벤트 처리 로직

1.1. sf::Event::getIf<T>  (이벤트 선별 방식)

getIf는 내가 받고자 하는 이벤트가 수신 됐을 때 해당 타입을 반환하는 멤버함수이다. 

아래 코드를 보면 `키보드 누름` 이벤트가 들어왔을 경우, Event::KeyPressed  타입의 keyPressed 변수를 정의하여

어떤 입력값이 들어오는 지 판별하여 처리하도록 로직을 만들 수 있다. 

while (window.isOpen())
{
    while (const std::optional event = window.pollEvent())
    {
        // 키가 눌러졌을 때 이벤트 반환
        if (const auto* keyPressed = event->getIf<sf::Event::KeyPressed>())
        {
            if (keyPressed->scancode == sf::Keyboard::Scancode::Escape)
                window.close();
        }
        // 키가 떼어졌을 때 이벤트 반환
        else if (const auto* keyReleased = event->getIf<sf::Event::KeyReleased>())
        {
            if (keyPressed->scancode == sf::Keyboard::Scancode::Space)
        		std::cout << "Released Space bar." << std::endl;
        }
    }


}

 

1.2. sf::WindowBase::handleEvents (이벤트 처리 로직을 람다함수로 만들고 등록)

두번째 방식은 이벤트 처리 로직을 람다 형식으로 정의하고 이를 window.handleEvents()에 등록하는 것이다.

해당 함수의 인수의 제한은 없다.

다만 동일한 인수를 사용하는 서로 다은 람다함수들을 등록할 경우, 컴파일 오류가 발생한다.

 window.handleEvents(람다1, 람다2, 람다3,...);
// 각 이벤트 람다 함수 정의 
const auto onClose = [&window](const sf::Event::Closed&)
{
    window.close();
};

const auto onKeyPressed = [&window](const sf::Event::KeyPressed& keyPressed)
{
    if (keyPressed.scancode == sf::Keyboard::Scancode::Escape)
        window.close();
};

while (window.isOpen())
{
	// 
    window.handleEvents(onClose, onKeyPressed);

    // Rest of the main loop
}

 

1.3. 두 방법의 차이점 

1.1 번 방식은 window.pollEvent() 를 통해 폴링된 이벤트 변수를 받아서 그 이벤트의 입력 방식, 입력 버튼을 추출하는 방식이라 어떻게 보면 절차 지향적인 성격을 가지고 있다.

1.2 번 방식은 이벤트 처리 로직을 람다 함수에 구현하고 구현된 람다함수를 이벤트 핸들에 등록하는 방식이라 객체지향적인 성격을 가지고 있다. 

성능 상으로 두 방식의 차이는 없어보이고 각자 선호하는 코딩 스타일에 맞게 적용하면 되지 않을까 싶다.

 

필자는 1.2 방식을 더 선호하는데 그 이유는 

  • 구현하고 안쓰는 이벤트는 이벤트 핸들에 구현한 람다함수만 수정하면 되어 주석처리하는 번거로움이 없다.
  • 20개 이상의 if ~else 로직을 관리할 자신이 없다. (분명 실수 할거다.)

 


2. 주요 이벤트 함수들 

2.1. sf::Event::Closed

SFML window가 닫혔을 때, 해당 이벤트가 트리거 된다.

if (event->is<sf::Event::Closed>())
    window.close();

 

2.2. sf::Event::Resized

window.setSize() 함수가 실행 될 때 해당 이벤트가 트리거 된다. 

if (const auto* resized = event->getIf<sf::Event::Resized>())
{
    std::cout << "new width: " << resized->size.x << std::endl;
    std::cout << "new height: " << resized->size.y << std::endl;
}

2.3. sf::Event::FocusLost & sf::Event::FocusGained

SFML window가 현재 포커스 되었는지의 상태를 반환해준다. 해당 이벤트 실행 주기는 단발성이다. 

if (event->is<sf::Event::FocusLost>())
    myGame.pause();

if (event->is<sf::Event::FocusGained>())
    myGame.resume();

2.4. sf::Event::TextEntered

키보드 문자가 입력되었을 때 이벤트가 트리거된다.

해당 이벤트는 `KeyPressed` 이벤트랑 다르다.(혼동하면 안된다.)

트리거 된 문자 데이터는 기본적으로 유니코드 형식으로 반환된다. 

이를 sf::String 타입으로 바꾸어서 사용 할 수 있으니 사용 방법은 이 링크를 참고하자.

if (const auto* textEntered = event->getIf<sf::Event::TextEntered>())
{
    if (textEntered->unicode < 128)
        std::cout << "ASCII character typed: " << static_cast<char>(textEntered->unicode) << std::endl;
}

2.5. sf::Event::KeyPressed & sf::Event::KeyReleased

키보드 입력이 들어왔을 때 트리거 되는 이벤트들이다. 

해당 이벤트의 트리거 방식은 두 가지가 존재한다.

  1. 버튼을 한번만 눌렀다 떼었을 때 : 이벤트가 한번만 들어온다.
  2. 버튼 하나를 계속 누르고 있을 때(hold): 일정 시간 경과 후, 연속적으로 이벤트가 들어온다. 

만약 게임에서 플레이어 컨트롤 기능을 구현할 때, 일부 기능은 의도에 맞지 않게 구현될 수 있다.

예를들어 플레이어 이동 기능을 만든다고 했을 때, 우리는 방향키를 계속 눌러서 그 방향으로 캐릭터가 이동하도록

만들고 싶은데 위에서 설명했듯이 일정시간 딜레이 후에 연속 이벤트가 들어온다. 

이것이 조작감 부분에서 치명적일 수 있어서 다른 방법을 찾아 보는게 좋을 것 같다. 

 

키보드 버튼 홀드 상태일 때 이벤트 트리거 설정을 끌 수 있는 방법이 있다.

window.setRepeatEnabled(false);
if (const auto* keyPressed = event->getIf<sf::Event::KeyPressed>())
{
    if (keyPressed->scancode == sf::Keyboard::Scan::Escape)
    {
        std::cout << "the escape key was pressed" << std::endl;
        std::cout << "scancode: " << static_cast<int>(keyPressed->scancode) << std::endl;
        std::cout << "code: " << static_cast<int>(keyPressed->code) << std::endl;
        std::cout << "control: " << keyPressed->control << std::endl;
        std::cout << "alt: " << keyPressed->alt << std::endl;
        std::cout << "shift: " << keyPressed->shift << std::endl;
        std::cout << "system: " << keyPressed->system << std::endl;
        std::cout << "description: " << sf::Keyboard::getDescription(keyPressed->scancode).toAnsiString() << std::endl;
        std::cout << "localize: " << static_cast<int>(sf::Keyboard::localize(keyPressed->scancode)) << std::endl;
        std::cout << "delocalize: " << static_cast<int>(sf::Keyboard::delocalize(keyPressed->code)) << std::endl;
    }
}

2.6. sf::Event::MouseWheelScrolled

마우스 휠이 움직여 졌을 때 해당 이벤트가 트리거 된다.

if (const auto* mouseWheelScrolled = event->getIf<sf::Event::MouseWheelScrolled>())
{
    switch (mouseWheelScrolled->wheel)
    {
        case sf::Mouse::Wheel::Vertical:
            std::cout << "wheel type: vertical" << std::endl;
            break;
        case sf::Mouse::Wheel::Horizontal:
            std::cout << "wheel type: horizontal" << std::endl;
            break;
    }
    std::cout << "wheel movement: " << mouseWheelScrolled->delta << std::endl;
    std::cout << "mouse x: " << mouseWheelScrolled->position.x << std::endl;
    std::cout << "mouse y: " << mouseWheelScrolled->position.y << std::endl;
}

2.7. sf::Event::MouseButtonPressed & sf::Event::MouseButtonReleased

마우스 버튼이 조작되었을 때 해당 이벤트가 트리거 된다. 

트리거 이벤트 종류는 다섯 종류가 있다.

  1. 마우스 왼쪽 버튼
  2. 마우스 오른쪽 버튼
  3. 마우스 중앙 버튼
  4. 커스텀 마우스 버튼 #1
  5. 커스텀 마우스 버튼 #2
if (const auto* mouseButtonPressed = event->getIf<sf::Event::MouseButtonPressed>())
{
    if (mouseButtonPressed->button == sf::Mouse::Button::Right)
    {
        std::cout << "the right button was pressed" << std::endl;
        std::cout << "mouse x: " << mouseButtonPressed->position.x << std::endl;
        std::cout << "mouse y: " << mouseButtonPressed->position.y << std::endl;
    }
}

2.8. sf::Event::MouseMoved

마우스 커의 위치가 변경되었을 때 해당 이벤트가 트리거 된다.

if (const auto* mouseMoved = event->getIf<sf::Event::MouseMoved>())
{
    std::cout << "new mouse x: " << mouseMoved->position.x << std::endl;
    std::cout << "new mouse y: " << mouseMoved->position.y << std::endl;
}

2.9. sf::Event::MouseMovedRaw

마우스가 너무 작은 거리로 이동하여 인지할 수 없는 경우에도 창 내에서 마우스가 이동할 때 트리거 된다.

sf::Event::MouseMoved의 위치 값은 화면 해상도에 따라 달라지지만, 이 이벤트의 원시 데이터는 그렇지 않다.

물리적 마우스가 너무 적게 이동하여 화면의 커서가 최소 한 픽셀이라도 이동하지 않으면 sf::Event::MouseMoved 이벤트는 생성 되지 않는 반면, 센서 해상도와 무관하게 마우스에서 생성된 모든 이동 정보는 항상 sf::Event::MouseMovedRaw 이벤트를 생성한다.

if (const auto* mouseMovedRaw = event->getIf<sf::Event::MouseMovedRaw>())
{
    std::cout << "new mouse x: " << mouseMoved->delta.x << std::endl;
    std::cout << "new mouse y: " << mouseMoved->delta.y << std::endl;
}

2.10. sf::Event::MouseEntered & sf::Event::MouseLeft

마우스가 SFML window 안에 들어왔는지의 유무가 트리거 된다.

if (event->is<sf::Event::MouseEntered>())
    std::cout << "the mouse cursor has entered the window" << std::endl;

if (event->is<sf::Event::MouseLeft>())
    std::cout << "the mouse cursor has left the window" << std::endl;

2.11. sf::Event::JoystickButtonPressed & sf::Event::JoystickButtonReleased

조이스틱 버튼 입력이 들어왔을 때 트리거 된다.

SFML은 32개의 조이스틱 버튼을 지원한다.

if (const auto* joystickButtonPressed = event->getIf<sf::Event::JoystickButtonPressed>())
{
    std::cout << "joystick button pressed!" << std::endl;
    std::cout << "joystick id: " << joystickButtonPressed->joystickId << std::endl;
    std::cout << "button: " << joystickButtonPressed->button << std::endl;
}

2.12. sf::Event::JoystickMoved

조이스틱 커서가 이동됐을 때 트리거 된다.

SFML은 8가지의 조이스틱 입력축을 지원한다. 

입력축: X, Y, Z, R, U, V, POV X, POV Y

if (const auto* joystickMoved = event->getIf<sf::Event::JoystickMoved>())
{
    if (joystickMoved->axis == sf::Joystick::Axis::X)
    {
        std::cout << "X axis moved!" << std::endl;
        std::cout << "joystick id: " << joystickMoved->joystickId << std::endl;
        std::cout << "new position: " << joystickMoved->position << std::endl;
    }
}

2.13. sf::Event::JoystickConnected & sf::Event::JoystickDisconnected

조이스틱 연결 상태가 변경 되었을 때 트리거 된다.

if (const auto* joystickConnected = event->getIf<sf::Event::JoystickConnected>())
    std::cout << "joystick connected: " << joystickConnected->joystickId << std::endl;

if (const auto* joystickDisconnected = event->getIf<sf::Event::JoystickDisconnected>())
    std::cout << "joystick disconnected: " << joystickDisconnected->joystickId << std::endl;
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함