定时器的使用
如果我们在SDL程序中需要每个一个固定时间去处理一件事情,那么我们需要一个定时器,就像生活中钟表一样可以整点报时,这个功能就是SDL的定时器子系统,你要使用SDL的定时器必须初始化定时器子系统,请看《SDL起动与退出》,SDL定时器比较简单就是一个间隔固定时间的函数调用,如果你要添加一个定时器可以使用函数SDL_AddTimer,其原型为:
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param);
其中:函数的返回值SDL_TimerID是一个结构体指针,SDL文档中并没有给出其结构,所以我们只要知道这是一个指向添加的定时器的指针就可以了。
函数的第一个参数interval是定时器间隔的时间,以毫秒(1秒=1000毫秒)为单位;第二个参数是要执行的函数,也称回调函数,是定时器每个interval时间段调用的函数,不是我们自己调用的,SDL_NewTimerCallback是一个函数指针类型,指明了要调用的函数原型:
typedef Uint32 (SDLCALL *SDL_NewTimerCallback)(Uint32 interval, void *param);
要调用的函数的原型满足返回值是Uint32,函数的第一个参数是定时器的时间间隔(一般和SDL_AddTimer的interval一样),第二个参数是需要给函数传递的参数,不需要传参的话可以写NULL。SDL_AddTimer第三个参数也是同样的。
下面我们做一个SDL电子表,显示当前时间,所需图片如下图所示:
所需图片 | 程序效果 |
程序代码如下:
1 /* 2 功能:Timer的用法 3 作者:csl 4 日期:2012-5-24 5 */ 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <time.h> 9 #include <string.h> 10 #include <SDL.h> 11 12 //窗口长和宽 13 #define SCREENWIDTH 280 14 #define SCREENHEIGH 40 15 #define BPP 32 16 17 //图片长和宽 18 #define DIGITALWIDTH 13 19 #define DIGITALHEIGH 23 20 21 SDL_Surface *gpScreen;//显示表面 22 SDL_Surface *gpDigitals;//数字的精灵图 23 SDL_Event myEvent;//事件 24 25 //时间定时器 26 SDL_TimerID myTime; 27 const unsigned int INTERVALS = 1000;//1秒 28 29 SDL_Surface *loadImage(char *aFilename); 30 void cleanUp(); 31 int cToD(char ch); 32 SDL_Rect locatePosition(char ch); 33 Uint32 displayTime(Uint32 interval, void *param); 34 35 int main(int argc,char *argv[]) 36 { 37 int quit = 0; 38 SDL_Rect src,dst; 39 40 if((SDL_Init(SDL_INIT_EVERYTHING)==-1)) //初始化视频子系统 41 { 42 printf("Unable to init SDL: %s\n", SDL_GetError()); 43 exit(-1); 44 } 45 atexit(cleanUp);// 注册cleanUp,当退出时调用,使得退出时程序自动清理 46 47 //创建32位600*480窗口 48 gpScreen = SDL_SetVideoMode(SCREENWIDTH,SCREENHEIGH, BPP, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF ||SDL_NOFRAME ); 49 if(!gpScreen) 50 { 51 exit(1); 52 } 53 gpDigitals = loadImage("jpg\\digitals.jpg"); 54 //displayTime(1000,0); 55 56 myTime = SDL_AddTimer(INTERVALS,displayTime,NULL); 57 if (!myTime) 58 { 59 exit(0); 60 } 61 62 while (!quit) 63 { 64 while (SDL_PollEvent(&myEvent)) 65 { 66 switch (myEvent.type) 67 { 68 case SDL_QUIT: 69 quit = 1; 70 break; 71 } 72 } 73 } 74 75 system("pause"); 76 return 0; 77 }
要使用定时器,我们首先要定义一个SDL_TimerID myTime;值得注意的是myTime是一个指针,在主函数里初始化子系统的时候要初始化定时器子系统,所以我们的初始化SDL_Init(SDL_INIT_EVERYTHING),是初始化所有的SDL子系统,然后要首先添加一个定时器,在56行我们添加了定时器,如果不成功则要结束程序。添加以后每隔1秒也就是1000毫秒就会调用回调函数displayTime来显示时间。displayTime代码如下:
/*-------------------------------------------------------------------- 函数名: displayTime 参 数: interval定时器的时间间隔; param需要传递给displayTime函数的参数 返回值: 下一次要调用该函数的时间间隔 功 能: 定时器回调函数,每隔interval时间段被系统调用 备 注: ----------------------------------------------------------------------*/ Uint32 displayTime(Uint32 interval, void *param) { struct tm *currentTime; SDL_Rect src; SDL_Rect dst = {(SCREENWIDTH-19*DIGITALWIDTH)/2,(SCREENHEIGH-DIGITALHEIGH)/2,DIGITALWIDTH,DIGITALHEIGH}; char strTime[20]; int i,len; time_t t = time(NULL);//取得当前系统时间 //取得当前时间,并按格式转换成字符串 currentTime = localtime(&t);//将t转换成当地时间 strftime(strTime,20,"%Y-%m-%d %H:%M:%S",currentTime); //在屏幕中央显示系统时间 for (i = 0;strTime[i]!='\0';i++) { src = locatePosition(strTime[i]); SDL_BlitSurface(gpDigitals,&src,gpScreen,&dst); dst.x+=DIGITALWIDTH; } SDL_Flip(gpScreen); return INTERVALS; }
在函数里,我们首先要取得当前系统时间,其中time_t是c语言标准库里定义的,是一个整型类型,C语言里用这个类型的变量保存系统时间,实际上保存的是按unix时间格式保存自1970年1月1日0时0分0秒起至现在的总秒数,这个我们很难看懂,所以我们使用了另外一种描述时间的结构struct tm:
struct tm { int tm_sec; /* 秒–取值区间为[0,59] */ int tm_min; /* 分 - 取值区间为[0,59] */ int tm_hour; /* 时 - 取值区间为[0,23] */ int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */ int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */ int tm_year; /* 年份,其值从1900开始 */ int tm_wday; /* 星期–取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */ int tm_yday; /* 从每年的1月1日开始的天数–取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */ int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负。*/ };
我们需要把time_t格式的时间转换成struct tm表示的时间,这个转换函数就是localtime,转换完成后我们就可以得到当前时间了,strftime函数可以将strcut tm时间转换成一个字符串,并且可以指定转换格式:
%a |
星期的缩略形式 |
%A |
星期的完整形式 |
%b |
月份的缩略形式 |
%B |
月份的完整形式 |
%c |
月份的缩略形式 |
%d |
月中的第几天(1-31) |
%H |
小时, 24小时格式 (0-23) |
%I |
小时, 12小时格式 (1-12) |
%j |
年中的第几天(1-366) |
%m |
月份 (1-12). Note: 某些版本的Microsoft Visual C++ 可能使用取值范围0-11. |
%M |
分钟(0-59) |
%p |
本地时间的上午或下午(AM or PM) |
%S |
秒钟(0-59) |
%U |
年中的第几周,星期天是一周的第一天 |
%w |
星期几的数字表示(0-6, 星期天=0) |
%W |
一年中的第几周,星期天是一周的第一天 |
%x |
标准日期字符串 |
%X |
标准时间字符串 |
%y |
年(0-99) |
%Y |
用CCYY表示的年(如:2004) |
%Z |
时区名 |
%% |
百分号 |
转换完成后我们得到一个字符格式的当前日期,比如说:2012-05-24 7:47:23,这个串里的每一个字符对对应精灵图里的一个精灵,所以我们就可以根据字符取得它在精灵图里的位置,然后将它显示在窗口上,我们是通过locatePosition函数取得位置的,它的代码如下:
/*-------------------------------------------------------------------- 函数名: locatePosition 参 数: ch需要确定位置的字符 返回值: 字符在精灵图中的位置 功 能: 计算字符在精灵图中的位置 备 注: ----------------------------------------------------------------------*/ SDL_Rect locatePosition(char ch) { int y; SDL_Rect tmp; switch(ch) { case '-': tmp.y=0; break; case ' ': tmp.y=DIGITALHEIGH; break; case ':': tmp.y = 2 * DIGITALHEIGH ; break; default: tmp.y=(12-cToD(ch))*DIGITALHEIGH; } tmp.x = 0; tmp.w = DIGITALWIDTH; tmp.h = DIGITALHEIGH; return tmp; }
每一副精灵图其x坐标都是0,y坐标可以分门别类加以列举,每一副精灵图的长和宽固定,最后返回一个矩形变量(SDL_Rect)就可以了 。
我们取得了字符的精灵图,在计算它应该显示在屏幕的位置就可以了,然后将其传输到显示表面上显示就可以了,显示完一副图片后将dst的x坐标加上图片宽度就是下一副图片的位置。
这样我们就可以每隔1秒钟显示下当前的系统时间了。最后不要忘记调用SDL_RemoveTimer(myTime)移除定时器。
例子的完整代码请点击这儿下载。
各位看官,如果你觉得那里不清楚请给回复,如果你觉得还可以请加关注,加粉丝,谢谢!总之有点反映。