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