热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

裁剪精灵图片

如果我们要做一个游戏,会涉及到大量的图片,如果这些图片都用单个文件保存,那程序里会加载大量的图片,会有很多表面,

       如果我们要做一个游戏,会涉及到大量的图片,如果这些图片都用单个文件保存,那程序里会加载大量的图片,会有很多表面,处理起来很麻烦,一般会把游戏里使用的图片分类,一类图片保存到一个大图片里,使用的时候在按要求裁剪出需要的部分,这张大图就叫精灵图,裁剪出来的部分叫精灵。

      下面我们做一个简单的例子,来演示一下如何裁剪精灵图,需要的图片有两张,第一张是背景图,随便用什么都可以,第二张是精灵图。我们会让精灵在地图上走动。

spiritbackgroundspirit

        精灵图里是一个小人各种样子,小人大小都是一样的,图片大小为96*192,所以可以计算出每个小人(精灵)大小为32*48,这样我们就可以按照这个比例抠出精灵。

SDL_Surface *gpScreen;//显示表面
SDL_Surface *gpBackGround;//背景表面
SDL_Surface *gpSpirit;//精灵表面//显示背景图片

gpBackGround
= loadImage("background.jpg");
SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL);
SDL_Flip(gpScreen);
//抠出第1副精灵的图
SDL_Rect src[4][3];src[0][0].x = src[0][0].y = 0;//精灵图左上角坐标
src[0][0].w = SPIRITWIDTH; //精灵的大小
src[0][0].h = SPIRITHEIGH;
SDL_BlitSurface(gpSpirit,
&src[0],gpScreen,NULL);//在屏幕左上角显示第一个精灵
SDL_Flip(gpScreen);

       我们定义了一个SDL_Rect类型的数组src[4][3],每一个数组元素代表了一个精灵在大图上的位置,数组的每一行代表了图片上的每一行图片,然后设置src[0][0]各个成员的值,然后把gpSpirit传输到显示表面上,不过这次我们不是将整个表面传输,而是传输src[0][0]指示的大小,所以在屏幕上显示的不是整个精灵大图,而是由src[0][0]指示的第一个精灵的图。精灵图的裁剪就是这么简单。运行效果:

image

      但精灵图的黄色背景也会显示,这会很难看,如何去除这个背景色呢?SDL中有一个函数可以设定图像的指定颜色透明。

     int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);

     参数:surface是指设定的是那个表面;

               flag的值如下表所示

flag值

含义

0取消colorkey 功能
SDL_SRCCOLORKEY以 key 为透明色。我们可以用  SDL_MapRGB() 来求得与 surface 的像素格式相符的颜色值。
SDL_RLEACCEL用RLE (Run Length Encoding) 的方式来提高 Blit 的效率。

     其中key是要设定为透明的颜色,使用SDL_MapRGB()来取,为什么不能直接指定RGB颜色值呢,因为表面的颜色格式和RGB颜色格式不同,所以必须使用这个函数来返回将RGB颜色值转换成表面格式的颜色值。

     Uint32 SDL_MapRGB(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b);

     参数:fmt指定表面;r、g、b是颜色值,你可以通过画图软件取得指定图像的颜色值。

   有了这两个函数,我们就可以将精灵大图gpSpirit的背景颜色设成透明,显示的时候就没有背景色了。修改前面的代码:

gpBackGround = loadImage("background.jpg");
SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL);
SDL_Flip(gpScreen);
gpSpirit
= loadImage("spirit.bmp");//载入精灵大图

colorkey
= SDL_MapRGB(gpSpirit->format, 255, 238, 187
);
SDL_SetColorKey(gpSpirit, SDL_SRCCOLORKEY , colorkey );

//抠出第1副精灵的图

SDL_Rect    src[4][3];

src[0][0].x = src[0][0].y = 0;//精灵图左上角坐标
src[0][0].w = SPIRITWIDTH; //精灵的大小
src[0][0].h = SPIRITHEIGH;
SDL_BlitSurface(gpSpirit,
&src[0][0],gpScreen,NULL);//在屏幕左上角显示第一个精灵
SDL_Flip(gpScreen);

  运行后显示效果:

image

  那么你是否还记得我们讲过的键盘事件检测?如果你会了键盘事件检测,我们可以做些有意思的尝试了,我们看一下精灵大图,会发现第一排小人是小人从上往下走的不同姿势,第二排从右往左走,第三排是从左往右走,第四排是从下往上走;那么我们是否可以通过键盘按键来让小人走路,转向呢?

  我们首先实现小人直行,要一直往前走,要不停的切换同一排小人图片,比如让小人从上往下走,第一幅图示src[0][0],下一步图示src[0][1],在走一步是src[0][2],那么我们可以设置一个变量step来标示下一步要出现的图片,其初值为0。

1 /*
2 功能:演示精灵图裁剪
3 作者:csl
4 日期:2012-5-11
5 */
6 #include
7 #include
8 #include "SDL.h"
9
10 #include
11
12
13 //屏幕尺寸
14 #define SCREENWIDTH 640
15 #define SCREENHEIGH 480
16 #define BPP 32
17
18 //精灵尺寸
19 #define SPIRITWIDTH 32
20 #define SPIRITHEIGH 48
21
22 SDL_Surface *gpScreen;//显示表面
23 SDL_Surface *gpBackGround;//背景表面
24 SDL_Surface *gpSpirit;//精灵表面
25
26 SDL_Event myEvent;//事件
27
28 SDL_Surface *loadImage(char *aFilename);
29 char *localeToUTF8(char *src);
30
31 int main(int argc,char *argv[])
32 {
33 int quit = 0;
34 char caption[20]={""};
35 SDL_Rect src[4][3];
36 SDL_Rect dst;
37 Uint32 colorkey;
38 int spiritX = 0;//小球的初始坐标
39 int spiritY = 0;
40 int speed = 5;
41 int step = 0;//步伐
42 int i,j;
43
44
45 if((SDL_Init(SDL_INIT_VIDEO)==-1)) //初始化视频子系统
46 {
47 printf("Unable to init SDL: %s\n", SDL_GetError());
48 exit(-1);
49 }
50 atexit(SDL_Quit);// 注册SDL_Quit,当退出时调用,使得退出时程序自动清理
51
52 //创建32位600*480窗口
53 gpScreen = SDL_SetVideoMode(SCREENWIDTH,SCREENHEIGH, BPP, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF );
54 if(!gpScreen)
55 {
56 exit(1);
57 }
58 printf("%s\n",caption);
59 SDL_WM_SetCaption(localeToUTF8("兵棋"),NULL);
60
61 //SDL_WM_SetCaption(caption,NULL);
62 SDL_EnableKeyRepeat(500,30);//起动粘连键
63
64 gpBackGround = loadImage("background.jpg");
65 SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL);
66 SDL_Flip(gpScreen);
67 gpSpirit = loadImage("spirit.bmp");//载入精灵大图
68
69 colorkey= SDL_MapRGB(gpSpirit->format, 255, 238, 187);
70 SDL_SetColorKey(gpSpirit, SDL_SRCCOLORKEY , colorkey );
71
72 //设定每一个精灵在精灵大图上的位置
73 for (i &#61; 0;i<4;i&#43;&#43;)
74 {
75 for (j &#61; 0;j<3;j&#43;&#43;)
76 {
77 src[i][j].x &#61; j*SPIRITWIDTH;
78 src[i][j].y &#61; i*SPIRITHEIGH;
79 src[i][j].w &#61; SPIRITWIDTH;
80 src[i][j].h &#61; SPIRITHEIGH;
81 }
82 }
83
84 //抠出第1副精灵的图
85 dst.x&#61;dst.y &#61; 0;
86 SDL_BlitSurface(gpSpirit,&src[0][0],gpScreen,&dst);//在屏幕左上角显示第一个精灵
87 SDL_Flip(gpScreen);
88
89 while (!quit)
90 {
91 while (SDL_PollEvent(&myEvent))
92 {
93 switch (myEvent.type)
94 {
95 case SDL_QUIT:
96 quit &#61; 1;
97 break;
98 case SDL_KEYDOWN:
99 switch(myEvent.key.keysym.sym)
100 {
101 case SDLK_DOWN:
102 spiritY&#43;&#61;speed;
103 step&#43;&#43;;
104 spiritY &#61; (spiritY&#43;SPIRITHEIGH)>SCREENHEIGH?(SCREENHEIGH-SPIRITHEIGH):spiritY;
105 break;
106 case SDLK_MINUS:
107 speed--;
108 speed&#61;speed>0?speed:0;
109 case SDLK_EQUALS:
110 speed&#43;&#43;;
111 }
112
113 step %&#61;3;
114 SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL);
115 dst.x &#61; spiritX;
116 dst.y &#61; spiritY;
117 dst.w &#61; SPIRITWIDTH;
118 dst.h &#61; SPIRITHEIGH;
119 SDL_BlitSurface(gpSpirit,&src[0][step],gpScreen,&dst);
120 SDL_Flip(gpScreen);
121 break;
122 case SDL_KEYUP:
123 switch(myEvent.key.keysym.sym)
124 {
125 case SDLK_LEFT:
126 printf("小人的坐标&#xff1a;(%d,%d)\n",spiritX,spiritY);
127 break;
128 case SDLK_RIGHT:
129 printf("小人的坐标&#xff1a;(%d,%d)\n",spiritX,spiritY);
130 break;
131 case SDLK_UP:
132 printf("小人的坐标&#xff1a;(%d,%d)\n",spiritX,spiritY);
133 break;
134 case SDLK_DOWN:
135 printf("小人的坐标&#xff1a;(%d,%d)\n",spiritX,spiritY);
136 break;
137 }
138 break;
139 }
140 }
141 }
142
143 //system("pause");
144 return 0;
145 }
146
147
148 /*--------------------------------------------------------------------
149 函数名: loadImage
150 参 数: char *filename 图像文件的名字
151 返回值: SDL_Surface * 返回指向图像表面的指针
152 功 能: 载入图像
153 备 注:
154 ----------------------------------------------------------------------*/
155 SDL_Surface *loadImage(char *aFilename)
156 {
157 SDL_Surface* loadedImage &#61; NULL;
158 SDL_Surface* optimizedImage &#61; NULL;
159
160 //载入图像
161 loadedImage &#61; IMG_Load( aFilename);
162
163 if( NULL !&#61; loadedImage )//If the image loaded
164 {
165 //创建优化图像
166 optimizedImage &#61; SDL_DisplayFormat( loadedImage );
167
168 //释放loadImage
169 SDL_FreeSurface( loadedImage );
170 }
171 return optimizedImage;
172 }
173
174 /*--------------------------------------------------------------------
175 函数名: loadImage
176 参 数: char *src 中文字符串
177 返回值: char * UTF8字符串
178 功 能: 将汉字转换成UTF8字符串
179 备 注:
180 ----------------------------------------------------------------------*/
181 char *localeToUTF8(char *src)
182 {
183 static char *buf &#61; NULL;
184 wchar_t *unicode_buf;
185 int nRetLen;
186
187 if(buf){
188 free(buf);
189 buf &#61; NULL;
190 }
191 nRetLen &#61; MultiByteToWideChar(CP_ACP,0,src,-1,NULL,0);
192 unicode_buf &#61; (wchar_t*)malloc((nRetLen&#43;1)*sizeof(wchar_t));
193 MultiByteToWideChar(CP_ACP,0,src,-1,unicode_buf,nRetLen);
194 nRetLen &#61; WideCharToMultiByte(CP_UTF8,0,unicode_buf,-1,NULL,0,NULL,NULL);
195 buf &#61; (char*)malloc(nRetLen&#43;1);
196 WideCharToMultiByte(CP_UTF8,0,unicode_buf,-1,buf,nRetLen,NULL,NULL);
197 free(unicode_buf);
198 return buf;
199 }

  第73-82行将每个小人的位置存储到src数组里&#xff0c;这样我们显示图像就不会错了&#xff0c;第101行我们检测了是否按下了↓键&#xff0c;如果按下了&#xff0c;我们把step的值加1&#xff0c;显示下一步的图片&#xff0c;但向下行走只有三幅图片&#xff0c;所以当step等于3时&#xff0c;应当将其置成0&#xff0c;这一步在第113行用了一个模运算完成&#xff0c;接下来就是定位将图片显示在屏幕上的位置&#xff0c;这个在键盘检测教程就已经说了&#xff0c;这不在累述&#xff0c;然后显示。运行结果可以看到很像一个人在行走。  

  现在&#xff0c;程序还有一点问题&#xff0c;就是不能转向&#xff0c;我们可以再设一个变量direct表示方向&#xff0c;其值为0表示向下&#xff0c;1向左&#xff0c;2向右&#xff0c;3向上&#xff0c;并且direct的值对应src行下标&#xff0c;然后再增加对其他三个方向的检测就可以了。另外我们在这个教程里设置了应用程序的名字并且名字是中文&#xff0c;所以需要加载windows.h头文件&#xff0c;这个会在后续章节讲述。

  这个程序没有对小人进行碰撞检测&#xff0c;所以小人遇树撞树&#xff0c;以后会做碰撞检测的讲解&#xff0c;完整的代码请点击这儿下载。

转:https://www.cnblogs.com/landmark/archive/2012/05/11/2496205.html



推荐阅读
  • 包含phppdoerrorcode的词条 ... [详细]
  • MySQL 5.7 服务端在 Windows 上的安装与配置
    本文详细介绍了在 Windows 系统上安装和配置 MySQL 5.7 服务端的方法,包括 my.ini 配置文件的设置、初始化数据库、启动服务以及设置用户权限等步骤。 ... [详细]
  • 本文详细介绍了Linux系统中用于管理IPC(Inter-Process Communication)资源的两个重要命令:ipcs和ipcrm。通过这些命令,用户可以查看和删除系统中的消息队列、共享内存和信号量。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • 使用ArcGIS for Java和Flex浏览自定义ArcGIS Server 9.3地图
    本文介绍了如何在Flex应用程序中实现浏览自定义ArcGIS Server 9.3发布的地图。这是一个基本的入门示例,适用于初学者。 ... [详细]
  • PHP函数的工作原理与性能分析
    在编程语言中,函数是最基本的组成单元。本文将探讨PHP函数的特点、调用机制以及性能表现,并通过实际测试给出优化建议。 ... [详细]
  • 本文介绍了 Oracle SQL 中的集合运算、子查询、数据处理、表的创建与管理等内容。包括查询部门号为10和20的员工信息、使用集合运算、子查询的注意事项、数据插入与删除、表的创建与修改等。 ... [详细]
  • 开发笔记:前端之前端初识
    开发笔记:前端之前端初识 ... [详细]
  • 在一个整型数组中,除了两个数字只出现一次外,其他所有数字都出现了两次。编写一个程序来找出这两个只出现一次的数字。 ... [详细]
  • Vue 中实现动态增删表单区域
    本文介绍如何在 Vue 项目中通过按钮实现表单区域的动态添加和删除功能。 ... [详细]
  • 本文探讨了 TypeScript 中泛型的重要性和应用场景,通过多个实例详细解析了泛型如何提升代码的复用性和类型安全性。 ... [详细]
  • 【线段树】  本质是二叉树,每个节点表示一个区间[L,R],设m(R-L+1)2(该处结果向下取整)左孩子区间为[L,m],右孩子区间为[m ... [详细]
  • 通过将常用的外部命令集成到VSCode中,可以提高开发效率。本文介绍如何在VSCode中配置和使用自定义的外部命令,从而简化命令执行过程。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
author-avatar
镜头拿反的葛小峥给_523
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有