基于stm32f103c8t6与OLED实现的贪吃蛇游戏

一、前言

本篇实现了一个使用c8t6驱动0.96寸OLED的贪吃蛇游戏。也可以使用rct6,就是改改启动文件,芯片类型,宏定义等等。需要完整代码的可以私信我。

二、硬件介绍

1、0.96寸OLED

我是用的是四线IIC接口的OLED

基于stm32f103c8t6与OLED实现的贪吃蛇游戏_第1张图片

2、stm32f103c8t6

基于stm32f103c8t6与OLED实现的贪吃蛇游戏_第2张图片

三、硬件连接

B5 -> OLED上的SDA

B6 -> OLED上的SCL

B8 -> 左按键

3.3v -> 左按键

B9 -> 右按键

3.3v -> 右按键

B10 -> 下按键

3.3v -> 下按键

B11 -> 上按键

3.3v -> 上按键

B12 -> 结束按键

3.3v -> 结束按键

R -> 复位按键

GUD -> 复位按键

四、主要代码实现

1、在OLED屏幕上显示/删除一个点

该代码主要实现在指定的x,y上打印一个点,将它存放在自定义的OLED_GRAM中,最终通过OLED_Refresh他们一并刷新到OLED上。

void OLED_DrawPoint(u8 x,u8 y)
{
    u8 pos,bx,temp=0;
    if(x>127||y>63)return;//超出范围了.
    pos=7-y/8;
    bx=y%8;
    temp=1<<(7-bx);
    OLED_GRAM[x][pos]|=temp;
}
void OLED_DrawNoPoint(u8 x,u8 y)
{
    u8 pos,bx,temp=0;
    if(x>127||y>63)return;//超出范围了.
    pos=7-y/8;
    bx=y%8;
    temp=1<<(7-bx);
    OLED_GRAM[x][pos]&=~temp; 
}

2、主要结构体与变量的定义

因为0.96寸OLED屏幕是128*64,将每2*2的四个OLED上的节点看做蛇的一个节点,所以定义地图大小为32*12

#define MAXLENGTH  100    //蛇的最大长度
    int map[32][12]={0};//地图大小  x,y(一个坐标为4*4个像素点)     实际按4倍尺寸放大后地图大小为128*48个像素点
    int score;            //分数
    bool eated=false;      //蛇吃到食物的标记
    u8 KeyValue=0;        //获取按键值
    struct {       
    int snake_Grid[MAXLENGTH][2];      //二维数组,行坐标表示蛇节点,列表示当前节点的x,y坐标
    int length;    //蛇的长度
    int direction;//蛇的方向
}snake;                          //定义结构体变量snake

int tailX, tailY; //标记尾部位置
int FoodX, FoodY; //标记食物位置

3、地图各个部分的输出

//创建地图函数
void Creat_map()//创建地图
{
	int i,j;
	for(i=0;i<12;i++)
	{
		for(j=0;j<32;j++)
		{
  		 if(i==0||i==11)
		 {
		   map[j][i]=-2;
		 }
		 if(j==0||j==31)
		 {
		   map[j][i]=-2;
		 }
		}
	}
}

//绘制地图函数
void Paint_Map(int x,int y)//绘制地图点坐标
{
	int i,j;
	for(i=4*y;i<4*y+4;i++)     
	{
	  for(j=4*x;j<4*x+4;j++)
	  {
		    OLED_DrawPoint(j,i+16); 
	  }
	
	}

}

//绘制蛇头函数
void Paint_Head(int x,int y )//绘制蛇头点坐标
{
	int i,j;
	for(i=4*y;i<4*y+4;i++)     
	{
	  for(j=4*x;j<4*x+4;j++)
	  {
		if(i==4*y||i==4*y+3)
		{
			
			 OLED_DrawPoint(j,i+16);  
			
		}
		if(j==4*x||j==4*x+3)
		{
			
			 OLED_DrawPoint(j,i+16);  
		
		}
	  }
    }
}

//绘制蛇身函数
void Paint_Body(int x,int y )//绘制蛇身函数
{
	int i,j;
	for(i=4*y;i<4*y+4;i++)     
	{
	  for(j=4*x;j<4*x+4;j++)
	  {
		if(i==4*y||i==4*y+3)
		{
			
			 OLED_DrawPoint(j,i+16);  
			
		}
		if(j==4*x||j==4*x+3)
		{
			
			 OLED_DrawPoint(j,i+16);  
		
		}
	  }
    }
}

//绘制食物坐标函数
void Paint_Food(int x,int y )//绘制食物点坐标
{
	int i,j;
	for(i=4*y;i<4*y+4;i++)     
	{
	  for(j=4*x;j<4*x+4;j++)
	  {
		
	   if(i==4*y+1||i==4*y+2)
		{
			
			 OLED_DrawPoint(j,i+16);  
			
		}
	   if(j==4*x+1||j==4*x+2)
		{
			
			 OLED_DrawPoint(j,i+16); 
		
		} 
	  }
	
	}
    // 更新食物的坐标
    FoodX = x;
    FoodY = y;
}

void Paint_Clean(int x,int y)//取消地图点坐标
{
	int i,j;
	for(i=4*y;i<4*y+4;i++)     
	{
	  for(j=4*x;j<4*x+4;j++)
	  {
		    OLED_DrawNoPoint(j,i+16); 
	  }
	
	}

}
//清除界面函数
void GUI_Clear()//界面清除
{
   int i,j;
	for(i=0;i<32;i++)
	{
		for(j=0;j<12;j++)
		{
          map[i][j]=0; 
		}
	}
}

//画蛇函数:将二维数组snake.snake_Grid里面的各个蛇坐标的值存入map数组
void drawSnake()            //画蛇
{
    int i,x,y;
	//蛇头
		x=snake.snake_Grid[0][0];
		y=snake.snake_Grid[0][1];
        map[x][y]=1; 
    //蛇身
	for(i=1;i

4、界面刷新函数

通过这个函数来输出这个游戏的所有显示数据。

//刷新界面函数:根据map数组里面的值一次性刷新游戏界面内的点坐标
void GUI_Refresh()//界面刷新
{
	int i,j,temp;
	for(i=0;i<32;i++)                 
	{
		for(j=0;j<12;j++)
		{
		   temp=map[i][j];
         switch (temp)
		 {
			 case 2:	      
			   Paint_Body(i,j);  
		          break;
			 case  1:		   
		       Paint_Head(i,j);  
		          break;
			 case -2:		   
		       Paint_Map(i,j);  
		          break;
			 case-1:		   
		       Paint_Food(i,j);  
		          break;
			 case 0:		   
		       Paint_Clean(i,j);
		    	  break;
	   }		   
		}
	}
}

5、蛇的初始化

初始化蛇长度为5、方向向右、蛇头坐标位于(7,5)。

//初始化蛇和地图函数:初定蛇长度为5、方向向右、蛇头坐标位于(7,5)
void Snake_Init()//蛇及地图初始化
{  
	int i;
   snake.length=5;
   snake.direction=RIGHT;
   score=0;
   snake.snake_Grid[0][0]=7;//x坐标,蛇头坐标
   snake.snake_Grid[0][1]=5;//y坐标,蛇头坐标
   tailX = snake.snake_Grid[snake.length - 1][0]; // Initialize tail coordinates
   tailY = snake.snake_Grid[snake.length - 1][1];
   for(i=1;i

6、获取键盘值

初始在snake.h中宏定义了方向

#define RIGHT 1
#define LEFT 2
#define DOWN 3
#define UP 4

使用中断的方法读取按键对应引脚的上升沿,将值赋给全局变量KeyValue;

//按键处理函数:自定义矩形键盘值控制蛇的方向
void Get_Command()//获取键盘值
{
	u8 key=1;
	key=KeyValue;	
		switch(key)
		{
			case 1:if(snake.direction!=RIGHT)         //左
                   snake.direction=LEFT;			
                   break;
			case 2:if(snake.direction!=LEFT)           //右
                   snake.direction=RIGHT;			
                   break;
			case 3:if(snake.direction!=DOWN)        //上
                   snake.direction=UP;
                   break;
			case 4:if(snake.direction!=UP)         //下
                   snake.direction=DOWN;
                   break;		
		}    
}

7、中断部分函数为下

void EXTI9_5_IRQHandler(void)
{					 
    if(KEY8==1)		//按键0
	{
        KeyValue=1;
        EXTI->PR=1<<8;
	}else if(KEY9==1){
        KeyValue=2;
        EXTI->PR=1<<9;
    }
 	     //清除LINE5上的中断标志位  
}
//外部中断15~10服务程序
void EXTI15_10_IRQHandler(void)
{					 
    if(KEY10==1)		//按键1
	{
        KeyValue=3;
        EXTI->PR=1<<10;
	}else if(KEY11==1)		//按键1
	{
        KeyValue=4;
        EXTI->PR=1<<11;
	}else if(KEY12==1)		//按键1
	{
        Show_Endface();
        KeyValue=5;
        EXTI->PR=1<<12;
	}
 	 //清除LINE15上的中断标志位  
}
//外部中断初始化程序
//初始化PA0,PC5,PA15为中断输入.
void EXTI_Init(void)
{
	KEY_Init();
	Ex_NVIC_Config(GPIO_B,8,RTIR); 		//上升沿触发
	Ex_NVIC_Config(GPIO_B,9,RTIR);		//上升沿触发
	Ex_NVIC_Config(GPIO_B,10,RTIR);		//上升沿触发
    Ex_NVIC_Config(GPIO_B,11,RTIR);		//上升沿触发
    Ex_NVIC_Config(GPIO_B,12,RTIR);		//上升沿触发

	MY_NVIC_Init(2,0,EXTI9_5_IRQn,2);    	//抢占2,子优先级2,组2
    MY_NVIC_Init(2,1,EXTI15_10_IRQn,2);    	//抢占2,子优先级2,组2 
}

8、蛇的移动函数

主要思想就是先判断蛇头结点是否与食物结点重合,如果没重合就删掉尾节点,头结点根据方向移动;如果重合,就不删掉尾节点,头结点根据方向移动。然后前结点的坐标等于后结点的坐标。

//蛇的移动函数:蛇身移动就让其坐标等于前一个坐标的值,蛇头则根据蛇的方向就行移动
void Move() {
    int i;
    int headX = snake.snake_Grid[0][0];
    int headY = snake.snake_Grid[0][1];
    tailX = snake.snake_Grid[snake.length - 1][0]; // 存储当前尾巴的坐标
    tailY = snake.snake_Grid[snake.length - 1][1];
    // 判断蛇头是否与食物坐标重合
    if (headX == FoodX && headY == FoodY) {
        snake.length++;
        eated = true;  // 设置为true,表示吃到了食物
        score += 1;
        
        Food(); // 生成新的食物
    } else {
        map[tailX][tailY] = 0; // 清除尾巴
    }

    for (i = snake.length - 1; i > 0; i--) {
        snake.snake_Grid[i][0] = snake.snake_Grid[i - 1][0];
        snake.snake_Grid[i][1] = snake.snake_Grid[i - 1][1];
    }

    switch (snake.direction) {
        case UP:
            snake.snake_Grid[0][1]--;
            break;
        case DOWN:
            snake.snake_Grid[0][1]++;
            break;
        case LEFT:
            snake.snake_Grid[0][0]--;
            break;
        case RIGHT:
            snake.snake_Grid[0][0]++;
            break;
    }

    drawSnake();
}

9、生成食物

//生成食物函数:在游戏界面内空位生成食物点坐标,并将食物坐标存入map数组
int Chek(int i,int j)//检查地图空位
{
	if(map[i][j]!=0)
	{
		return 0;
	}
	return 1;   //是空位就返回1
}

void Food()//生成食物
{
	int i,j;
	do
	{
		i=(rand()%30)+1;                 //生成1~30之间的一个数
        j=(rand()%10)+1;                 //生成1~10之间的一个数
	
	}
	while(Chek(i,j)==0);                //检查该点是否为空位
	map[i][j]=-1;//画出食物
}

10、判断游戏是否结束

bool GameOver()//游戏结束
{
	bool isGameOver=false;
	int sx=snake.snake_Grid[0][0],sy=snake.snake_Grid[0][1],i;//蛇头坐标
	for(i=1; i

11、显示分数

//显示分数
void Show_Score()//显示分数
{
	OLED_ShowString(30,6,"Score:",16);
	OLED_ShowNum(80,6,score,2,16);

}

五、效果展示

基于stm32f103c8t6与OLED实现的贪吃蛇游戏_第3张图片基于stm32f103c8t6与OLED实现的贪吃蛇游戏_第4张图片

你可能感兴趣的:(STM32,stm32,游戏,嵌入式硬件)