内容简介
1、第三部分第三课: SDL开发游戏之显示图像
2、第三部分第四课预告: SDL开发游戏之事件处理
第三部分第三课:SDL开发游戏之显示图像
上一课中,我们学习了如何加载SDL库(SDL_Init),释放SDL库(SDL_Quit),如何打开一个窗口(Window),如何使用表面(Surface)。
这些都是SDL库最最基本的操作。暂时,我们只会给窗口自带的表面上点颜色,好像挺乏味的。
这一课我们来学习如何插入图片。上一课我们说过,SDL中绘制图样需要在Surface上进行。Surface就好像一个画板。所以,我们来学习如何在Surface上“贴上”图片。
然而,SDL的库默认只能载入BMP格式的图片,那我们要载入其他格式(PNG,JPG,GIF等)的图片怎么办呢?
我们就需要使用到SDL_image这个SDL的插件(或称扩展)。我们会学习如何给SDL库安装插件。
还将学习如何设置图片的透明度。
老的SDL版本一般是1.2版,但是目前已经不再怎么更新了吧。SDL官网最新的稳定版本(截止2015年8月13日)是SDL2.0.3,有人说2.0.4版也快发布了。
SDL1.2到SDL2.0,是一个很大的飞跃,增加了很多新的元素,性能也增强了很多。
不过可惜的是SDL2的API不向后兼容,不过一般来说SDL1.2编写的程序要迁移到SDL2.0,改动是不太大的。官网也有migration(迁移)的教程贴(全是英语,又一次“论学好英语对编程的帮助”,虽不是必须,但做编程不会英语是很可惜的):
http://wiki.libsdl.org/MigrationGuide
推荐一个不错的百度贴吧:SDL吧
http://tieba.baidu.com/f?kw=sdl&ie=utf-8
如果你英语还不错,那么SDL官网的WiKi毫无疑问是最好的老师了,所有你想知道的SDL的知识几乎都在WiKi里:
注:小编会在Mac OS下的XCode上用SDL2来编写演示下面的课程。其他平台(Windows,Linux等)类似,就是环境配置略有不同,也会讲解如何配置。SDL具有可移植性。
加载BMP格式的图片
加载图片是开发游戏很基本很重要的一点,因为游戏中的精灵(sprite)是由一张张图片构成的。
基础的SDL库是比较简单的,为了节省代码量,它只支持加载一种图片格式:BMP,这种图片的后缀是 .bmp。
虽然我们平时用的图片格式是PNG和JPG居多,但是BMP也是一种常用的格式。
BMP取自位图BitMaP的缩写,也称为DIB(与设备无关的位图),是微软视窗图形子系统(Graphics Device Interface)内部使用的一种位图图形格式,它是微软视窗平台上的一个简单的图形文件格式。
BMP文件通常是不压缩的,所以它们通常比同一幅图像的压缩图像文件格式要大很多。例如,一个800×600的24位几乎占据1.4MB空间。因此它们通常不适合在因特网或者其他低速或者有容量限制的媒介上进行传输。但是BMP文件被电脑读取也快一些,毕竟不需要解压缩这一步。
但是也不要担心,正如开头我们所说,我们可以通过给SDL添加一个插件SDL_image,获得加载其他各种图片格式的能力。
加载BMP图片
要加载BMP图片,在SDL中我们只需要调用SDL_LoadBMP函数。
函数原型如下:
SDL_Surface* SDL_LoadBMP(const char* file)
这个函数很简单,只有一个参数,就是一个字符串,指明要加载的BMP图片的路径。
返回值则是一个SDL_Surface的指针。
我们就来加载下面这个BMP图像试试吧(下面的图片是JPG格式的,因为BMP格式太大,有1.4MB,所以我制作成了JPG格式,小很多。大家可以下载这个图片,然后转存为BMP格式,或者用自己的图片):
代码如下:
#include <SDL2/SDL.h>
#include <unistd.h>
/* 窗口宽和高 */
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
/* 窗口标题 */
#define WINDOW_TITLE "SDL窗口"
/* 窗口 */
SDL_Window* window = NULL;
/* 窗口自带的表面(Surface) */
SDL_Surface* screen = NULL;
/* 加载BMP图片后返回的表面 */
SDL_Surface* image = NULL;
int main(int argc, char* args[])
{
if(SDL_Init(SDL_INIT_VIDEO) < 0) { // 如果初始化SDL失败
printf("SDL2 不能被初始化,SDL2_Error : %s\n", SDL_GetError());
} else {
// 创建宽800像素,高600像素的窗口。
window = SDL_CreateWindow(WINDOW_TITLE,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH,
WINDOW_HEIGHT,
SDL_WINDOW_SHOWN);
// 取得窗口自带的表面
screen = SDL_GetWindowSurface(window);
// 加载BMP图片
image = SDL_LoadBMP("/Users/enmingxie/Desktop/TestSDL2/ubuntu.bmp");
// 将BMP图片的表面贴到窗口自带的表面上
SDL_BlitSurface(image, NULL, screen, NULL);
// 刷新窗口表面
SDL_UpdateWindowSurface(window);
}
pause(); // 暂停
SDL_FreeSurface(image); // 释放BMP图片表面
SDL_DestroyWindow(window); // 释放窗口
SDL_Quit(); // 释放SDL
return 0;
}
运行结果如下图所示:
我们的BMP图片的原始像素是800 x 600,所以正好填充满整个窗口(窗口也是 800 x 600)。
上面代码通过注释应该不难理解,不过我们还是讲一下其中的一个函数SDL_BlitSurface。
这个函数的作用是将一个表面贴到另一个表面上。
我们创建一个窗口(window)后,窗口自带一个表面,可以由SDL_GetWindowSurface取得。这个表面如果不做设定,默认是没有颜色的(也就是黑色)。如果把这个表面看成一张黑色的纸面,那么我们还可以创建很多其他大大小小的纸面(Surface),黏贴到这张纸面上。
函数原型如下:
int SDL_BlitSurface(SDL_Surface* src, const SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect)
这个函数接受四个参数:
src |
源表面,也就是要黏贴的那个表面 |
srcrect |
一个SDL_Rect结构体,代表了黏贴的表面的位置和宽高;如果设定为NULL,那么就是拷贝整个表面 |
dst |
目的表面,也就是被黏贴的那个表面 |
dstrect |
一个SDL_Rect结构体,代表了被贴的表面的位置和宽高;如果设定为NULL即可,那就是从表面左上角开始拷贝 |
返回值:
0 :如果黏贴成功。
一个负值:代表错误信息,如果失败。可以调用SDL_GetError函数来获得错误信息。
顺便介绍一下SDL_Rect结构体的定义:
Rect是rectangle的前四个字母,就是英语“矩形”的意思。所以SDL_Rect就是SDL的矩形的结构体定义。
在SDL源代码中,此结构体的定义如下:
typedef struct{
Sint16 x, y;
Uint16 w, h;
} SDL_Rect;
这个结构体包含四个参数:
x |
矩形的左上角的横坐标值 |
y |
矩形的左上角的纵坐标值 |
w |
矩形的宽 |
h |
矩形的高 |
给应用添加图标
通常我们创建一个应用程序(游戏也是一种应用程序),都会给它添加自定义的图标。一般我们都说图标是icon,是英语“图标”的意思。
如果没有添加图标,那么应用被添加到桌面或者是任务栏时都会是空白的,很难看。一般来说,添加的图标也会显示在打开应用时的窗口的左上角,增加美观度。
那么我们如何给窗口添加图标呢?
用SDL_SetWindowIcon函数就可以。
函数原型如下:
void SDL_SetWindowIcon(SDL_Window* window, SDL_Surface* icon)
这个函数没有返回值,接受两个参数,第一个参数不难理解,就是要添加图标的那个窗口,第二个参数是加载图标的图片后返回的表面。
第二个参数的类型又是SDL_Surface* 这个指针,所以我们可以用加载BMP图像来得到图标所对应的那个表面。
我们要添加的图标如下:
完整代码如下:
#include <SDL2/SDL.h>
#include <unistd.h>
/* 窗口宽和高 */
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
/* 窗口标题 */
#define WINDOW_TITLE "SDL窗口"
/* 窗口 */
SDL_Window* window = NULL;
/* 窗口自带的表面(Surface) */
SDL_Surface* screen = NULL;
/* 加载BMP图片后返回的表面 */
SDL_Surface* image = NULL;
/* 加载图标后返回的表面 */
SDL_Surface* icon = NULL;
int main(int argc, char* args[])
{
if(SDL_Init(SDL_INIT_VIDEO) < 0) { // 如果初始化SDL失败
printf("SDL2 不能被初始化,SDL2_Error : %s\n", SDL_GetError());
} else {
// 创建宽800像素,高600像素的窗口。
window = SDL_CreateWindow(WINDOW_TITLE,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH,
WINDOW_HEIGHT,
SDL_WINDOW_SHOWN);
// 加载图标的图片
icon = SDL_LoadBMP("/Users/enmingxie/Desktop/TestSDL2/mushroom.bmp");
// 设置窗口图标
SDL_SetWindowIcon(window, icon);
// 取得窗口自带的表面
screen = SDL_GetWindowSurface(window);
// 加载BMP图片
image = SDL_LoadBMP("/Users/enmingxie/Desktop/TestSDL2/ubuntu.bmp");
// 将BMP图片的表面贴到窗口自带的表面上
SDL_BlitSurface(image, NULL, screen, NULL);
// 刷新窗口表面
SDL_UpdateWindowSurface(window);
}
pause(); // 暂停
SDL_FreeSurface(image); // 释放BMP图片表面
SDL_DestroyWindow(window); // 释放窗口
SDL_Quit(); // 释放SDL
return 0;
}
运行一下代码。
在Mac OS中,虽然在xCode中运行时不会在窗口左上角产生图标,但是应用的图标是会改变成这只萌萌的大蘑菇的(再看我,再看我就把你吃掉...):
如果是在Windows中,那么是会在窗口左上角出现这个蘑菇的图标的。
使用SDL_image插件,加载更多类型图片
SDL默认只支持加载BMP类型的图片,虽说这种类型的图片在游戏开发中可以使游戏加载更快(不需要对图片进行解压缩),但是今天我们在网络上传播时一般使用JPG,PNG,GIF等图片格式。
SDL存在不少插件,这些插件就好像平时我们用浏览器,例如Chrome,火狐之类,也会有add-on(称为扩展或插件),我们只需要安装到浏览器,就能获得额外的功能。SDL的基本功能虽然很强大,但是如果有了更多插件的配合,则威力更甚。
SDL的一些插件的汇总页面:
http://www.libsdl.org/projects/
在这个页面,有一个项目,SDL_image,就是我们此处要使用的插件:
只要安装了这个插件,那么SDL在BMP图片格式以外,还可以支持:GIF, JPEG, LBM, PCX, PNG, PNM, TGA, TIFF, WEBP, XCF, XPM, XV 这些图片格式。
我们来到SDL_image的主页:
http://www.libsdl.org/projects/SDL_image/
我们所要安装的包是Development Libraries对应的那个,不是Runtime Binaries对应的那个。Development Libraries是英语“开发库”的意思,就是我们做开发的时候需要用到的库。Runtime Binaries是英语“运行时二进制文件”的意思,是安装以后可以供运行应用的安装包,只用于运行,不用与开发。
Windows下安装SDL_image插件
一般在Windows中我们使用SDL做开发,会用到的IDE是Codeblocks或者Visual C++(也包含在Visual Studio中)。
Codeblocks安装SDL_image插件
下载那个 SDL2_image-devel-2.0.0-mingw.tar.gz 的文件,解压。
-
打开你的SDL项目,点击菜单栏中的 项目->属性
-
点击 Project's Build Options... 按钮
-
添加 -lSDL2_image
然后,如果系统是32位Windows的,则把对应的那些DLL文件都放到 C:\WINDOWS\SYSTEM32 文件夹下;如果是64位Windows系统,则把对应的那些DLL文件都放到 C:\Windows\SysWOW64 文件夹下。
Visual C++安装SDL_image插件
下载那个 SDL2_image-devel-2.0.0-VC.zip的文件,解压。
打开你的SDL项目,点击 项目 -> 属性:
然后,如果系统是32位Windows的,则把对应的那些DLL文件都放到 C:\WINDOWS\SYSTEM32 文件夹下;如果是64位Windows系统,则把对应的那些DLL文件都放到 C:\Windows\SysWOW64 文件夹下。
小编我发现了,Windows下面开发SDL是最麻烦的... 在Linux和Mac OS X下SDL的各种配置都没有Windows下面那么麻烦,也是醉了。
怪不得好多读者在Windows下一直配置SDL2不成功,而且64位Windows和32位Windows的配置也有差异,Win XP,Win 7和Win8什么的也有差异。
Linux下安装SDL_image插件
如果你使用的是Ubuntu,Fedora,CentOS之类常用的Linux发行版,那么安装很简单。只需要用命令行来安装即可:
-
Debian一族或Ubuntu中安装SDl_image插件:
sudo apt-get install libsdl2-image-dev
-
Redhat一族或Fedora或CentOS中安装SDl_image插件:
sudo yum install SDL2_image-devel
如果你的Linux发行版没有如上所示的包管理软件可以帮你快速安装SDL_image插件,那也不要急,你可以下载其源码,自己编译,安装:
-
下载源代码
去SDL_image的页面,下载那个 Source 下面的随意一个,一般推荐下载tar.gz的那个:
-
解压文件
(如果下载的是那个zip文件,那么也可以用对应解压软件解压)
tar zxvf SDL_image-2.0.0.tar.gz
-
编译源码
cd SDL_image-2.0.0
./configure
make-
安装
sudo make install
Mac下安装SDL_image插件
下载Development Libraries(不是Runtime Binaries)下的那个 SDL2_image-2.0.0.dmg 文件。
打开Finder,也可以随便打开哪个文件夹,同时按下 Cmd + Shift + G这三个键,就会打开一个对话框,输入 /Library/Frameworks
点击“前往”,就到了 /Library/Frameworks 这个文件夹中。
双击下载的SDL2_image-2.0.0.dmg文件,会解压为如下所示:
将其中的 SDL2_image.framework 这个文件拷贝到/Library/Frameworks 这个文件夹中,会让你输入管理员密码。输入后,确定,拷贝完毕:
然后进入你的xCode中的SDL的项目,点击项目名称,就会显示设置页。可以看到Link Binary With Libraries 那栏目前只有一个SDL2.framework(此前我们创建SDL2的项目时添加的),我们需要添加 SDL2_image.framework。点击那个加号,如下图:
点击+号之后会弹出如下窗口,点击 Add Others:
然后用老方法,同时按下 Cmd + Shift + G这三个键,就会打开一个对话框,输入 /Library/Frameworks
点击Go,进入/Library/Frameworks,选择SDL2_image.framework,点击Open;或者双击SDL2_image.framework文件也可以:
SDL2_image.framework就添加到我们xCode的SDL项目的链接库中了,可以看到多了一项:
如何调用SDL_image的内容
要想调用SDL_image库的内容,其实很简单,只需要在程序的开头处插入一个预处理命令:
Linux或者Windows平台如下:
#include <SDL/SDL_image.h>
Mac OS X与其他平台不一样,使用:
#include <SDL2_image/SDL_image.h>
加载图片
一般来说,只需要使用一个函数即可:IMG_Load,这个函数可以接受任意格式(只要是SDL_image支持的格式)的图片作为参数。
函数原型:
SDL_Surface* IMG_Load(const char *file)
可以看到与之前加载BMP图片的SDL_LoadBMP函数是很类似的。唯一的参数就是图片的路径,返回值也是一个SDL_Surface指针。
我们来试着加载一个PNG格式的图片(以Mac OS X下为例,所以如果是其他平台,记得将代码中的#include <SDL2_image/SDL_image.h> 改为 #include <SDL/SDL_image.h>):
我们要载入的PNG图片如下:
代码如下:
#include <unistd.h>
#include <SDL2/SDL.h>
#include <SDL2_image/SDL_image.h>
/* 窗口宽和高 */
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
/* 窗口标题 */
#define WINDOW_TITLE "SDL窗口"
/* 窗口 */
SDL_Window* window = NULL;
/* 窗口自带的表面(Surface) */
SDL_Surface* screen = NULL;
/* 加载BMP图片后返回的表面 */
SDL_Surface* image = NULL;
/* 加载图标后返回的表面 */
SDL_Surface* icon = NULL;
/* 加载PNG图片后返回的表面 */
SDL_Surface* pngImage = NULL;
/* PNG图片的矩形:左上角横、纵坐标,宽,高 */
SDL_Rect positionPNG;
int main(int argc, char* args[])
{
if(SDL_Init(SDL_INIT_VIDEO) < 0) { // 如果初始化SDL失败
printf("SDL2 不能被初始化,SDL2_Error : %s\n", SDL_GetError());
} else {
// 创建宽800像素,高600像素的窗口。
window = SDL_CreateWindow(WINDOW_TITLE,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH,
WINDOW_HEIGHT,
SDL_WINDOW_SHOWN);
// 加载图标的图片
icon = SDL_LoadBMP("/Users/enmingxie/Desktop/TestSDL2/mushroom.bmp");
// 设置窗口图标
SDL_SetWindowIcon(window, icon);
// 取得窗口自带的表面
screen = SDL_GetWindowSurface(window);
// 加载BMP图片
image = SDL_LoadBMP("/Users/enmingxie/Desktop/TestSDL2/ubuntu.bmp");
// 将BMP图片的表面贴到窗口自带的表面上
SDL_BlitSurface(image, NULL, screen, NULL);
// 加载PNG图片
pngImage = IMG_Load("/Users/enmingxie/Desktop/TestSDL2/frog.png");
// 设置PNG图片的矩形
positionPNG.x = 125; // 左上角横坐标
positionPNG.y = 100; // 左上角纵坐标
positionPNG.w = 150; // 宽150像素
positionPNG.h = 193; // 宽193像素
// 将PNG图片的表面贴到窗口自带的表面上
SDL_BlitSurface(pngImage, NULL, screen, &positionPNG);
// 刷新窗口表面
SDL_UpdateWindowSurface(window);
}
pause(); // 暂停
SDL_FreeSurface(image); // 释放BMP图片表面
SDL_DestroyWindow(window); // 释放窗口
SDL_Quit(); // 释放SDL
return 0;
}
运行,结果如下:
图片的透明度
设置图片的透明度(transparency)在开发游戏中十分常用。因为当我们有好多表面一张贴着一张时,有时候就需要设置某一些图片背景透明,不然会很难看。
有两种设置透明度的方式:
-
Color Keying:也就是采用设置三原色的方式,对图片的红,绿,蓝分别设置。使用函数:SDL_SetColorKey
函数原型:int SDL_SetColorKey(SDL_Surface* surface, int flag, Uint32 key)
surface
要更改的图片对应的表面指针
flag
如果是SDL_TRUE,则激活color key设置;如果是 SDL_FALSE,则不能设置color key
key
透明度像素
代码样例:
上面的代码将图片的蓝色完全变成透明,也就是完全抹去。大家可以用背景为蓝色的图片试一下。
SDL_Surface* loadedSurface = IMG_Load("图片路径");
SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0, 0, 0xFF ));
}-
Alpha混合:英语是Alpha Blending。使用函数SDL_SetSurfaceAlphaMod。
函数原型:
int SDL_SetSurfaceAlphaMod(SDL_Surface* surface, Uint8 alpha)
surface
要更改的图片对应的表面指针
alpha
alpha值,0~255之间的整数值。255表示完全不透明;0表示完全透明;所以数值从255到0透明度依次递增
总结
SDL中可以在Surface(表面)上载入图片,默认SDL只能载入BMP格式的图片。
我们可以安装SDL_image这个第三方库或者插件,使得SDL除了默认的BMP图片格式外,还能加载很多其他图片格式。
我们可以用SDL_SetColorKey来设置图片上某一种(几种)颜色的透明(也就是不显示)。
我们可以用SDL_SetSurfaceAlphaMod来设置图片整体的透明度,从255~0的整数范围内取值。255表示完全不透明,0表示完全透明(图片消失)。
第三部分第四课预告:
今天的课就到这里,一起加油吧。
下一次我们学习: SDL开发游戏之事件处理