使用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<SDL\SDL_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。





  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GSM呼叫流程 移动台的呼入接续过程: 1、寻呼。MSC/VLR在数据库中查出用户的资 料并向相 关的BSC发送寻呼信息。该信息包含用户所在区域的 LAI和用户的IMSI或者TMSI。 2、寻呼命令。BSC向LA区内的所有BTS发出寻呼命令。 该信息包含IMSI或TMSI。收发信单元识别码、信道类型 和时隙号。 3、寻呼请求。BTS在PCH上向移动台发送寻呼信息。该 信息包含用户的IMSI或TMSI。 4、信道请求。被寻呼的移动台在RACH上发送一个短的 接入脉冲串至BTS。BTS接收该寻呼响应信号后记录该突 发脉冲串的迟滞值。(TA 动态PWR) 5、信道请求。BTS向BSC发信道请求信息。该信息还包 含移动台接入系统的迟滞值(TA.PWR)。 6、信道激活。BSC选择一条空闲的SDCCH并指示BTS激 活该信道。 7、信道激活证实。BTS激活SDCCH后向BSC发信道激活 证实信息。 8、立即分配。BSC透过BTS经由AGCH向移动台发出允许 接入系统信息。该信息包含频率、时隙号、SDCCH信道号 和移动台将要使用的时间提前值TA等。 9、寻呼响应。移动台通过SDCCH向BSC发寻呼响应信息。 该信息包含移动台的IMSI或TMSI和移动台的等级标记, BSC加入CGI后把信息送往MSC/VLR。 10、鉴权请求。MSC/VLR透过BSC、BTS向移动台发鉴权 请求,其中包含随机数RAND,用移动台的鉴权运算。 11、鉴权响应。移动台经鉴权计算后向MSC/VLR发回鉴权 响应信息,MSC/VLR检查用户全法性,如用户全法,则开 始启动加密程序。 12、加密模式命令。MSC/VLR通过BSC、BTS向移动用户 发加密模式命令。该命令在SDCCH上传送。 13、加密模式完成。移动台进行加密运算后向BTS发出已加 密的特定信号,BTS解密成功后透过BSC向MSC/VLR发加密 模式完成信息。 14、设置呼叫类型。MSC向移动台发送呼叫类型设置信息。 该信息包含该次呼叫的类型。如传真、通话或数据通信等类型。 15、呼叫类型证实。移动台设置好呼叫类型后向MSC发出呼叫 类型证实信息。 16、分配请求。MSC要求BSC选择一条通往移动台的话音信道, 同时MSC在一条通往BSC的PCM上选择一个空闲时隙,并把时 隙的电路识别码CIC送往BSC。 17、信道激活。如果BSC发现某小区上有一条空闲的TCH,它 将向BTS发送信道激活命令。 18、信道激活证实。BTS激活TCH后向BSC发回信道激活证实 信息。 19、分配命令。BSC通过SDCCH向移动台发信道切换指令,命 令移动台切换至所指定的TCH。 20、分配完成。移动台切换至所指定的TCH后向BSC发送信道 分配完成信息,BSC接收后再送往MSC/VLR。 21、无线频率信道释放/释放证实。BSC释放SDCCH信道并把 它标记为空闲状态。 22、振铃回应。当移动台开始振铃时移动台要向MSC发送一 个通知信息。 23、连接。当移动台摘机应答时,移动台向MSC发送一个连 接信息,MSC把移动台的电路接通,开始通话。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值