使用SDL2.0编写一个模拟电话拨号盘的程序

[复制链接]

该用户从未签到

2380

主题

2433

帖子

9139

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
9139
QQ
跳转到指定楼层
楼主
发表于 2017-12-22 15:20:11 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

想要查看内容赶紧注册登陆吧!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
这次更新的是一个关于使用SDL编写模拟电话拨号盘的程序。
下面先来描述一下这个程序需要实现的功能:
       在这次的程序中,我们将要实现的功能是拨动拨号盘,在拨动期间会伴有拨号声音,并且在拨号完后会显示出号码。
下面是我们将要用到的一些图片资源:

这是我在网上找的一张图片,作为我们整个程序的原始图片,后面我们需要的图片要从它上面进行ps提取,首先我们要把它中间的那个圆盘给p下来,保存成一张透明的png图片,同样将那个黑色的卡条给p下来,保存成一张透明的png图片,这里我就不贴上来了,因为图片太大了,待会我会在文章最后附上我全部程序资源的百度云网址,供大家下载。
下面简要说一下此程序需要用到的技术:
1、对于如何加载窗口和进行事件响应的工作我在这里就不再说明了,如果有什么问题可以参考我的上一篇博客,里面有相关的说明。
2、我们首先来实现拨号的功能,也就是如何让中间的拨号盘可以转动起来。我这里使用的方法是用将中间的拨号盘的图片扣出来,做成一张周围透明的图片(这些图片的加载 需要用到SDL_Image库),覆盖在原始的电话机上,并使用这个抠出来的图片响应鼠标的拖动,以实现拨号的功能,这里面将会使用到一个函数SDL_RenderCopyEx,这个函数可以参照我的另一篇文章:http://blog.csdn.net/qq_29883591/article/details/52924047
3、然后我们要实现拨号声音,这里面我们将会使用SDL_Mixer库,这个可以参照我的另一篇博客:http://blog.csdn.net/qq_29883591/article/details/52913658,对于我程序中用到的音乐只有不到1秒钟,所以在拨号期间我得记录时间间隔并重复播放音乐。
4、实现号码的展示,这里我偷了个懒,没有使用真实数字的展示,而是通过带数字的图片进行展示的,大家有兴趣的话可以自己去实现下真实的字。
下面直接上代码,里面有很清楚的注释:
  1. #include<iostream>
  2. #include<SDL/SDL.h>
  3. #include<SDL/SDL_Image.h>
  4. #include<SDL\SDL_mixer.h>
  5. #include<string>
  6. #include<time.h>
  7. #include<stdlib.h>
  8. #include<vector>
  9. using namespace std;


  10. SDL_Renderer *renderer = nullptr;
  11. SDL_Window *window = nullptr;
  12. const int SCREEN_WIDTH = 1000;
  13. const int SCREEN_HEIGHT = 668;
  14. //buttons用于记录电话盘上0到9的圆心坐标
  15. const int buttons[10][10]={{692,551},{696,344},{641,334},{590,344},{552,378},{530,424},{530,475},{554,522},{592,552},{645,564}};
  16. const int radius=20;  //代表电话盘上圆心的半径
  17. int phoneNumber[11];   //中国的号码最多11位
  18. int index=0;    //用于记录号码的个数
  19. SDL_Texture *number[10];    //存放10个数字的图片

  20. //此函数用于加载图片
  21. SDL_Texture* LoadImage(std::string file);

  22. //此函数用于将纹理画到渲染器上
  23. void ApplySurface(int x, int y, SDL_Texture *tex, SDL_Renderer *rend);

  24. //此函数用于判断鼠标是否按在了0到9这10个数字中的一个,如果是,则返回这个数字的值,否则返回-1
  25. int check(int mouse_x,int mouse_y);

  26. //此函数用于计算两个向量之间的夹角,运用的是余弦定理和反余弦函数求角度
  27. double calDegree(int x1,int y1,int x2,int y2);

  28. //计算第二个向量相对于第一个向量旋转的方向,用的是向量叉乘定理
  29. bool direct(int x1,int y1,int x2,int y2);

  30. //此函数用于显示我们将要展示的图形界面
  31. void show(SDL_Texture *phone,SDL_Texture *dialdial,SDL_Texture *button,int angle,SDL_Point center);

  32. //此函数用于加载十个数字
  33. void initNumber();

  34. //销毁十个数字的图片
  35. void destroyNumber();

  36. int main(int argc,char *argv)
  37. {
  38.         if (SDL_Init(SDL_INIT_EVERYTHING) == -1){
  39.         std::cout << SDL_GetError() << std::endl;
  40.         return 1;
  41.     }
  42.     window = SDL_CreateWindow("LightDemo", SDL_WINDOWPOS_CENTERED,
  43.         SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);  //创建一个绘制图片的窗口
  44.     if (window == nullptr){
  45.         std::cout << SDL_GetError() << std::endl;
  46.         return 2;
  47.     }
  48.         renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED
  49.         | SDL_RENDERER_PRESENTVSYNC);   //创建一个指定到窗口的渲染器
  50.     if (renderer == nullptr){
  51.         std::cout << SDL_GetError() << std::endl;
  52.         return 3;
  53.     }
  54.        
  55.         SDL_Texture *phone = nullptr, *dial = nullptr,*button=nullptr;
  56.     try {
  57.                 //用于加载电话机的整体图片
  58.                 phone=LoadImage("phone.jpg");
  59.                 //用于加载电话的拨号盘
  60.         dial = LoadImage("dial.png");
  61.                 //用于加载拨号盘上的挂钩
  62.                 button=LoadImage("button.png");
  63.     }
  64.                 catch (const std::runtime_error &e){
  65.                         std::cout << e.what() << std::endl;
  66.                 return 4;
  67.     }

  68.     initNumber();      //加载0到9这10张图片
  69.         double angle=0;    //angle代表旋转的角度
  70.         SDL_Point center;   //center用于表示dial图片的圆心
  71.         center.x=644;
  72.         center.y=448;
  73.        
  74.         //加载声音文件
  75.         Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,2048);
  76.     Mix_Music *sound=Mix_LoadMUS("D://sound.wav");
  77.        
  78.         time_t start,end=0;   //用于标识时间的起始,在后面我们在配音的时候会用到
  79.         time_t timeDist=0;    //此变量用于表示start和end的差值   
  80.         SDL_Event e;
  81.         bool quit=false;    //用于标记用户是否想退出程序
  82.         bool mouseDown=false;   //此变量用于指示鼠标是否一直按着没有松开
  83.         int downButton=-1;     //此变量用于标识用户在拨动的号码
  84.         //下面两组变量用于存储两个不同时刻鼠标的位置
  85.         int mouse_x,mouse_y;   
  86.         int curMouse_x;   
  87.         int curMouse_y;
  88.         bool flag;    //flag用于标识拨号盘的号码键是否被成功拨到底端,成功拨号置为true,否则为false
  89.         show(phone,dial,button,angle,center);
  90.         while(!quit)
  91.         {
  92.                 while(SDL_PollEvent(&e))
  93.                 {
  94.                         switch(e.type)
  95.                         {
  96.             //此种情况是退出程序用的,当你点击窗口的红X时就会关闭界面
  97.                         case SDL_QUIT:   
  98.                                 quit=true;
  99.                                 break;
  100.                         //此处处理的是鼠标按下事件
  101.                         case SDL_MOUSEBUTTONDOWN:
  102.                                 mouseDown=true;
  103.                                 mouse_x=e.button.x;
  104.                                 mouse_y=e.button.y;
  105.                                 downButton=check(mouse_x,mouse_y);   //记下当前鼠标按下的数字(当然也可能为-1,即没有按在数字上)
  106.                                 show(phone,dial,button,angle,center);
  107.                                 start=clock();     //记录下起始时间,以便播放声音
  108.                                 break;
  109.                         //鼠标弹起操作
  110.                         case SDL_MOUSEBUTTONUP:
  111.                                 while(angle>=0)   //此时angle大于0则表示拨号盘被松开了且没有回到原位置,则动态展示拨号盘自动转回来的场景
  112.                                 {
  113.                                         show(phone,dial,button,angle,center);
  114.                                         angle-=2;   //设置每次转动角度为2
  115.                                         end=clock();  //记录下时间
  116.                                         if(difftime(end,start)-timeDist>800)   //当时间过了800毫秒时,重新播放音乐
  117.                                         {
  118.                                                 timeDist+=800;
  119.                                                 Mix_PlayMusic(sound,1);
  120.                                         }
  121.                                 }
  122.                                 if(angle<=0)
  123.                                         end=0;      //当拨号盘转回到初始位置时,将结束时间清0
  124.                                 mouseDown=false;    //记录下鼠标没有按下
  125.                                 timeDist=0;     //时间间隔清0
  126.                                 break;
  127.                     //处理鼠标移动
  128.                         case SDL_MOUSEMOTION:
  129.                                 {
  130.                                         //记录下鼠标移动过程中位置的坐标
  131.                                         curMouse_x=e.motion.x;
  132.                                         curMouse_y=e.motion.y;
  133.                                         if(mouseDown)    //当鼠标按下去时,此时即是鼠标在拖动
  134.                                         {
  135.                                                 //此处的75+downButton*27是根据拨号盘中数字的位置计算每个数字可以被转动的角度,当到极限时不允许转动,此时即拨号成功
  136.                                                 if(downButton!=-1&&angle<75+downButton*27)  
  137.                                                 {
  138.                                                         if(end==0)    //当拨号盘从初始位置转动时,播放音乐
  139.                                                                  Mix_PlayMusic(sound,1);
  140.                                                         end=clock();    //记录下时间,以便计算时间间隔
  141.                                                         flag=false;
  142.                                                         int vec1_x=mouse_x-center.x;
  143.                                                         int vec1_y=mouse_y-center.y;
  144.                                                         int vec2_x=curMouse_x-center.x;
  145.                                                         int vec2_y=curMouse_y-center.y;
  146.                                                         if(direct(vec1_x,vec1_y,vec2_x,vec2_y))   //计算鼠标旋转的方向,顺时针时才可以拨动号码盘
  147.                                                             angle+=calDegree(vec1_x,vec1_y,vec2_x,vec2_y);
  148.                                                         show(phone,dial,button,angle,center);
  149.                                                         //SDL_Delay(20);
  150.                                                         //将当前鼠标的位置赋值给记录前一次移动鼠标的位置,以便进行迭代
  151.                                                     mouse_x=curMouse_x;
  152.                                                         mouse_y=curMouse_y;
  153.                                                         //当时间过了800毫秒时,重新播放音乐
  154.                                                         if(difftime(end,start)-timeDist>800)
  155.                                                         {
  156.                                                                 timeDist+=800;
  157.                                                             Mix_PlayMusic(sound,1);
  158.                                                         }
  159.                                                 }
  160.                                                 else if(!flag&&angle>75+downButton*27)   //此时代表拨号成功
  161.                                                 {
  162.                                                         flag=true;
  163.                                                         //将号码存储在数组中
  164.                                                         if(downButton==10)   //10代表的是数字0
  165.                                                            phoneNumber[index]=0;   
  166.                                                         else
  167.                                                            phoneNumber[index]=downButton;
  168.                                                         index++;
  169.                                                         //当号码的位数超过11位时,清空
  170.                                                         if(index>=11)
  171.                                                                 index=0;
  172.                                                 }
  173.                                         }
  174.                                 }
  175.                                 break;
  176.                         //处理默认的操作
  177.                         default:
  178.                                 break;
  179.                 }
  180.         }
  181.   }
  182.   SDL_DestroyTexture(phone);
  183.   SDL_DestroyTexture(dial);
  184.   SDL_DestroyTexture(button);
  185.   SDL_DestroyRenderer(renderer);
  186.   destroyNumber();
  187.   SDL_DestroyWindow(window);
  188.   return 0;
  189. }

  190. //此函数用于加载十个数字
  191. void initNumber()
  192. {
  193.         number[0]=LoadImage("0.png");
  194.     number[1]=LoadImage("1.png");
  195.     number[2]=LoadImage("2.png");
  196.         number[3]=LoadImage("3.png");
  197.         number[4]=LoadImage("4.png");
  198.         number[5]=LoadImage("5.png");
  199.         number[6]=LoadImage("6.png");
  200.         number[7]=LoadImage("7.png");
  201.         number[8]=LoadImage("8.png");
  202.         number[9]=LoadImage("9.png");
  203. }

  204. void destroyNumber()
  205. {
  206.         for(int i=0;i<9;i++)
  207.         {
  208.                 SDL_DestroyTexture(number[i]);
  209.         }
  210. }
  211. //此函数用于加载图片
  212. SDL_Texture* LoadImage(std::string file){
  213.     SDL_Texture* tex = nullptr;
  214.     tex = IMG_LoadTexture(renderer, file.c_str());
  215.     if (tex == nullptr)
  216.         throw std::runtime_error("Failed to load dial: " + file + IMG_GetError());
  217.     return tex;
  218. }

  219. //此函数用于将纹理画到渲染器上
  220. void ApplySurface(int x, int y, SDL_Texture *tex, SDL_Renderer *rend)
  221. {
  222.     SDL_Rect pos;
  223.         //x,y是图片左上角的坐标
  224.     pos.x = x;
  225.     pos.y = y;
  226.     SDL_QueryTexture(tex, NULL, NULL, &pos.w, &pos.h);

  227.     SDL_RenderCopy(rend, tex, NULL, &pos);   //将纹理tex画到渲染器rend
  228. }

  229. //此函数用于判断鼠标是否按在了0到9这10个数字中的一个,如果是,则返回这个数字的值,否则返回-1
  230. int check(int mouse_x,int mouse_y)
  231. {
  232.         double dist;
  233.         for(int i=0;i<10;i++)
  234.         {
  235.                 //dist存储的值为鼠标到圆形数字圆心的距离
  236.                 dist=sqrt(double((mouse_x-buttons[i][0])*(mouse_x-buttons[i][0])+(mouse_y-buttons[i][1])*(mouse_y-buttons[i][1])));
  237.                 if(dist<=radius)    //当距离小于半径时,则表示鼠标触碰到了数字
  238.                 {
  239.                         if(i==0)   //此处将0处理成10是为了后面旋转拨号时角度的处理方便
  240.                                 return 10;
  241.                         return i;
  242.                 }
  243.         }
  244.         return -1;
  245. }

  246. //此函数用于计算两个向量之间的夹角,运用的是余弦定理和反余弦函数求角度
  247. double calDegree(int x1,int y1,int x2,int y2)
  248. {
  249.         int n=x1*x2+y1*y2;
  250.         double m=sqrt(double(x1*x1+y1*y1))*sqrt(double(x2*x2+y2*y2));
  251.         return acos(n/m)*180/3.14;
  252. }

  253. //计算第二个向量相对于第一个向量旋转的方向,用的是向量叉乘定理
  254. bool direct(int x1,int y1,int x2,int y2)
  255. {
  256.         int n=x1*y2-x2*y1;
  257.         return n>=0;   //n大于0表示顺时针,否则表示逆时针
  258. }

  259. //此函数用于显示我们将要展示的图形界面
  260. void show(SDL_Texture *phone,SDL_Texture *dialdial,SDL_Texture *button,int angle,SDL_Point center)
  261. {
  262.         SDL_RenderClear(renderer);
  263.         ApplySurface(0,0,phone,renderer);
  264.         SDL_RenderCopyEx(renderer, dialdial, NULL,
  265.                 NULL, angle, ¢er, SDL_FLIP_NONE);
  266.         //下面的循环用于在界面上显示拨打的号码
  267.         for(int i=0;i<index;i++)
  268.         {
  269.                 ApplySurface(30+30*i,450,number[phoneNumber[i]],renderer);
  270.         }
  271.         SDL_RenderPresent(renderer);
  272. }
复制代码
完整程序下载
游客,如果您要查看本帖隐藏内容请回复

分享到:  QQ好友和群QQ好友和群
收藏收藏
回复

使用道具 举报

快速回复高级模式
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表