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,但是内部的图片的大小和偏移可能会不同,所以在绘制的过程中还需要加上一定的偏移。

 

 

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