본문 바로가기

Study/Engine

[SDL Tutorial] Lesson 2 - 이미지의 로드와 화면 출력 최적화

사용자 삽입 이미지


 안녕하세요~ 이번이 두번째 강의가 되는군요 ㅎㅎ 리눅스에서의 국산 온라인 게임을 꿈꾸며 살고 있는 온새미입니다. 이번 강의는 전보다 더 편리하게 이미지를 불러오게 하는 것과 화면에 뿌려주는 것에 대해 해볼까 합니다. 이 강의를 보시기 전에 이전에 강의를 보시지 않으신 분들은 아래의 링크에서 보시고 오시기 바랍니다.


 그럼, 본격적으로 시작해보겠습니다!




2 - 1 : 이미지의 로드와 화면 출력 최적화 코드

사용자 삽입 이미지

결과화면!



 강의를 보고 소스를 치면서 따라하실 분께서는, 저번 강의 때 썼던 소스를 써보시기 바랍니다. [다운로드]
추가하는 소스는 빨강색으로 표시하겠습니다.

#include "SDL/SDL.h"
#include <string>


 여기에서 string 헤더를 추가한 이유는 나중에 이미지 로드하는 함수에서 쓸 std::string 때문이지요.

// 화면에 필요한 구성요소
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;


 프로그래밍을 할 때 그저 숫자로 쓰는 것보다 이렇게 문자로 치환해주는게 좋습니다. 다른 사람들과 팀 작업을 할 때 이러한 약속을 할 수가 있으니까요~

// 화면에 쓰일 변수들
SDL_Surface *message = NULL;
SDL_Surface *background = NULL;
SDL_Surface *screen = NULL;


 screen은 저번 강의 때도 설명했지만, 화면을 표현할 변수입니다. 꼭 screen이라고 쓸 필요는 없고, 마음대로 정해도 됩니다. 그리고 message는 말그대로 메시지를 출력할 이미지를 담는 변수고 background는 배경화면을 담을 변수입니다.

SDL_Surface *load_image( std::string filename )
{

    // 불러온 이미지를 임시로 담아줄 변수 
    SDL_Surface* loadedImage = NULL;
    // 최적화된 이미지를 저장하는 변수 
    SDL_Surface* optimizedImage = NULL;


 위의 함수는 이미지를 불러오는 함수입니다. 기존에 쓰던 SDL_LoadBMP을 보다 쉽고 편리하게 쓰기 위해서 새로 정의해봅니다. loadedImage는 불러올 이미지를 담아주는 임시 변수이고, optimizedImage는 loadedImage에 있는 이미지를 최적화하여 담을 변수입니다.

    // 이미지 로드 
    loadedImage = SDL_LoadBMP( filename.c_str() );


 SDL_LoadBMP를 이용하여 이미지 파일을 불러옵니다. 하지만 불러온 이미지를 바로 사용하지 말아야 합니다. 그 이유는 BMP가 24비트이기 때문이죠. 화면은 대부분 32비트인데, 다른 화면에 있는 다른 비트의 것을 뿌리는 것은 그다지 좋지 않습니다. 이유는 SDL을 할 때 느려질 가능성이 있기 때문입니다.

    // loadedImage에 제대로 로드가 됬다면,
    if( loadedImage != NULL ) {
        // 최적화한 이미지를 optimizedImage에 생성 
        optimizedImage = SDL_DisplayFormat( loadedImage );
       
        // loadedImage를 메모리에서 해제 
        SDL_FreeSurface( loadedImage );
    }


 SDL_DisplayFormat는 현재 화면에 맞게 이미지를 최적화 시켜줍니다. 즉, 24비트로 불러진 BMP는 현재 화면의 비트인 32비트로 최적화가 되었다는 소리지요.
SDL_FreeSurface로 이제 더이상 필요없는 loadedImage를 메모리에서 해제시켜줍니다.

    // 최적화된 이미지를 반환 
    return optimizedImage; 
}


 새롭게 최적화 된 버전으로 반환해줍니다.

void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination )
{
    // 임시로 사각형 구조체를 선언 
    SDL_Rect offset;
   
    // 함수에서 받은 인자값을 대입 
    offset.x = x;
    offset.y = y;


 이번 함수는 화면으로 출력시켜주는 역할을 합니다. 함수에서 인자로 받은 x, y값에 따라서 화면상에 원하는 위치에 출력이 가능합니다. SDL_Rect는 x, y, w(width), h(height)로 구성되어있습니다.

    // 화면으로 출력 
    SDL_BlitSurface( source, NULL, destination, &offset ); 
}


 SDL_BlitSurface는 실제로 화면에 뿌려주는 함수입니다.
첫번째 인자값은 화면에 출력시킬 원본이미지(source)를 가르킵니다.
두번째 인자는 원본이미지의 어느부분을 출력할 것인가를 정해주는 것인데, 그냥 원본이미지의 전부를 출력하고 싶다면 NULL을 써야 합니다. 두번째 인자를 쓸 경우는 스프라이트 애니메이션을 할 때 자주 사용합니다.
세번째 인자값은 원본이미지가 출력될 목적지(destination)입니다. 주로 화면(screen)으로 정해놓은 변수가 들어갑니다.
네번째 인자값은 아까 위에서 세팅했던 사각형 구조체의 주소값을 넣는 곳입니다. 아까 offset.x와 offset.y에 값을 넣은 이유가 바로 화면상의 x, y위치에 원본이미지를 출력하고자 함이었습니다.

int main(int argc, char* argv[])
{


 메인 함수는 위처럼 쓸 수도 있지만, int main(int argc, char** argv) 이런식으로도 가능합니다. 그리고 int main() 이나 void main() 으로도 쓰셔도 무방합니다.

    // SDL 초기화 
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 ) {
        return 1;
    }


 SDL을 시작하기 위한 함수인 SDL_Init입니다. 함수의 인자를 SDL_INIT_EVERYTHING 으로 하게되면, SDL의 서브시스템들인 비디오, 오디오, 타이머 등등을 사용할 준비를 하게 도와줍니다. 만약, SDL 초기화에 실패하게되면 프로그램을 종료하게 했습니다.

    // 화면 셋업
    screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);


 위에서 미리 정의해둔 것들을 1, 2, 3번째 인자에 넣어줍니다.

    // 화면 세팅에 문제가 있는지 검사 
    if( screen == NULL ) {
        return 1;
    }


 세팅한 screen이 문제가 있다면 프로그램을 종료시켜줍니다.

    // 윈도우의 제목을 설정해줍니다.
    SDL_WM_SetCaption( "SDL Lesson2", NULL );


 제목을 설정해주면 아래와 같이 표현됩니다.

사용자 삽입 이미지


    // 이미지 로드 
    message = load_image( "Message.bmp" );
    background = load_image( "Bg.bmp" );


 위에서 정의한 함수인 load_image를 사용하여 각각 bmp파일을 불러옵니다.

    // 배경을 화면에 출력 
    apply_surface( 0, 0, background, screen );


 위와 같이 화면에 출력해주게되면 아래와 같이 나옵니다.

사용자 삽입 이미지


    apply_surface( 320, 0, background, screen );
    apply_surface( 0, 240, background, screen );
    apply_surface( 320, 240, background, screen );


 그래서 같은 것을 3개 더 출력시켜줍니다.

    // 화면에 메시지를 출력 
    apply_surface( 180, 140, message, screen );


 불러온 message 이미지를 x:180, y:140에 출력시켜줍니다. SDL에서의 좌표는 우리가 수학하면서 배웠던 좌표와는 조금 다릅니다.

사용자 삽입 이미지

출처 : Lazy Foo' Productions



 그래서 그림처럼 표현이 됩니다.

사용자 삽입 이미지


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


 여기에서 SDL_Flip을 쓰지 않는다면, 화면엔 아무 변화도 없을 것입니다. SDL_Flip은 screen에 출력된(저장된) 이미지들을 실제 화면으로 보여주는 역할을 합니다. 화면을 갱신하는데 문제가 있다면 프로그램을 종료합니다.

    // 2초간 정지
    SDL_Delay( 2000 );


 SDL_Delay는 첫번째 강의때에도 설명했었지만, 밀리세컨드로 입력받은 시간만큼 프로그램을 일시정지(Pause) 시켜주는 역할을 하는 함수입니다. 현재 이 예제 프로그램에서 저 함수가 없다면 우리는 갱신된 화면을 볼 새도 없이 프로그램이 할일을 다 하고 종료했을 것입니다.

    // 불러왔던 이미지를 메모리에서 해제
    SDL_FreeSurface( message );
    SDL_FreeSurface( background );

    // SDL 끝
    SDL_Quit();

    return 0;
}


 프로그램을 종료하기 위해서 메모리상에 올려두었던 데이터를 정리하는 부분입니다. SDL_FreeSurface과 SDL_Quit는 저번에도 설명해드렸죠. 다시한번 언급하자면, 함수명 그대로 Surface를 Free(해제)시켜주는 것이고, Quit(종료)시켜주는 함수들이죠. 그다지 어려울 것 없다고 봅니다 ㅎㅎ


이번 강의는 여기까지 하도록 하겠습니다. 처음에 포스팅할 땐 추가하는 부분이 별로 없을 줄 알고 추가부분은 빨강색으로 표시한다고 위에 써놓았었는데, 막상 다 쓰고 보니 거의 다 빨강색을 썼더군요. 그만큼 이번에 새로운 내용이 많았다는 것일까요?ㅎㅎ

 아래 첨부된 파일은 오늘 강의한 프로그램의 소스코드입니다.


다음 강의는 SDL의 확장기능에 대해 해볼까 합니다. 많은 관심좀...




- Lesson 2를 마무리하며...

 두번쨰 쓰는 강의라 그런지 속도가 붙나 싶었는데 결국은 시궁창으로 흘러들었습니다 ㅇ<-<
Lazy Foo' Productions 사이트에 있는 걸 번역해보면서 하다가 결국은 제 저질같은 영어실력에 못이겨서 제가 알고 있는대로 써내려갔습니다. 간혹 좀 애매하게 알고있는 부분은 번역기를 돌려가면서(...) 했지요. 그래도 이렇게 저도 공부하면서 하니까 즐겁네요! 다만 지금 다쓰고나니 긴장이 풀려서 바로 엄청난 피곤함이 몰려오긴 했습니다만 ^^;
아, 그리고 키보드 얻어오고나서 프로그래밍은 만족했습니다만 게임쪽에서는 시궁창을 경험했습니다(...) 이 전 포스팅에서도 언급했었지만, 당분간은 게임 안하려구요 ㅇ<-<
어쨌든! 이 글이 SDL을 입문하시는 여러분께 도움이 되길 간절히 빌어봅니다 ^^

사용자 삽입 이미지

아, 생각보단 도움이 안될지도 모르겠군요...