STM32+LCD实现简单的贪吃蛇小游戏

寒假放假回家,只能宅在家里,无聊之余,幸好带了一块开发板回来,以前做项目都是在网上找相似或者有关联的项目,把别人的代码拿过来,修改修改,拼拼凑凑出自己项目,既然无聊就自己动脑筋思考,自己动手写贪吃蛇的程序。至于制作贪吃蛇的思路嘛,就是贪吃蛇在行动过程中,只有头的位置发生了变化,所以只需要对头部坐标做特殊处理即可,而在吃到食物后头部坐标直接变为食物坐标就实现了贪吃蛇吃到食物后身长增加的效果。下面是视频效果:

STM32+LCD实现简单的贪吃蛇小游戏


因为自己开发板只有三个按键,所以不能当做上下左右,只有通过屏幕来控制贪吃蛇运动的方向。

视频录了之后又修改了一下代码,修复了吃不到边缘食物的问题,以及加上了碰到自己也游戏结束的规则。

下面是代码:`

snake.c

```c
#include "snake.h"
#include "lcd.h"
#include "timer.h"
#include "touch.h"
#include "stdlib.h"
#include "usart.h"

Snake snake;
extern u8 count;

/*
初始化蛇
*/
void snakeInit(){
	TIM3_Int_Init(4999,7199); //初始化定时器
	snake.length=3;           //初始长度为3
	
	snake.snakeX[0]=55;
	snake.snakeX[1]=55;
	snake.snakeX[2]=55;
	
	snake.snakeY[0]=55;
	snake.snakeY[1]=65;
	snake.snakeY[2]=75;
	
	snake.headX=snake.snakeX[0]; //记录下头部的位置
	snake.headY=snake.snakeY[0]; //记录下头部的位置
	snake.dir=1;  //设置运动方向
	snake.tpdir=1;//设置初始按键方向
	
	snake.life=1; //1:蛇还活着;0:蛇死亡
	
	generateFood();//生成食物
	
	refresh();    //调用函数显示出蛇和食物的位置
}
/*
每隔一秒将会执行一次,以此实现蛇的运动
*/
void snakeGo(int direct){
	u8 i;
	if(snake.life==1){
		if(abs(direct)!=abs(snake.dir)){	//如果按下的方向不是和运动的方向相同或相反
			snake.dir=direct; //将蛇运动的方向改变为按下的方向
		}else{
			direct=snake.dir;	//蛇运动的方向仍是以前运动的方向
		}
		switch(direct){
			case 1:
				snake.headY-=10;  //上:蛇的身体是有半径为5的圆组成,每运动一格的单位为10
			break;
			case -1:
				snake.headY+=10;  //下:蛇的身体是有半径为5的圆组成,每运动一格的单位为10
			break;
			case -2:
				snake.headX-=10;  //左:蛇的身体是有半径为5的圆组成,每运动一格的单位为10
			break;
			case 2:
				snake.headX+=10;  //右:蛇的身体是有半径为5的圆组成,每运动一格的单位为10
			break;
		}
		
	if(snake.headX==snake.foodX&&snake.headY==snake.foodY){ //如果吃到了食物
		snake.length++;  //长度加1
		
		for(i=1;i<snake.length;i++){ //除头部以外的坐标前移
			snake.snakeX[snake.length-i]=snake.snakeX[snake.length-i-1];
			snake.snakeY[snake.length-i]=snake.snakeY[snake.length-i-1];
		}
		snake.snakeX[0]=snake.foodX; //头部坐标等于食物的坐标
		snake.snakeY[0]=snake.foodY;	
		generateFood();  //再生成一个食物
	}else{
		for(i=1;i<snake.length;i++){
			snake.snakeX[snake.length-i]=snake.snakeX[snake.length-i-1];
			snake.snakeY[snake.length-i]=snake.snakeY[snake.length-i-1];
		}
		snake.snakeX[0]=snake.headX;
		snake.snakeY[0]=snake.headY;			
	}	
	}
	if(snake.headY<230&&snake.headY>10&&snake.headX<230&&snake.headX>10&&isNearSelf()){
		snake.life=1;
	}
	else{ //触碰到墙壁游戏结束
		dead();	
	}
}
/*
刷新屏幕上蛇和食物的位置
*/

void refresh(){
	if(snake.life==1){
	u8 i;
	LCD_Fill(11,11,229,229,WHITE);
	for(i=0;i<snake.length;i++){
		Draw_Circle_Solid(snake.snakeX[i],snake.snakeY[i],5);  //以算的蛇的坐标画半径为5的实心圆
	}
	Draw_Circle_Solid(snake.foodX,snake.foodY,5);
}
}
/*
开始游戏
*/
void GameStart(){
	while(1){
		tp_dev.scan(0);
		if(tp_dev.sta)			//触摸屏被按下
		{	
		 	if(tp_dev.x<lcddev.width&&tp_dev.y<lcddev.height)
			{	
				  //printf("%d %d\n\r",tp_dev.x,tp_dev.y);
				if(tp_dev.x>90&&tp_dev.x<150&&tp_dev.y<270&&tp_dev.y>240)snake.tpdir=1; //上
				if(tp_dev.x>90&&tp_dev.x<150&&tp_dev.y<310&&tp_dev.y>280)snake.tpdir=-1;//下
				if(tp_dev.x>10&&tp_dev.x<70&&tp_dev.y<290&&tp_dev.y>260)snake.tpdir=-2; //左
				if(tp_dev.x>170&&tp_dev.x<230&&tp_dev.y<290&&tp_dev.y>260)snake.tpdir=2;//右
				if(tp_dev.x>90&&tp_dev.x<150&&tp_dev.y<180&&tp_dev.y>150&&snake.life==0){
					snake.life=1;  //死了之后点击重新开始
					snakeInit();					
				}
  			   
			}
		}
		
		if(count==2){  //定时器设置的500ms的中断,1S运行一次蛇的运动函数
			count=0;			//重新计数
			snakeGo(snake.tpdir);
			refresh();	
		}
	}
}
/*
判断随机产生的食物是否处于蛇体内
*/
u8 isCover(u8 snakeX[],u8 snakeY[],u8 foodX,u8 foodY){
	u8 i;
	for(i=0;i<snake.length;i++){
		if(snakeX[i]==foodX&&snakeY[i]==foodY)
		{
		return 1;
		}
		
	}
	return 0;
}

/*
判断是否头部是否触碰到自己
*/
u8 isNearSelf(){
	u8 i;
	for(i=1;i<snake.length;i++){
		if(snake.snakeX[i]==snake.headX&&snake.snakeY[i]==snake.headY)
		{
		return 0;
		}	
	}
	return 1;
}
/*
产生随机的食物坐标
*/
void generateFood(){
		unsigned int ran=rand()%22+1; //因为贪吃蛇运动区域为10<x<230,10<y<230,取得1到22的随机数
		snake.foodX=(ran*2+1)*5;  //随机数取奇数再乘以5
		ran=rand()%22+1;
		snake.foodY=(ran*2+1)*5;		
	while(isCover(snake.snakeX,snake.snakeY,snake.foodX,snake.foodY)){
		ran=rand()%22+1; //因为贪吃蛇运动区域为10<x<230,10<y<230,取得1到22的随机数
		snake.foodX=(ran*2+1)*5;  //随机数取奇数再乘以5
		ran=rand()%22+1;
		snake.foodY=(ran*2+1)*5;	
	}		

}

/*
在头部撞到墙之后执行死亡程序
*/
void dead(){
	snake.length=0;
	
	snake.snakeX[0]=0;
	snake.snakeX[1]=0;
	snake.snakeX[2]=0;
	
	snake.snakeY[0]=0;
	snake.snakeY[1]=0;
	snake.snakeY[2]=0;
	
	snake.headX=snake.snakeX[0];
	snake.headY=snake.snakeY[0];
	snake.dir=0;  
	
	snake.life=0;	
	LCD_ShowString(80,50,200,16,16,"You dead!!!");
	LCD_Fill(90,150,150,180,BLUE);  //点击此区域重新开始游戏
	LCD_ShowString(92,160,200,16,16,"restart"); 
	
}


snake.h文件

#ifndef __SNAKE_H
#define __SNAKE_H
#include "sys.h"
#include "stdlib.h"

typedef struct  
{	
	u8 life;
	u8 headX;			
	u8 headY;		
	u8 snakeX[300];
	u8 snakeY[300];	
	u8 length;
	int dir;
	int tpdir;
	u8 foodX;
	u8 foodY;

}Snake; 	

void snakeInit(void);
void snakeGo(int direct);
void refresh(void);
void GameStart(void);
void generateFood(void);
void dead(void);
u8 isCover(u8 snakeX[],u8 snakeY[],u8 foodX,u8 foodY);
u8 isNearSelf(void);

#endif 

这个算是界面布局吧,调用这个函数画出游戏的界面来

void Load_Drow_Dialog(void)
{  
 	POINT_COLOR=BLUE;//设置字体为蓝色 
	LCD_DrawRectangle(10, 10, 230, 230);  //画矩形
	LCD_Fill(90,240,150,270,BLUE);        //画出‘上’区域
	LCD_Fill(10,260,70,290,BLUE);         //画出‘左’区域
	LCD_Fill(90,280,150,310,BLUE);        //画出‘下’区域
	LCD_Fill(170,260,230,290,BLUE);       //画出‘右’区域
  	POINT_COLOR=RED;//设置画笔红色 
}

在main文件中先调用initSnake()函数,然后调用GameStart()函数就可以开始游戏。
上面代码的注释已经写的很清楚,应该不需要详细每个函数的功能,值得一提的是取随机数机制,没有加上随机数种子,所以随机取出来的食物位置次序都是一样的。

鉴于找我要源码的人有点多,我上传到网盘上了,有需要的自行下载,源码的STM32芯片是STM32F103ZET6,LCD屏幕的驱动可能需要修改成自己屏幕适配的驱动。
链接:https://pan.baidu.com/s/1xOWsxzoCF_mTC_sCMb3rFw
提取码:osxj

本人菜鸟,个人能力有限,可能有不少没有考虑到的地方,思路仅凭参考

评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值