C++로 하드웨어 장치에 저수준 접근이 가능한 SDL2 라이브러리로 이미지를 로드하는 예제이다.
크래스 플랫폼이고 그래픽 적으로는 OpenGL과 Direct3D를 사용하기 때문에 공부하기에도 좋은 라이브러리이다.
동영상 프로그램이나 에뮬레이터를 만드는데도 사용되었을 만큼 상당히 저수준 하드웨어에서 안정적으로 작동하는 라이브러리다.
이름 그대로 Simple DirectMedia Layer 단순히 직접적으로 하드웨어에 접근가능한 것을 목적으로 하였기 때문에 SDL로 자신만의 게임 엔진을 만들어 사용하는 사람도 많다.
나온지 꽤 되었기 때문에 안정화된 버전이다.
대체적으로 사용하기 편하다. C로도 사용할 수 있고 C++의 클래스와 함께 사용할 수 있다.
파이썬에서 제공하는 PYGAME 라이브러리는 C로 작성된 SDL의 Wrapper 클래스이다. 문법을 쉽게 만들어서 사용자 장벽을 낮춘 것은 신의 한수 였는데 아무래도 파이썬의 속도이다 보니까 아마추어 수준을 벗어나기 힘든 것도 사실이다. C로만든 라이브러리가 파이썬을 거쳐서 가다 보니 PC사양에 따라 하드웨어 지연이 발생하는 부분이 있다고 한다.
(그런 불만 사항이 있지만 파이썬으로 짜면 1만줄 코드가 3000줄 정도로 줄어든다고 한다)
C++ 에서 SDL을 사용하기 위한 유일한 문제는 처음 설치하는게 까다로울 수 있다. 필자도 오랜만에 설치했는데 한시간은 넘게 헤메인 것 같다;;; 다음에 설치할 때는 시간을 줄이자는 취지에서 여기 기록을 남겨둔다.
IDE 를 사용하지 않아도 개발이 가능하겠으나 생산성을 위해서 IDE를 사용하는게 좋다. C++ 이고 Direct3D를 사용하니까 가장 좋은 IDE는 MS의 비주얼 스튜디오라고 생각하지만 이 포스팅에서는 비주얼 스튜디오 코드(Visual Studio Code)와 MinGW 32bit 버전을 사용한다.
SDL2의 설치에 관련해서는 아래 포스팅을 참고한다.
C++ 게임 라이브러리 | SDL2 설치와 테스트 (MinGW 32bit) | 비주얼 스튜디오 코드 (tistory.com)
Simple DirectMedia Layer - SDL version 2.0.12 (stable) (libsdl.org)
*SDL 로 진행할 예제는 아래의 윈도우 창 처럼 이미지를 로드한다. 파일 포맷은 png다.
우선 SDL 이 설치되지 않았다면 위에 SDL2 설치와 테스트에서 SDL2를 설치하고 온다.
그런 다음 이미지 확장 라이브러리를 다운로드 받아야 한다.
SDL만 설치하면 bmp 파일밖에 사용하지 못한다. 아래 저자의 사이트에 들어가서 라이브러리를 받는다. SDL의 창시자인 Sam Laninga 와 Mattias Engdegard 의 투박한 웹사이트의 중간에 있다. MinGW 버전을 받는다.
www.libsdl.org/projects/SDL_image/
압축 파일을 열어보면 i686.. 이란 폴더가 32비트 라이브러리다. 이 폴더를 그대로 SDL2 가 설치된 폴더에 넣으면 된다. SDL 폴더에도 bin / include / lib 세개의 폴더가 있다.
이제 비주얼 스튜디오 코드의 환경설정을 해야한다. tasks.json의 g++ 컴파일 옵션에 -lSDL2_image 를 추가 시킨다.
설정 파일들은 이 포스트의 첨부를 확인한다.
tasks.json 파일에 쓰다보면 자잘한 실수를 할 때가 있는데 그럴때는 배치파일로 테스트해보는게 좀 더 빠르게 해결될 것이다. 배치파일에서 제대로 작동하면 json 파일로 잘 옮겨놓으면 된다.
echo new start
g++ src/main.cpp -ID:/Programming/SDL2/i686-w64-mingw32/include -LD:/Programming/SDL2/i686-w64-mingw32/lib -w -Wl,-subsystem,windows -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -o build/gameTest
build\gameTest.exe
* 첨부파일 - 설정파일이다
라이브러리가 제대로 설치되어있는지 확인할 차례다.
소스코드에서 이미지를 로드하는 부분은 SDL_Surface 와 SDL_Texture 를 사용한다. 아래 윈도우가 생성될 때 보면 이미지를 IMG_LOAD 라는 함수가 불러온다. 기존 LOAD BMP 메서드는 bmp 한개만 로드할 수 이었는데 IMG_LOAD 에서는 png, jpg 등 다양한 이미지 포맷을 불러올 수 있다.
SDL_CreateTextureFromSurface 는 이미지를 렌더러에 전달한다. 경로설정에 실패하면 실행이 되지 않으므로 주의한다. 생각보다 경로 설정때문에 발생하는 오류가 있다. 이번 포스팅은 SDL을 시작하는 의미에서 가볍게 코드를 정리했다. 모든 오류 코드를 포함하는 소스코드는 점점 가독성이 낮아진다. 그래서 프로그램의 목적과 방향성을 이해하는게 중요하다. 흐름을 알면 어떤 함수가 언제 등장하는지 알 수있다.
게임 프로그래밍은 큰 틀에서 게임루프라는 알고리즘으로 돌아가기 때문에 프로그래밍 실력을 높이는데 도움이 된다.
SDL2 과 함께 공부한다면 C++를 사용하기 때문에 도전적이다. 결과적으로 얻는게 더 많을 것이다. 요즘은 컴퓨터 프로그래밍의 분야가 너무 많아져서 한 사람이 모든 것을 마스터 하는 것은 불가능에 가깝다. 이런 쪽에 시간을 투자하면 얻는게 있어야 한다.
자바스크립트를 중심으로 프론트엔드가 유행인 현재의 추세에 비추어 보면 C++과 SDL을 사용하는 이쪽은 좀 전통적인 프로그램에 가깝다고 본다. 그러나 아직도 세계의 누군가는 계속 하고 있는 일이라는게 신기하기도 하다.
다음에 여력이 되면 SDL에 대하여 더 많은 것을 알아보겠다.
*전체 소스코드
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include <string.h>
bool init (const char* title, int xpos, int ypos, int height, int width, int flas);
void render();
void close();
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Surface* image = NULL;
SDL_Texture* texture = NULL;
int main(int argv, char** args)
{
bool game = false;
bool running = false;
game = init("SDL GAME", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH, WINDOW_HEIGHT, 0);
if(game)
{
running = true;
}
else
{
return 1;
}
SDL_Event event;
while (running)
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
{
std::cout << "Window Closing...\n";
running = false;
}
}
}
render();
}
SDL_Delay(500);
close();
return 0;
}
bool init (const char* title, int xpos, int ypos, int height, int width, int flags)
{
if(SDL_Init(SDL_INIT_EVERYTHING) >= 0)
{
window = SDL_CreateWindow(title, xpos, ypos, height, width, flags);
renderer = SDL_CreateRenderer(window, -1, 0);
image = IMG_Load("src/snowman.png");
texture = SDL_CreateTextureFromSurface(renderer, image);
}
else
{
return false; // SDL 초기화 실패
}
return true;
}
void render()
{
// SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); //Black Color
// SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
void close()
{
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}