스프라이트 애니메이션 :
게임 개체의 동작을 표현할 때 사용하는 방법, 애니메이션과 마찬가지로 움직이는 그림을 순서대로 빠른 시간 안에 이어주면 눈의 착시로 연속된 동작으로 보이게 된다. 때문에 필요한 동작 대로 그 움직이는 각 단위의 그림이 모두 필요하다. 이그림을 모두 그려준 다음, 그림의 바탕색만 빼주면 캐릭터가 배경 위에 올라온 것처럼 보이게 된다.
보통 아래 그림과 같이 연속된 동작이 일정한 크기로 구성되어져 있다.
각 동작별로 연속으로 그려주게 되면 애니메이션이 된다!
여기서는 SDL로 스프라이트 애니메이션을 그리는 법을 구현해본다.
우선 SDL을 통해 비트맵파일을 로드한다.
1 2 3 4 5 6 7 8 9 10 | typedef struct AppData { SDL_Window* window; SDL_Surface* window_surface; SDL_Surface* player_surface; } AppData; AppData ad; ad.player_surface = SDL_LoadBMP("./test.bmp"); SDL_SetColorKey(ad.player_surface, SDL_TRUE, SDL_MapRGB(ad.player_surface->format, 255, 0, 255)); | cs |
SDL_LoadBMP()는 SDL에서 비트맵을 로드하는 API로 이미지를 불러온 뒤 SDL_Surface형태로 반환한다.
기본적으로 비트맵은 투명 배경을 지원하지 않아,
위 이미지에서 보는 배경과 같이 보라색이나 특정한 색으로 설정하는 경우가 많다.
이럴 경우엔 직접 코드상으로 처리가 필요하다.
SDL_SetColorKey(ad.player_surface, 1, SDL_MapRGB(ad.player_surface->format, 255, 0, 255));
SDL에선 SDL_SetColorKey() API를 통해 비트맵 이미지 배경을 투명으로 처리할 수 있다.
1 2 3 | int SDL_SetColorKey(SDL_Surface* surface, int flag, Uint32 key) | cs |
설정을 원하는 surface를 자신이 설정한 key의 색을 투명으로 설정한다. 위 예제에서는 배경색(255,0,255)를 투명으로 설정한다.
이상태로 이미지를 그려보면 보라색 배경이 투명으로 사라진 걸 볼 수 있다.
캐릭터 애니메이션을 그리기 위해선 위와같이 불러온 서피스 이미지를 프레임마다 한동작씩 잘라서 그려주면 된다.
전체의 이미지에서 특정 크기의 이미지 부분만 화면에 그리기 위해선 SDL_BlitSurface() API를 활용한다.
'Blit'이란 그래픽스에서 한 이미지에서 다른 이미지로 복사하는 것을 뜻한다. 공식적인 정의는 데이터 배열을 목적지 배열에 복사하는 것을 의미한다.
로드한 비트맵 서피스를 윈도우 서피스로 'blit'해주면 화면에 출력되는 윈도우에 그려지는데, 이 때 특정 사이즈만 blit을 하면 잘라진 이미지가 그려지게 된다.
srcrect크기만큼 dstRect위치에 그려준다.
1 2 3 4 | int SDL_BlitSurface(SDL_Surface* src, const SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect) | cs |
우선 아래와 같이 rcRect를 전체 이미지 사이즈로 해서 전체 이미지를 올려본다.(위 이미지와 같이)
srcrect를 NULL로 하면 전체 사이즈를, dstrect를 NULL로 하면 0,0에 위치하게 된다.
1 2 3 4 | ad.window_surface = SDL_GetWindowSurface(ad.window); SDL_BlitSurface(ad.player_surface, NULL, ad.window_surface, NULL); | cs |
이제 srcrect값을 캐릭터 하나의 크기(50x50)에 맞춰서 설정을 해서 인자값을 넘겨주자.
1 2 | SDL_Rect rcSprite = {0, 0, 50, 50}; SDL_BlitSurface(ad.player_surface, &rcSprite, ad.window_surface, NULL); | cs |
잘라진 이미지가 올라간 것을 확인할 수 있다.
예제에서 rcSprite(SDL_Rect)의 x,y,w,h의 값을 각각 0,0,50,50으로 설정했는데
w,h는 올릴 이미지의 사이즈가 되고, x,y는 내가 올릴 이미지의 좌표라고 할 수 있다.
즉, 애니메이션을 설정하기 위해선 매 프레임마다 이 x좌표값을 변경해주면 된다.
매프레임 그릴때 x좌표를 변경해주기 위한 코드를 넣어보자.
우선 캐릭터의 매프레임마다 그릴 이미지를 선택하기 위한 player_idx를 선언한다.
이 값을 통해 이미지의 x좌표를 계산할 것이다.
1 | int player_idx = 0; | cs |
다음으로 매프레임 그리는 곳에 애니메이션을 위한 x값을 계산해준다.
idx값은 캐릭터 애니메이션의 갯수인 5까지 반복하며, 5가 되면 0으로 다시 처음으로 돌아간다.
그리고 이 idx값을 캐릭터의 크기인 50만큼 곱해주기만 x좌표가 구해진다(0->50->100->150->200)
1 2 3 4 | player_idx ++; if(player_idx >= 5) player_idx = 0; rcSprite.x = 50 * player_idx; | cs |
SDL_BlitSurface()에 계산한 rcSprite값을 넘겨주면 애니메이션을 하게 된다.
준비한 비트맵의 경우 한방향으로만 달리고 있는게 아닌 상/하/좌/우 모두 움직이고 있다. 이에 맞게 방향키를 눌렀을 때 원하는 방향의 애니메이션을 설정해줘보자.
구현은 애니메이션을 그리는 방법과 같다. 각 방향키에 해당하는 입력이 왔을 때 y값을 해당하는 위치의 애니메이션에 맞게 설정만 해주면 된다.
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 32 33 34 | int player_state = 0;//y값 변경을 위한 값 선언 ////////////main loop/////////////////// SDL_Event event; if(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: isQuit = SDL_TRUE; break; case SDL_KEYUP: switch(event.key.keysym.scancode) { case SDL_SCANCODE_LEFT: player_state = 0; break; case SDL_SCANCODE_RIGHT: player_state = 1; break; case SDL_SCANCODE_UP: player_state = 2; break; case SDL_SCANCODE_DOWN: player_state = 3; break; } break; } } } rcSprite.y = 50 * player_state; | cs |
SDL_PollEvent()를 통해 키보드 입력을 위한 이벤트를 받고 각 방향에 해당하는 이벤트가 입력으로 들어왔을 경우(SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN), y값 변경을 위한 'player_state'값을 변경해준다.
이후 키보드 입력을 통해 방향별로 애니메이션을 하는 것을 확인 할 수 있다.
전체 코드는 아래에서 확인할 수 있다.
https://github.com/huiyueun/GraphicsStudy/tree/master/sprite_sample
'Software Development > Graphics' 카테고리의 다른 글
Premultiplied alpha (0) | 2018.10.30 |
---|---|
텍스쳐 맵 (2) | 2018.09.15 |
프레임 드롭현상과 대책 (3) | 2018.08.18 |
프레임과 FPS (0) | 2018.08.04 |
OpenGL Texture 그리기 (0) | 2018.08.04 |