SDL游戏开发之五-解析并使用GIF

mac2022-06-30  15

在游戏开发中,使用的图片一般情况下的格式为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,但是内部的图片的大小和偏移可能会不同,所以在绘制的过程中还需要加上一定的偏移。

 

 

最新回复(0)