stm32f103贪吃蛇lcd与labview控制

本文详细描述了一款在320x240LCD屏上运行的贪吃蛇游戏的实现,包括蛇的初始化、移动、食物生成、游戏结束条件判断以及使用外部中断控制的按键方向控制。同时提到了如何通过LabVIEW扩展实现更多按键控制和串口通信。
摘要由CSDN通过智能技术生成

一、界面初始化

        在上方显示得分,下方进行游戏,吃到一个食物加十分。

               

        用一个结构体表示蛇的坐标,枚举表示方向

enum DIRECTION
{
	 up,
	 down,
	 left,
	 right
};

typedef struct
{
    int x;     
		int y;
		
} _snake;
void snake_init(void)
{
			POINT_COLOR=GREEN;
			LCD_ShowString(40,20,100,20,24,"SCORE:");
			LCD_ShowNum(160, 20, 0, 3, 24);
			LCD_DrawLine(0,79, 240, 79);
				snake[0].x=1;
			  snake[0].y=0;//蛇第一个点坐标
				snake[1].x=0;
			  snake[1].y=0;	//蛇第二个点坐标
				dir=right;//蛇头的方向
				length=2;//蛇长度
			draw_point(snake[0].x, snake[0].y,BLACK) ;
			draw_point(snake[1].x, snake[1].y,BLACK) ;
			food();//生成食物
}

        我使用的是320X240的lcd屏,将屏幕竖屏分为两部分,下半部分游戏部分为240X240,这里设置蛇的每一个节点大小为16X16的大小,横为x,竖为y,即为15X15的大小棋盘。

void draw_point(u16 x, u16 y, u16 color)//画蛇的节点
{
	for(int i=0;i<snake_point;i++)
	{
		for(int j=0;j<snake_point;j++)
		{
			LCD_Fast_DrawPoint( snake_point*x+i, snake_point*y+80+j,color); 
		}
	}

}

        宏定义引出蛇的每一个结点像素大小与最终胜利的得分(这里初始蛇身为2即当蛇的长度为32时胜利,吃到一个食物得10分)

#define snake_point 16   //蛇节点大小
#define win_score 300    //最终胜利得分

        定义初始蛇长度以及一个结构体数组,数组大小为棋盘大小。例如节点大小为16,棋盘的横纵数量为240/16=15,把整个棋盘吃满蛇的长度为15的平方即225,从零开始计数的,减去一即可。

_snake snake[(240/snake_point)*(240/snake_point)-1];
u16 length=2;

         左边是16x16的,右边是8x8的

​​​​​​​​​​​​​​

二、食物生成

使用rand()函数产生随机数

void food(void) //生成食物
{
	do
	{
	food_x=(rand()%(240/snake_point));                
    food_y=(rand()%(240/snake_point));  //根据蛇的节点大小生成食物,此时节点为16X16,所以生成0-14的随机数
	
	}
	while(LCD_ReadPoint(food_x*snake_point, food_y*snake_point+80)==BLACK); //检查该点是否为空位
	draw_point(food_x,food_y,RED);		//画食物
}

三、游戏结束

        当蛇越界以及吃到自己身体时游戏结束,在屏幕上显示“GAME OVER”

        当得分到达设定的分数时游戏胜利,在屏幕上显示“YOU WIN”

u16 snake_over(void)
{
	//判断蛇有没有越界
	if(snake[0].x>(240/snake_point-1) || snake[0].y>(240/snake_point-1) || snake[0].x<0 || snake[0].y<0)
		{
			POINT_COLOR=RED;
			LCD_ShowString(60,140,100,40,24,"GAME OVER");
			return 1;
		}
		//判断蛇头有没有吃到自己身体
	for(int i=1;i<length;i++)
		{
			if(snake[0].x==snake[i].x && snake[0].y==snake[i].y)
			{
						POINT_COLOR=RED;
				LCD_ShowString(60,140,100,40,24,"GAME OVER");
				return 1;
			}
	
		}
	
	if((length-2)*10==win_score)
		{
			POINT_COLOR=RED;
			LCD_ShowString(60,140,100,40,24," YOU WIN ");
			return 1;
		}

	return 0;
}

四、蛇的移动

        蛇每一步移动时,先将最后一个节点保存下来,然后从后到前依次更新每个节点的位置,随后判断蛇的方向,根据方向更新蛇头的坐标。如果吃到食物则长度加一并且将之前保存的节点作为尾巴,如果没吃到食物,则删除。最后判断游戏是否结束即可。

        例如此时蛇的长度为3,分别为snak0,snake1,sanke2,先将snake3的坐标保存,然后从后往前让snake2的坐标等于snake1的坐标,snake1的坐标等于sanke0的坐标,再判断蛇的方向,根据方向更新蛇头sanke0的坐标,随后判断是否吃到食物,如果吃到食物,则让snake3的坐标为之前保留snake2的坐标。

        每一步移动过程只需要画出蛇头以及是否删除蛇尾即可。吃到食物保留之前的蛇尾,没吃到就删除。

void snake_move(void)
{
	u16 snake_lastx;
	u16 snake_lasty;
	snake_lastx=snake[length-1].x;
	snake_lasty=snake[length-1].y;   //将蛇的最后一个节点保存下来
		for(int i=length-1;i>0;i--)
		{
			snake[i].x=snake[i-1].x;
			snake[i].y=snake[i-1].y;     //从后到前将除了蛇头之外的节点依次更新
		}
	switch (dir)                     //判断方向,更新蛇头
    {
    	case up:    snake[0].y--;   					 
    					    break;
    	case down:  snake[0].y++;
     			  	    break;
    	case left:  snake[0].x--;
      			      break;
      case right: snake[0].x++;
      			      break;
	  }
		draw_point(snake[0].x,snake[0].y,BLACK);  //画蛇头节点
	if(snake[0].x==food_x && snake[0].y==food_y) //如果吃到食物则蛇长度加一得分加10并且保留之前的节点并变为尾巴,否则删除
	{
		length++;
		LCD_ShowNum(160, 20, (length-2)*10, 3, 24);
		snake[length-1].x=snake_lastx;
		snake[length-1].y=snake_lasty;
		food();
	}
	else
		{	
			draw_point(snake_lastx,snake_lasty,WHITE);//背景是白色的,所以画白色就表示删除
		}

	if(snake_over()==1)     //判断游戏是否结束
		{
		while(start_flag==0); //等待游戏重新开始信号
		}
	if(start_flag==1)
		{
			LCD_Clear(WHITE); //清屏
			snake_init();     //初始化
			start_flag=0;     //清空标志位
		}
}

        main函数只需要延时每一步的移动即可,延时大小代表蛇的移动速度。

 int main(void)
 {	 

	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(9600);	 	//串口初始化为9600
 	LED_Init();			     //LED端口初始化
	LCD_Init();
	EXTIX_Init();		 	//外部中断初始化
	snake_init();
  	
  while(1) 
		{		 
		snake_move();
		//LED0=!LED0;				   		 
		delay_ms(400);
		} 
	}

五、控制方向

        用四个按键来分别控制蛇的移动方向,这里用外部中断的方式来控制,以保证程序运行时能够及时改变方向,当每一个按键按下时,要确保此时的方向不是对应的反方向,否则会吃到自己。

        这里进行上下方向改变的时候不是简单的判断是否为反方向,是因为如果按键按快了,连续改变了两次方向,而此时蛇并没有移动,会吃到自己。所以当进行上下方向改变的时候,我们判断蛇头与蛇的第二个节点是否在用一竖轴上,即他们的x坐标是否相同,如果相同,则不能改变方向。判断左右同理。

//外部中断0服务程序 
void EXTI0_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(WK_UP==1)	 	 //WK_UP按键
	{	
			if( snake[0].x != snake[1].x) //这里不用dir!=up;因为如果按快了会改变两次方向,而                                                                                        
                                          //此时蛇并没有移动,所以会吃到自己
				dir=down;	
	}
	EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
}
 
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY2==0)	  //按键KEY2
	{
		if( snake[0].x != snake[1].x)
		dir=up;
	}		 
	EXTI_ClearITPendingBit(EXTI_Line2);  //清除LINE2上的中断标志位  
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY1==0)	 //按键KEY1
	{				 
		if(snake[0].y != snake[1].y)
			dir=right; 
	}		 
	EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志位  
}

void EXTI4_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY0==0)	 //按键KEY0
	{
		if(snake[0].y != snake[1].y)
		dir=left;
	}		 
	EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志位  
}

六、labview扩展

        由于硬件资源受限,我的开发板只有三个按键,所以这里使用labview模拟几个按键使用。

        上下左右四个按键控制方向,start按键控制游戏重启。

        labview与单片机使用串口通信传递数据,当某一个按键按下时,向单片机发送数据,再由单片机处理接收到的数据进行操作。

        labview需要使用VISA控件,首先配置串口参数,创建输入控件即可。

        在条件结构里发送数据。

        读取从键盘输入的值,在进行判断,这里用WASD来控制上下左右,回车键重启游戏。只需要判断从键盘获取的值是否为我们想要的值即可,随后进行处理。

        将按键的动作改为单击触发,这样按下一次表示触发一次。

      

单片机在串口中断里判断接收到的数据,随后进行方向与重启处理。

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}
		if(Res==0 && snake[0].x != snake[1].x)
			dir=up;	
		if(Res==1 && snake[0].x != snake[1].x)
			dir=down;	
		if(Res==2 && snake[0].y != snake[1].y)
			dir=left;	
		if(Res==3 && snake[0].y != snake[1].y)
			dir=right;	
		if(Res==5 )
			start_flag=1;//重新开始标志
     } 

七、总结

        在lcd屏上玩贪吃蛇,可以由按键控制,也可以由labview控制,可以由电脑键盘控制。

        第一次写博客,记录一下《*……*》

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值