基于Stm32f407 的贪吃蛇小游戏【正点原子-探索者开发板】

基于单片机stm32f407的单机小游戏----贪吃蛇小游戏

1.介绍

这是我花一个星期完成的一个简单地单机贪吃蛇小游戏项目,芯片是stm32f407,项目是基于正点原子-探索者开发板完成的,有需要的伙伴可以看看,其他开发板也区别不大,主要看算法。


一、运行效果

  1. 运行效果图
    运行效果图
    开始游戏
    游戏结束

二、软件设计

1.主程序main.c

main.c中,首先对系统的一些初始化,初始化完后开始游戏,游戏结束后按下按键up键,重新开始游戏。用到硬件有:led,lcd,按键,硬件随机数发生器,蜂鸣器(可选),串口,外部SRAM,flash(存放最高分)。

代码如下:

#include "snake.h"
int main(void)
{ 
	START:
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);					//初始化延时函数
	uart_init(115200);     				//初始化串口
	FSMC_SRAM_Init();//外部SRAM初始化
	my_mem_init(SRAMIN);//初始化内部内存池
	my_mem_init(SRAMEX);//初始化外部内存池
	my_mem_init(SRAMCCM);//初始化CCM内存池
	LED_Init();		        	//初始化LED端口
	LCD_Init();							//初始化LCD
	KEY_Init();							//初始化按键
	RNG_Init();							//初始化随机数发生器
	BEEP_Init();						//初始化蜂鸣器
	SNAKE_Init();  
	Game_Start();
	Game_End();
	while(KEY_Scan(0)!= WKUP_PRES);//死亡后按下按键重新开始游戏
	goto START;
}

2.包含头文件(snake.h)

此头文件主要是引入项目包含源文件的头文件,以及贪吃蛇行进方向定义,蛇体数据结构定义,函数声明。
代码如下:

#ifndef SNAKE__H
#define SNAKE__H
#include "sys.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "lcd.h"
#include "flash.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h" 
#include "math.h"
#include "rng.h"
#include "beep.h"
#include "bmp.h"
#include "malloc.h"
#include "sram.h"
enum POS{//行进方向
	LEFT=1,
	RIGHT,
	UP,
	DOWN
};
//蛇结构体
typedef struct SnakeNode{
	int x;//节点坐标x
	int y;//节点坐标y
	u16 color;//节点颜色
	struct 	SnakeNode *next,*prev;//结构采用双向链表,表头的前节点指向表尾
}SNAKE_t,*SNAKELIST;

void Game_Start(void);
void SNAKE_Init(void);
void Game_End(void);
#endif

3.SNAKE_Init()函数;

初始化,本函数主要是对游戏界面进行绘制,以及蛇的数据结构初始化。这里初始化蛇的初始长度为2。
代码如下:

void SNAKE_Init(void)
{
	int i=0;
	SNAKE_t *p;
	POINT_COLOR = BLUE;
	score=max_score=0;
	//STMFLASH_Write(FLASH_SAVE_ADDR,(u32*)&max_score,1);
	STMFLASH_Read(FLASH_SAVE_ADDR,(u32*)&max_score,1);//从flash中读出最大成绩
	printf("max_score :%d\r\n",max_score);
	//GUI 图形界面绘制
	LCD_DrawBMP(20,0,logo1);//在LCD屏幕上画logo图标
	LCD_DrawBMP(120,0,logo);
	LCD_DrawRectangle(290-2,40-2,480-10,40+30);
	LCD_ShowString(290,40,190,24,24,"Score:   0");
	LCD_DrawRectangle(290-2,40+32-2,480-10,40+30+32);
	LCD_ShowString(290,40+32,190,24,24,"Max_s:    0");//显示最大成绩
	LCD_ShowNum(290+6*12,40+32,max_score,5,24);
	LCD_DrawLine(2,up_y,480-2,up_y);
	
	POINT_COLOR = RED;
	LCD_DrawRectangle(2,up_y+2,480-2,800-2);
	LCD_DrawRectangle(3,up_y+3,480-3,800-3);
	POINT_COLOR=GREEN;
//	Head=pvPortMalloc(sizeof(SNAKE_t));//freeRTOS提供的空间分配函数,这里不使用
	Head=mymalloc(SRAMEX,sizeof(SNAKE_t));//分配空间
	//头部初始化
	Head->x=140;
	Head->y=up_y+40;
	Head->next=NULL;
	Head->prev=Head;
	Head->color=RED;
	p=Head;
	score++;
	for(i=1;i<2;i++)
	{
//		SNAKE_t *node=pvPortMalloc(sizeof(SNAKE_t));
		SNAKE_t *node=mymalloc(SRAMEX,sizeof(SNAKE_t));
		node->x=140;
		node->y=up_y+40;
		node->prev=p;
		node->next=NULL;
		node->color=lcd_discolor[RNG_Get_RandomRange(0,12)];
		p->next=node;
		p=p->next;
		Head->prev=p;
		score++;
	}
	CreatFood();//创建一个实物
	p=Head;
	while(p!=NULL)
	{
		LCD_DrawNode(*p);
		p=p->next;
	}
	LCD_ShowNum(290+6*12,40,score,5,24);//更新成绩
	//while(1);
}

4.CreatFood()函数;

创建食物,在SNAKE_Init()函数里边调用了一个CreatFood()函数,这个函数很简单,就是利用随机数发生器产生三个随机数,分别是坐标x,坐标y和节点颜色,然后调用绘制节点函数LCD_DrawNode();绘制出节点。
代码如下:

void CreatFood(void)
{
	u8 col=RNG_Get_RandomRange(0,11);
	food.x=RNG_Get_RandomRange(13,469);
	food.y=RNG_Get_RandomRange(up_y+5,789);
	food.color=lcd_discolor[col];
	printf("food : %d %d\r\n",food.x,food.y);
	LCD_DrawNode(food);
}

5.LCD_DrawNode()函数;

在CreatFood()函数里边调用了一个CreatFood()函数,这个函数首先对其颜色进行判断,如果为红色,则表示此节点为蛇头,蛇头放大1.5倍,然后循环绘制出节点。声明:红色在随机颜色表里边不存在。
代码如下:

void LCD_DrawNode(SNAKE_t snake)
{
	int i;
	float j=1;
	if(snake.color==RED)j=1.5;//如果是蛇头,节点放大1.5倍
	POINT_COLOR=snake.color;
	for(i=0;i<5*j;i++)
	{
		LCD_Draw_Circle(snake.x,snake.y,i);
	}
}

6.Game_Start()函数;

项目难点核心部分,重点是对指针的处理。指针是C语言最难理解的一部分,若看不懂也没关系,画个指针操作草图理解一下。本项目用双向链表数据结构存储贪吃蛇信息,其中蛇头的前节点指向蛇尾。
代码如下:

void Game_Start(void){
	u8 key=0;//按键捕获值
	float dis;//蛇头中心点与食物中心点之间的距离,用于判断是否吃到食物
	SNAKE_t *h=Head,*n=Head->prev,*newNode;
	while(1)
	{
		key=KEY_Scan(0);
		h=Head;
		//绘制蛇身
		while(h!=NULL){
				LCD_ClearNode(*h);
				h=h->next;
			}
		h=Head;
		if(key!=0)
		{
			h=Head;//蛇头
			n=Head->prev;//蛇尾
			//蛇身向前移动
			while(n!=h){
				n->x=n->prev->x;
				n->y=n->prev->y;
				n=n->prev;
			}
			switch(key)
			{
				//左
				case KEY2_PRES:
					//不能向反方向180度转
					if(SNAKE_POS!=RIGHT){
						SNAKE_POS=LEFT;
						h->x -=10;
					}
					break;
				//右
				case KEY0_PRES:
					if(SNAKE_POS!=LEFT){
						SNAKE_POS=RIGHT;
						h->x += 10;
					}
					break;
				//上
				case WKUP_PRES:
					if(SNAKE_POS!=DOWN){
						SNAKE_POS=UP;
						h->y -= 10;
					}
					break;
				//下
				case KEY1_PRES:
					if(SNAKE_POS!=UP){
						SNAKE_POS=DOWN;
						h->y += 10;
					}
					break;
			}
		}
		SNAKE_Move();
		//计算蛇头中心点和食物中心点坐标之间的距离
		 dis=sqrt((h->x - food.x)*(h->x - food.x)+(h->y - food.y)*(h->y - food.y));
		if(dis<2*5){//吃到食物,添加节点
			//newNode=pvPortMalloc(sizeof(SNAKE_t));//freeRTOS操作系统自带的空间分配函数,本实验未用到freertos
			newNode=mymalloc(SRAMEX,sizeof(SNAKE_t));//空间分配
			n=Head->prev;
			newNode->x=n->x;
			newNode->y=n->y+10;
			newNode->prev=n;
			newNode->next=NULL;
			newNode->color=food.color;
			n->next=newNode;
			n=n->next;
			h->prev=n;
//			BEEP=1;//这里可以打开,吃到食物蜂鸣器鸣一声
//			delay_ms(10);
//			BEEP=0;
			//Head=h;
			score++;
			LCD_ClearNode(food);//清楚节点
			CreatFood();//创建食物
		}
//		printf("food : %d %d\r\n",food.x,food.y);
		//显示分数
		LCD_ShowNum(290+6*12,40,score,5,24);//更新分数值
		//判断是否到达边界,到边界游戏结束
		if(h->x<6 || h->x>474 || h->y<up_y+12 || h->y>794){//判断蛇头是否到达边界,若出界则游戏结束
			return;
		}
		delay_ms(80);
	}
}

7.SNAKE_Move()函数;

蛇移动处理函数,SNAKE_POS变量记录蛇行进方向,周期时间内蛇身向行进方向移动一个单位。
代码如下:

void SNAKE_Move()
{
	SNAKE_t *h=Head,*n=Head->prev;
	h=Head;
	n=Head->prev;
	h=Head;
	POINT_COLOR=RED;
	//LCD_DrawRectangle(2,up_y+2,480-2,800-2);
	LCD_DrawRectangle(3,up_y+3,480-3,800-3);
	//蛇身向前移动
	while(n!=h){
		n->x=n->prev->x;
		n->y=n->prev->y;
		n=n->prev;
	}
	switch(SNAKE_POS)
	{
		case LEFT:
			h->x -=10;
			break;
		case RIGHT:
			h->x += 10;
			break;
		case UP:
			h->y -= 10;
			break;
		case DOWN:
			h->y += 10;
			break;
	}
	h=Head;
	//LCD_Fill(6,up_y+6,480-6,800-6,WHITE);
	LCD_DrawNode(food);//画食物
	while(h!=NULL){
		LCD_DrawNode(*h);
		h=h->next;
	}
}

8.Game_End()函数;

游戏结束,绘制游戏结束界面。
代码如下:

void Game_End(void)
{
		LCD_Fill(0,up_y,480-1,800-1,WHITE);
		POINT_COLOR=BRRED;
		LCD_DrawBMP(150,300,over);
  	    POINT_COLOR=BLUE;
		if(score>max_score){//更新最高分
			STMFLASH_Write(FLASH_SAVE_ADDR,(u32*)&score,1);//将最高分写入flash中
		}
		LCD_ShowString(220,700,200,48,24,"Press the up key to continue");
		
}

总结

本文简单介绍了基于stm32f407的贪吃蛇小游戏,对于其他型号的芯片可参考其中的算法自行移植,希望可以帮助到各位志同道合的朋友,嵌入式这个坑,要一脚踩到底。需要源码的朋友,请自行下载。

源码链接

项目名称:贪吃蛇小游戏
单片机:stm32f407 正点原子-探索者

  • 20
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

博创客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值