SDL遊戲開發之五-解析並使用GIF

在遊戲開發中,使用的圖片一般情況下的格式爲PNG,在我看來PNG有着以下幾個好處:

  1. 不錯的壓縮比
  2. alpha通道(支持透明)
  3. 無損壓縮

以上的幾個特性使得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,它的使用過程如下:

  1. 調用該函數獲取到了2.gif的幀個數;
  2. 根據個數創建了一個數組,
  3. 再次調用該函數,把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,但是內部的圖片的大小和偏移可能會不同,所以在繪製的過程中還需要加上一定的偏移。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章