基于arm的嵌入式LCD显示屏贪吃蛇游戏
开始界面选择挑战模式,随机产生目标食物,触摸显示屏,控制蛇身移动,吃到食物时蛇身加长,分数加一,达到一定的数目挑战成功,碰到障碍物,挑战失败,在游戏开始之后,伴有背景音乐。
系统功能实现过程:
父进程开始,初始化LCD,设置LCD地址映射,打开LCD表面电容触摸屏触摸功能,打开BMP背景图片,进入开始界面,判断触摸点位置,回收LCD的资源,关闭LCD, 选择功能关卡,对应两个功能,父进程创建子进程,根据触摸点位置,子进程使用execl()函数,子进程执行贪吃蛇游戏代码。在子进程中,初始化LCD,设置LCD的地址映射,打开LCD表面电容触摸屏触摸功能,打开BMP背景图片,创建三个线程,通过三个线程配合使用实现随机食物的产生,蛇身移动,背景音乐的播放功能。
线程一:轮询LCD的触摸屏,判断触摸点的位置,判断蛇身下一次的移动方向。
线程二:打开madplay软件,实现背景音乐的播放。
线程三:根据线程一的触摸信息控制蛇身移动,蛇身的移动使用队列的方式,蛇头作为队尾,蛇尾作为队首,根据蛇头下一点的移动方向判断是否吃到食物,判断是否碰到了墙壁。
(1)吃到食物,判断是否完成挑战,即吃到了指定的食物数量,完成挑战,则发送cancel信号给其他两个线程,等待其他两个线程结束,回收资源,执行_exit()结束子进程。否则,入队,食物的显示位置作为新的蛇头,不进行出队操作,蛇尾保持不变,
(2)没有吃到食物,同时进行入队出队操作,增加新的蛇头,删除蛇尾。蛇尾到达下一点的队列的尾部。
(3)碰到了障碍物,则发送cancel信号给其他两个线程,等待其他两个线程结束,回收资源,执行_exit()结束子进程。
关键代码:
(1)线程一轮询LCD的触摸屏位置信息:
void *KEY_value(void *p)
{
while(1)
{
get_xy(&X0,&Y0);
sem_wait(&touch_condition);
if ( X0< 600 && Y0>300) KEY_state =turn_LEFT;
else if( ((X0> 611 && X0<670) && ( Y0>380 && Y0<440)) )KEY_state=turn_down;
else if( ((X0> 680 && X0<734) && ( Y0>380 && Y0<440)) )KEY_state=turn_right;
else if( ((X0> 610 && X0<670) && ( Y0>300 && Y0<360)) )KEY_state =turn_up ;
else {KEY_state=KEY_state;}
sem_post(&touch_condition);
if(OVER==2)pthread_exit(NULL);
}
}
get_xy(&X0,&Y0);实现LCD触摸屏位置信息的获取。
x0,y0,用于保存当前的触摸点坐标位置。
turn_LEFT;turn_right;turn_up ;turn_down得到按键的位置,赋值给KEY_state。
由于考虑到多线程通信是通过全局变量实现的,因此必须考虑变量的使用顺序问题,通过信号量的限制,更新KEY_state的值,KEY_state保存着当前触摸屏的位置信息,供线程三控制蛇身移动。
(2)蛇移动的实现:
从蛇头到蛇尾,每一处都是结构体数组的一个元素,每个元素存放该显示点的像素点坐标,循环的执行入队出队操作,根据蛇头蛇尾的坐标信息,触摸点的位置信息,确定下一点的坐标信息。
根据线程一KEY_state的值;
switch(KEY_state)
{
case turn_LEFT :
SNAK[snake_front_next].x_point=SNAK[snake_front].x_point-20;
SNAK[snake_front_next].y_point=SNAK[snake_front].y_point;
break;
case turn_right:
SNAK[snake_front_next].x_point=SNAK[snake_front].x_point+20;
SNAK[snake_front_next].y_point=SNAK[snake_front].y_point;
break;
case turn_up :
SNAK[snake_front_next].x_point=SNAK[snake_front].x_point;
SNAK[snake_front_next].y_point=SNAK[snake_front].y_point-20;
break;
case turn_down :
SNAK[snake_front_next].x_point=SNAK[snake_front].x_point;
SNAK[snake_front_next].y_point=SNAK[snake_front].y_point+20;
break;
default:
break;
}
蛇身的每一点图片大小为20*20的BMP格式图片。
SNAK[snake_front_next] 存放当前蛇头的位置信息
SNAK[snake_front] 存放下一点蛇头的位置信息
根据蛇头下一点位置信息,确定执行移动操作,通关操作和挑战失败操作:
sem_post(&touch_condition);
/
//判断是否撞在障碍物上:
/
if( (SNAK[snake_front_next].x_point>=520)||(SNAK[snake_front_next].x_point<0) || (SNAK[snake_front_next].y_point<0) || (SNAK[snake_front_next].y_point>=480))
{
end_condition=fail;
break;
}
/
//判断是否咬到蛇身:
/
for(value=0;value<MAX_NUM;value++)
{
if(SNAK[value].stat)
{
if( (SNAK[snake_front_next].x_point==SNAK[value].x_point) &&
( (SNAK[snake_front_next].y_point==SNAK[value].y_point) ) )
{
end_condition=fail;
break;
}
}
}
/
//判断是否吃到了食物:
/
if((SNAK[snake_front_next].x_point==target.x_point) &&
( (SNAK[snake_front_next].y_point==target.y_point) ))
{
snake_front=snake_front_next;
SNAK[snake_front].stat=1;
score+=1 ;
speed+=50 ;
eat_value=0;
printf("吃到了食物\r\n");
}
else
{
printf("处于查找状态\r\n");
snake_front=snake_front_next;
SNAK[snake_front].stat=1;
//蛇身位置更新:
bmp_show(S_PIC,SNAK[snake_front].x_point,SNAK[snake_front].y_point);
//画出新的舌头
bmp_show_bck(SNAK[snake_rear].x_point, SNAK[snake_rear].y_point,20,20);
//覆盖旧的蛇尾
SNAK[snake_rear].stat=0;
snake_rear=(snake_rear+1) %MAX_NUM;
}
总结
第一次使用带有操作系统的开发板完成一个小的项目,从纯裸机的开发,到操作系统的简单应用,从裸机程序到带有操作系统的的程序编写,对用户空间,内核空间有了简单的了解,用户空间的应用程序调用open()打开一个设备,再到内核空间通过系统调用(一堆函数)调用与硬件对应的驱动程序,实现了对外设的控制,实现了数据的获取,进而得到需要处理的数据,裸机的代码,与应用层的代码,有着很大的不同,裸机是操作一个一个的寄存器,而应用层则使用那些接口函数,在贪吃蛇这个项目的实现中,遇到了很多的问题,发现了自己的不足,对C语言的知识得到了巩固,也对嵌入式的这段学习有了简单的实际应用。
获取详细代码:关注该公众号呀!
LLP学嵌入式