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


    这次更新的是一个关于使用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、实现号码的展示,这里我偷了个懒,没有使用真实数字的展示,而是通过带数字的图片进行展示的,大家有兴趣的话可以自己去实现下真实的字。

    下面直接上代码,里面有很清楚的注释:

    #include<iostream>
    #include<SDL/SDL.h>
    #include<SDL/SDL_Image.h>
    #include<SDLSDL_mixer.h>
    #include<string>
    #include<time.h>
    #include<stdlib.h>
    #include<vector>
    using namespace std;
    
    
    SDL_Renderer *renderer = nullptr;
    SDL_Window *window = nullptr;
    const int SCREEN_WIDTH = 1000;
    const int SCREEN_HEIGHT = 668;
    //buttons用于记录电话盘上0到9的圆心坐标
    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}};
    const int radius=20;  //代表电话盘上圆心的半径
    int phoneNumber[11];   //中国的号码最多11位
    int index=0;    //用于记录号码的个数
    SDL_Texture *number[10];    //存放10个数字的图片
    
    //此函数用于加载图片
    SDL_Texture* LoadImage(std::string file);
    
    //此函数用于将纹理画到渲染器上
    void ApplySurface(int x, int y, SDL_Texture *tex, SDL_Renderer *rend);
    
    //此函数用于判断鼠标是否按在了0到9这10个数字中的一个,如果是,则返回这个数字的值,否则返回-1
    int check(int mouse_x,int mouse_y);
    
    //此函数用于计算两个向量之间的夹角,运用的是余弦定理和反余弦函数求角度
    double calDegree(int x1,int y1,int x2,int y2);
    
    //计算第二个向量相对于第一个向量旋转的方向,用的是向量叉乘定理
    bool direct(int x1,int y1,int x2,int y2);
    
    //此函数用于显示我们将要展示的图形界面
    void show(SDL_Texture *phone,SDL_Texture *dialdial,SDL_Texture *button,int angle,SDL_Point center);
    
    //此函数用于加载十个数字
    void initNumber();
    
    //销毁十个数字的图片
    void destroyNumber();
    
    int main(int argc,char *argv)
    {
    	if (SDL_Init(SDL_INIT_EVERYTHING) == -1){
            std::cout << SDL_GetError() << std::endl; 
            return 1;
        }
        window = SDL_CreateWindow("LightDemo", SDL_WINDOWPOS_CENTERED, 
            SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);  //创建一个绘制图片的窗口
        if (window == nullptr){
            std::cout << SDL_GetError() << std::endl;
            return 2;
        }
    	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED 
            | SDL_RENDERER_PRESENTVSYNC);   //创建一个指定到窗口的渲染器
        if (renderer == nullptr){
            std::cout << SDL_GetError() << std::endl;
            return 3;
        }
    	
    	SDL_Texture *phone = nullptr, *dial = nullptr,*button=nullptr;
        try {
    		//用于加载电话机的整体图片
    		phone=LoadImage("phone.jpg");
    		//用于加载电话的拨号盘
            dial = LoadImage("dial.png");
    		//用于加载拨号盘上的挂钩
    		button=LoadImage("button.png");
        }
    		catch (const std::runtime_error &e){
    			std::cout << e.what() << std::endl;
    		return 4;
        }
    
        initNumber();      //加载0到9这10张图片
    	double angle=0;    //angle代表旋转的角度
    	SDL_Point center;   //center用于表示dial图片的圆心
    	center.x=644;
    	center.y=448;
    	
    	//加载声音文件
    	Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,2048);
        Mix_Music *sound=Mix_LoadMUS("D://sound.wav");
    	
    	time_t start,end=0;   //用于标识时间的起始,在后面我们在配音的时候会用到
    	time_t timeDist=0;    //此变量用于表示start和end的差值    
    	SDL_Event e;
    	bool quit=false;    //用于标记用户是否想退出程序
    	bool mouseDown=false;   //此变量用于指示鼠标是否一直按着没有松开
    	int downButton=-1;     //此变量用于标识用户在拨动的号码
    	//下面两组变量用于存储两个不同时刻鼠标的位置
    	int mouse_x,mouse_y;   
    	int curMouse_x;    
    	int curMouse_y;
    	bool flag;    //flag用于标识拨号盘的号码键是否被成功拨到底端,成功拨号置为true,否则为false
    	show(phone,dial,button,angle,center);
    	while(!quit)
    	{
    		while(SDL_PollEvent(&e))
    		{
    			switch(e.type)
    			{
                //此种情况是退出程序用的,当你点击窗口的红X时就会关闭界面
    			case SDL_QUIT:   
    				quit=true;
    				break;
    			//此处处理的是鼠标按下事件
    			case SDL_MOUSEBUTTONDOWN:
    				mouseDown=true;
    				mouse_x=e.button.x;
    				mouse_y=e.button.y;
    				downButton=check(mouse_x,mouse_y);   //记下当前鼠标按下的数字(当然也可能为-1,即没有按在数字上)
    				show(phone,dial,button,angle,center);
    				start=clock();     //记录下起始时间,以便播放声音
    				break;
    			//鼠标弹起操作
    			case SDL_MOUSEBUTTONUP:
    				while(angle>=0)   //此时angle大于0则表示拨号盘被松开了且没有回到原位置,则动态展示拨号盘自动转回来的场景
    				{
    					show(phone,dial,button,angle,center);
    					angle-=2;   //设置每次转动角度为2
    					end=clock();  //记录下时间
    					if(difftime(end,start)-timeDist>800)   //当时间过了800毫秒时,重新播放音乐
    					{
    						timeDist+=800;
    						Mix_PlayMusic(sound,1);
    					}
    				}
    				if(angle<=0)
    					end=0;      //当拨号盘转回到初始位置时,将结束时间清0
    				mouseDown=false;    //记录下鼠标没有按下
    				timeDist=0;     //时间间隔清0
    				break;
    		    //处理鼠标移动
    			case SDL_MOUSEMOTION:
    				{
    					//记录下鼠标移动过程中位置的坐标
    					curMouse_x=e.motion.x;
    					curMouse_y=e.motion.y;
    					if(mouseDown)    //当鼠标按下去时,此时即是鼠标在拖动
    					{
    						//此处的75+downButton*27是根据拨号盘中数字的位置计算每个数字可以被转动的角度,当到极限时不允许转动,此时即拨号成功
    						if(downButton!=-1&&angle<75+downButton*27)  
    						{
    							if(end==0)    //当拨号盘从初始位置转动时,播放音乐
    								 Mix_PlayMusic(sound,1);
    							end=clock();    //记录下时间,以便计算时间间隔
    							flag=false;
    							int vec1_x=mouse_x-center.x;
    							int vec1_y=mouse_y-center.y;
    							int vec2_x=curMouse_x-center.x;
    							int vec2_y=curMouse_y-center.y;
    							if(direct(vec1_x,vec1_y,vec2_x,vec2_y))   //计算鼠标旋转的方向,顺时针时才可以拨动号码盘
    							    angle+=calDegree(vec1_x,vec1_y,vec2_x,vec2_y);
    							show(phone,dial,button,angle,center);
    							//SDL_Delay(20);
    							//将当前鼠标的位置赋值给记录前一次移动鼠标的位置,以便进行迭代
    						    mouse_x=curMouse_x;
    							mouse_y=curMouse_y;
    							//当时间过了800毫秒时,重新播放音乐
    							if(difftime(end,start)-timeDist>800)
    							{
    								timeDist+=800;
    							    Mix_PlayMusic(sound,1);
    							}
    						}
    						else if(!flag&&angle>75+downButton*27)   //此时代表拨号成功
    						{
    							flag=true;
    							//将号码存储在数组中
    							if(downButton==10)   //10代表的是数字0
    							   phoneNumber[index]=0;    
    							else
    							   phoneNumber[index]=downButton;
    							index++;
    							//当号码的位数超过11位时,清空
    							if(index>=11)
    								index=0;
    						}
    					}
    				}
    				break;
    			//处理默认的操作
    			default:
    				break;
    		}
    	}
      }
      SDL_DestroyTexture(phone);
      SDL_DestroyTexture(dial);
      SDL_DestroyTexture(button);
      SDL_DestroyRenderer(renderer);
      destroyNumber();
      SDL_DestroyWindow(window);
      return 0;
    }
    
    //此函数用于加载十个数字
    void initNumber()
    {
    	number[0]=LoadImage("0.png");
        number[1]=LoadImage("1.png");
        number[2]=LoadImage("2.png");
    	number[3]=LoadImage("3.png");
    	number[4]=LoadImage("4.png");
    	number[5]=LoadImage("5.png");
    	number[6]=LoadImage("6.png");
    	number[7]=LoadImage("7.png");
    	number[8]=LoadImage("8.png");
    	number[9]=LoadImage("9.png");
    }
    
    void destroyNumber()
    {
    	for(int i=0;i<9;i++)
    	{
    		SDL_DestroyTexture(number[i]);
    	}
    }
    //此函数用于加载图片
    SDL_Texture* LoadImage(std::string file){
        SDL_Texture* tex = nullptr;
        tex = IMG_LoadTexture(renderer, file.c_str());
        if (tex == nullptr)
            throw std::runtime_error("Failed to load dial: " + file + IMG_GetError());
        return tex;
    }
    
    //此函数用于将纹理画到渲染器上
    void ApplySurface(int x, int y, SDL_Texture *tex, SDL_Renderer *rend)
    {
        SDL_Rect pos;
    	//x,y是图片左上角的坐标
        pos.x = x;
        pos.y = y;
        SDL_QueryTexture(tex, NULL, NULL, &pos.w, &pos.h); 
     
        SDL_RenderCopy(rend, tex, NULL, &pos);   //将纹理tex画到渲染器rend
    }
    
    //此函数用于判断鼠标是否按在了0到9这10个数字中的一个,如果是,则返回这个数字的值,否则返回-1
    int check(int mouse_x,int mouse_y)
    {
    	double dist;
    	for(int i=0;i<10;i++)
    	{
    		//dist存储的值为鼠标到圆形数字圆心的距离
    		dist=sqrt(double((mouse_x-buttons[i][0])*(mouse_x-buttons[i][0])+(mouse_y-buttons[i][1])*(mouse_y-buttons[i][1])));
    		if(dist<=radius)    //当距离小于半径时,则表示鼠标触碰到了数字
    		{
    			if(i==0)   //此处将0处理成10是为了后面旋转拨号时角度的处理方便
    				return 10;
    			return i;
    		}
    	}
    	return -1;
    }
    
    //此函数用于计算两个向量之间的夹角,运用的是余弦定理和反余弦函数求角度
    double calDegree(int x1,int y1,int x2,int y2)
    {
    	int n=x1*x2+y1*y2;
    	double m=sqrt(double(x1*x1+y1*y1))*sqrt(double(x2*x2+y2*y2));
    	return acos(n/m)*180/3.14;
    }
    
    //计算第二个向量相对于第一个向量旋转的方向,用的是向量叉乘定理
    bool direct(int x1,int y1,int x2,int y2)
    {
    	int n=x1*y2-x2*y1;
    	return n>=0;   //n大于0表示顺时针,否则表示逆时针
    }
    
    //此函数用于显示我们将要展示的图形界面
    void show(SDL_Texture *phone,SDL_Texture *dialdial,SDL_Texture *button,int angle,SDL_Point center)
    {
    	SDL_RenderClear(renderer);
    	ApplySurface(0,0,phone,renderer);
    	SDL_RenderCopyEx(renderer, dialdial, NULL,
    		NULL, angle, ¢er, SDL_FLIP_NONE);
    	//下面的循环用于在界面上显示拨打的号码
    	for(int i=0;i<index;i++)
    	{
    		ApplySurface(30+30*i,450,number[phoneNumber[i]],renderer);
    	}
    	SDL_RenderPresent(renderer);
    }

    程序的所有相关资源放在了网盘,链接为:http://pan.baidu.com/s/1pLdF6BL。





  • 相关阅读:
    vue-loader介绍和单页组件介绍
    webpack的插件 http-webpack-plugin。 webpack-dev-server
    webpack的介绍
    Axios 的基本使用
    如何使用 re模块的, spilt.
    为 JS 的字符串,添加一个 format 的功能。
    另一种分页器 不依赖Paginator模块的方法
    Socket初识
    网络协议
    双下方法补充以及异常处理
  • 原文地址:https://www.cnblogs.com/hliu17/p/7399924.html
Copyright © 2020-2023  润新知