본문 바로가기

Study/Engine

[SDL Tutorial] Lesson 9 - 마우스 이벤트



 오늘은 마우스 이벤트에 대해서 강의를 해볼까 합니다. 어렵지 않으니 쉽게 하실 수 있을거에요.


9 - 1 : Mouse Event

 마우스 동작으로 일어날 수 있는 이벤트의 종류는 크게 3가지 입니다. 마우스가 움직일 때의 이벤트(MOUSEMOTION), 마우스의 버튼이 눌렸을 때의 이벤트(MOUSEBUTTONDOWN), 마우스의 버튼이 눌린상태에서 버튼을 땠을 때의 이벤트(MOUSEBUTTONUP)가 있지요. 이 3가지 이벤트는 이전에 배웠던 SDL_PollEvent 함수를 통해서 알 수 있습니다. 그리고 마우스 커서의 현재 위치에 대해서는 event.motion이나 event.button 으로 알 수 있습니다.


9 - 2 : Sheldon's Reaction



 빅뱅이론에 나오는 Sheldon의 반응(?)을 담은 예제 프로그램입니다. 한참 이름을 고민하다가 이전 포스팅에서 SDL Dock을 만들 때 썼던 것이 기억나서 한번 써봤습니다.

// Sheldon 클래스
class Sheldon{
// Sheldon 버튼의 크기를 담을 Rect
SDL_Rect box;

// 화면에 보여질 sprite의 위치와 크기를 담을 Rect
SDL_Rect *clip;
public:
// 생성자
Sheldon(int x, int y, int w, int h);

// 버튼에 대한 마우스 이벤트를 처리하는 함수
void handle_events();

// Sheldon을 화면에 뿌리기 위한 함수
void show();
};

 Sheldon을 화면에 뿌려주고 어떤 역할을 해야할 지 설정해놓은 클래스입니다. 화면에 실제로 뿌려질 크기와 sprite의 크기의 데이터를 담을 SDL_Rect가 필요하겠지요? 생성자로 클래스에 필요한 자원을 초기화 하고, 이벤트 처리와 화면 출력을 담당하는 함수를 정의했습니다.

void set_clips()
{
    // sprite 영역 설정
    clips[ CLIP_MOUSEOVER ].x = 320;
    clips[ CLIP_MOUSEOVER ].y = 240;
    clips[ CLIP_MOUSEOVER ].w = 320;
    clips[ CLIP_MOUSEOVER ].h = 240;

    clips[ CLIP_MOUSEOUT ].x = 0;
    clips[ CLIP_MOUSEOUT ].y = 240;
    clips[ CLIP_MOUSEOUT ].w = 320;
    clips[ CLIP_MOUSEOUT ].h = 240;

    clips[ CLIP_MOUSEDOWN ].x = 0;
    clips[ CLIP_MOUSEDOWN ].y = 0;
    clips[ CLIP_MOUSEDOWN ].w = 320;
    clips[ CLIP_MOUSEDOWN ].h = 240;

    clips[ CLIP_MOUSEUP ].x = 320;
    clips[ CLIP_MOUSEUP ].y = 0;
    clips[ CLIP_MOUSEUP ].w = 320;
    clips[ CLIP_MOUSEUP ].h = 240;
}

 우리가 이번에 쓴 sprite는



 이것인데요, sprite의 각 영역을 설정해놓은 것을 알 수 있을겁니다.

Sheldon::Sheldon(int x, int y, int w, int h)
{
// 버튼 인자 값 설정
box.x = x;
box.y = y;
box.w = w;
box.h = h;

// 기본 sprite 설정
clip = &clips[CLIP_MOUSEOUT];
}

 생성자에서는 Sheldon 버튼의 x, y위치와 가로, 세로의 크기를 초기화해주고 기본 sprite를 설정해주게 했습니다.

void Sheldon::handle_events()
{
    // 마우스 위치
    int x = 0, y = 0;

    // 만약 마우스가 움직이면,
    if( event.type == SDL_MOUSEMOTION )
    {
        // 현재 마우스의 위치 값을 받아옴
        x = event.motion.x;
        y = event.motion.y;

        // 마우스 커서가 Sheldon위에 있으면
        if( ( x > box.x ) && ( x < box.x + box.w ) && ( y > box.y ) && ( y < box.y + box.h ) )
        {
            // sprite를 MOUSEOVER로 설정
            clip = &clips[ CLIP_MOUSEOVER ];
        }
        // 그게 아니라면
        else
        {
            // sprite를 MOUSEOUT으로 설정
            clip = &clips[ CLIP_MOUSEOUT ];
        }
    }

 이벤트를 제어하는 함수입니다. 먼저 마우스가 움직일 때 어떤 동작을 할 지 정하는 부분입니다. 마우스가 움직이게 되면 SDL_MOUSEMOTION라는 이벤트가 발생합니다. 그렇게되면 일단 마우스가 움직일 당시의 위치를 얻어와서 그 마우스 위치가 Sheldon 버튼 안에 있다면 sprite를 OVER로 바꾸고, 아니라면 OUT으로 설정하게 되어있습니다.

        // 만약 마우스 버튼이 눌렸으면
if( event.type == SDL_MOUSEBUTTONDOWN )
{
// 왼쪽 마우스 버튼이 눌렸으면
if( event.button.button == SDL_BUTTON_LEFT )
{
// 눌렸을 때의 마우스 커서 위치 값을 얻어옴
x = event.button.x;
y = event.button.y;

// 마우스가 Sheldon위에 있으면
if( ( x > box.x ) && ( x < box.x + box.w ) && ( y > box.y ) && ( y < box.y + box.h ) )
{
// sprite를 MOUSEDOWN으로 설정
clip = &clips[ CLIP_MOUSEDOWN ];
}
}
}

 마우스가 눌렸을 때는 SDL_MOUSEBUTTONDOWN 이벤트가 발생합니다. 우리는 왼쪽 버튼이 눌렸을 때의 이벤트를 원하므로 SDL_BUTTON_LEFT인지도 확인했습니다. 다른 플래그는 SDL 문서를 참조하시기 바랍니다.

// 만약 마우스 버튼을 땠으면
if( event.type == SDL_MOUSEBUTTONUP )
{
// 왼쪽 마우스 버튼을 땠으면
if( event.button.button == SDL_BUTTON_LEFT )
{
// 위치를 받아와서
x = event.button.x;
y = event.button.y;

// Sheldon의 영역인지 확인한 후
if( ( x > box.x ) && ( x < box.x + box.w ) && ( y > box.y ) && ( y < box.y + box.h ) )
{
// sprite를 MOUSEUP으로 설정
clip = &clips[ CLIP_MOUSEUP ];
}
}
}
}

 마우스의 버튼을 땠을 때 일어나는 이벤트가 SDL_MOUSEBUTTONUP 입니다. 이것도 어느쪽 버튼에서 일어난 이벤트인지 알 수가 있습니다.

 이 예제는 이벤트로 마우스의 위치 값을 얻었지만, 좀 더 효율적인 방법이 있습니다. SDL_GetMouseState() 라는 함수로 가능한데, 쓰는 법은 검색해보시기 바랍니다(결코 제가 귀찮아서가 아닙니다(?)).

void Sheldon::show()
{
    // Sheldon을 보여줌
    apply_surface( box.x, box.y, sprite, screen, clip );
}

 Sheldon을 화면에 출력해주는 함수입니다.

set_clips();

Sheldon mySheldon(170, 120, 320, 240);

 main 함수 윗부분에 모든 것을 초기화하고 파일을 로드한 후 이 코드를 넣어주셔야 합니다. sprite의 영역과 Sheldon을 생성해줍니다.

// 사용자가 종료하지 않을 동안 도는 루프
while( quit == false )
{
// handle에 이벤트가 있다면,
if( SDL_PollEvent( &event ) )
{
// Sheldon(Button)의 마우스 이벤트를 다룸
mySheldon.handle_events();

        // 사용자가 X 버튼을 눌렀다면,
if( event.type == SDL_QUIT )
{
// 프로그램 종료 (루프 종료)
quit = true;
}
}

// 하얀색으로 화면을 채움
SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) );

// Sheldon을 화면에 보여줌
mySheldon.show();

// 화면 갱신
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
}

 그리고 메인 루프에서 위에서 구현했던 함수들을 적절한 위치에 넣어주시면 됩니다. 이벤트를 제어하는 함수는 SDL_PollEvent가 있는 if문 안에 넣어주시면 되고, Sheldon을 화면에 출력해주는 부분은 배경색을 뿌려주는 함수 다음에 넣어주시면 됩니다.

 일반적으로 이벤트를 처리할 때 while문을 쓰지만, 이번 예제(그리고 이전 예제)에서는 if문으로 썼습니다. 이런식으로 쓰게 되면 프레임당 하나의 이벤트를 처리하므로 이벤트 처리를 보기가 쉬워집니다. while문을 대부분의 프로그램에서 이벤트 처리에 쓰는 이유는 대기열에 모든 프레임에 발생하는 모든 이벤트를 처리할 수 있기 때문입니다.

 그리고 일부 빠른 컴퓨터에서는 MOUSEUP 때의 sprite가 출력되지 않을 수 있습니다. MOUSEUP 이벤트가 일어나는 시간은 정말 짧기 때문인데, 프로그램을 20fps로 돌려주면 보일 것입니다. 이 부분은 나중에 프레임레이트의 평균화에 대해 배울 때 다뤄볼 것입니다.

 이것으로 이번 강의는 마치겠습니다. 오늘 했던 예제 프로그램의 소스코드는


 여기에 있으니 받아서 실행해보시기 바랍니다. 다음 강의는 Key State(키보드 버튼의 상태)에 대해 해보겠습니다.


- Lesson 9을 마무리하며...

 오랜만에 쓰는 강의고, 밖에서 넷북으로 하다보니 생각보다 시간이 오래걸린 것 같습니다. 무엇보다 이미지 구하는 것이 가장 어려웠네요 (하하;) 다음에도 열심히 봐주세요 ^^

'Study > Engine' 카테고리의 다른 글

OpenGL 튜토리얼 사이트  (2) 2011.05.08
[SDL Tutorial] Lesson 10 - Key State  (4) 2010.03.16
[SDL Tutorial] Lesson 8 - Key Press  (4) 2010.01.07
[SDL Tutorial] Lesson 7 - 글꼴  (4) 2010.01.04
[SDL Tutorial] Lesson 6 - 스프라이트(Sprite)  (8) 2009.12.19