在遊戲開發中,使用的圖片一般情況下的格式爲PNG,在我看來PNG有着以下幾個好處:
- 不錯的壓縮比
- alpha通道(支持透明)
- 無損壓縮
以上的幾個特性使得PNG在遊戲開發中大放異彩。另外,主流就是使用PNG+TexturePacker,把碎圖整合成一張大的圖片(一般是POT Power Of Two,圖片的寬和高是2的冪),然後再在程序中使用。這樣做的好處主要是爲了兼容舊的設備、方便對齊處理以及字節對齊。
SDL支持一些主流的圖片格式,比如png、jpg等。而這其中並不包括GIF。
表情包經常會出現各種各樣的動圖,這種大多是GIF。一個GIF格式的圖片文件中內部一般都會有着多於一幀的圖片,由於GIF格式的特性,使得它在每隔一段時間會切換當前顯示的圖片,以達到動畫的效果,這也是目前的2D遊戲中的動畫的最常用的形式。
如上圖,則是一個標準的動圖。
雖然SDL官方並沒有提供一個解析GIF的方式,不過網上是有着對應的庫的。
這個並不是我開發的,是一個外國人,好像從2006年起就沒有更新過了,在以前心血來潮,就發了封郵件給SDL官方,官方說並不提供 animated gif,然後推薦了個網址,源文件是以SDL1.x寫的,因爲現在的主流是SDL2,所以就稍微改變了下代碼。
SDL_AniGIF是我更改後的文件,SDL_AniGIF-1.0.0是原先的文件,具體關於gif解碼的,我就不多說了(我也不會。。)見附件
在遊戲開發中,一般的動畫是每隔一段時間就切換圖片,如果第一張和倒數第二張圖片有着明顯連貫性的時候,就會感覺到非常流暢,就比如上面貼的關羽的圖片。
SDL_angif會先判斷這個文件有着幾幀,之後在申請到足夠的空間後調用相應的函數後,就會得到一個幀數組,它裏面包含着每一幀圖片以及對應的座標(在這裏指的是SDL_Surface,如果使用的SDL 2.x,建議轉換成SDL_Texture)以及該圖片的持續時間。注意需要預先設置好SDL2的開發環境。
代碼:
#include <iostream>
#include <SDL.h>
#include <cstdio>
#include <vector>
#include "SDL_anigif.h"
using namespace std;
首先包含到這個例子所需要的庫文件和外國熱心網友寫的gif解析庫。
int main(int argc,char**argv)
{
SDL_Event event;
bool g_bRunning = true;
int currentFrame = 0;
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window*gWin = SDL_CreateWindow("gif test",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,480,SDL_WINDOW_SHOWN);
SDL_Renderer*gRen = SDL_CreateRenderer(gWin,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);
使用SDL的時候,需要先初始化SDL庫(SDL_Init),接着創建一個窗口(SDL_CreateWindow)和渲染器(SDL_CreateRenderer)。
//先獲取最大數量
int number = AG_LoadGIF("2.gif",NULL,0);
AG_Frame*frames = new AG_Frame[number];
AG_LoadGIF("2.gif",frames,number);
SDL_angif庫僅僅提供了幾個簡單的函數,而用得最多的就是AG_LoadGIF,它的使用過程如下:
- 調用該函數獲取到了2.gif的幀個數;
- 根據個數創建了一個數組,
- 再次調用該函數,把GIF的圖片加載到內存之中。
//由frames創建texture
std::vector<SDL_Texture*> textures;
for(int i=0;i<number;i++)
{
SDL_Texture*texture = SDL_CreateTextureFromSurface(gRen,frames[i].surface);
textures.push_back(texture);
}
SDL 1.x使用的是SDL_Surface進行圖片的繪製;而在SDL 2.x後,就轉而創建了一個新的結構體SDL_Texture,代替了SDL_Surface的大部分功能。
SDL_Rect destRect = { 0,0,0,0 };
SDL_Rect srcRect = { 0, 0, 0, 0 };
SDL_SetRenderDrawColor(gRen,255,200,100,255);
while(g_bRunning)
{
//①
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
g_bRunning = false;
break;
}
}
//②
SDL_RenderClear(gRen);
destRect.x = frames[currentFrame].x;
destRect.y = frames[currentFrame].y;
srcRect.w = destRect.w = frames[currentFrame].surface->w;
srcRect.h = destRect.h = frames[currentFrame].surface->h;
SDL_RenderCopy(gRen, textures.at(currentFrame), &srcRect, &destRect);
SDL_RenderPresent(gRen);
SDL_Delay(frames[currentFrame].delay);
currentFrame = (currentFrame + 1) % number;
}
無論什麼遊戲,其底層一般都是一個大的循環體,在這個循環體的內部則進行繪製以及事件響應等。
在①處註釋中,這個循環體是爲了從事件隊列中拿取事件,使用循環的原因是要在每次循環時把事件隊列取空,以避免延遲響應。
在②處註釋後,則是具體的繪製代碼,SDL_RenderClear()的功能是清空畫布;而SDL_RenderPresent()則是把已經繪製的圖片顯示到畫布上。由於SDL內部提供了雙緩衝機制,所以一般不需要自己再實現雙緩衝機制。
在SDL_RenderClear()和SDL_RenderPresent()的中間進行調用繪製函數進行繪製即可。
在之前的AG_LoadGIF中,已經讀取到了圖片,所以在這個時候只需要調用SDL_RenderCopy()繪製圖片即可。srcRect表示從源圖片中按照srcRect進行提取;然後把取出的圖片繪製到destRect大小的矩形之中。
雖然是同一個GIF,但是內部的圖片的大小和偏移可能會不同,所以在繪製的過程中還需要加上一定的偏移。