单片机学习小组
直播中

卢表镜

7年用户 1004经验值
私信 关注

怎样去设计一种基于stm32f103c8t6的贪吃蛇小游戏呢

基于STM32f103c8t6的贪吃蛇小游戏是由哪些部分组成的?
怎样去设计一种基于stm32f103c8t6的贪吃蛇小游戏呢?



回帖(1)

刘双喜

2022-2-21 11:28:56
这是一个几个星期之前的小项目,参考修改了一下网上和正点原子的例程。
功能:贪吃蛇小游戏
单片机:stm32f103c8t6

用到的硬件资源:
1.LED指示灯
2.矩阵键盘
3.OLED模块
4.通用定时器

  软件设计:
1.矩阵键盘(中断方式)前面文章有介绍
2.定时器中断:用于更新游戏界面
3.贪吃蛇设计:

OLED的像素点是128*64的,为了可以显示清晰,在这里把游戏的点坐标映射为32x12(map【32】【12】),每个坐标占用16个像素点,前16行像素点用于显示分数。


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


清除界面函数

void GUI_Clear(int map[32][12])//界面清除
{
   int i,j;
        for(i=0;i<32;i++)
        {
                for(j=0;j<12;j++)
                {
          map[j]=0;
                }
        }
}
创建地图函数:填充游戏界面的边界坐标,并将边界坐标的值存入map数组


void Creat_map(int map[32][12])//创建地图
{
        int i,j;
        for(i=0;i<12;i++)
        {
                for(j=0;j<32;j++)
                {
                   if(i==0||i==11)
                 {
                   map[j]=-2;
                 }
                 if(j==0||j==31)
                 {
                   map[j]=-2;
                 }
                }
        }
}
绘制地图函数:用画点函数将坐标绘出来,y坐标+16是因为前16行用来显示分数,16行以后为游戏界面


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_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);  
               
                }
          }
       
        }
}
刷新界面函数:根据map数组里面的值一次性刷新游戏界面内的点坐标

void GUI_Refresh(int map[32][12])//界面刷新
{
        int i,j,temp;
        for(i=0;i<32;i++)                 
        {
                for(j=0;j<12;j++)
                {
                   temp=map[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;
           }                  
                }
        }
        OLED_Refresh_Gram();
       
}
初始化蛇和地图函数:初定蛇长度为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坐标,蛇头坐标
   for(i=1;i     {
        snake.snake_Grid[0]=snake.snake_Grid[0][0]-i;
        snake.snake_Grid[1]=snake.snake_Grid[0][1];  //给刚开始的蛇身几个初始坐标
    }
    Creat_map(map);
}
画蛇函数:将二维数组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     {
                x=snake.snake_Grid[0];
                y=snake.snake_Grid[1];
        map[x][y]=2;      
    }


}
按键处理函数:自定义矩形键盘值控制蛇的方向

void Get_Command()//获取键盘值
{
        u8 key;
        key=KeyValue;       
                switch(key)
                {
                        case 5:if(snake.direction!=RIGHT)         //左
                   snake.direction=LEFT;                       
                   break;
                        case 7:if(snake.direction!=LEFT)           //右
                   snake.direction=RIGHT;                       
                   break;
                        case 2:if(snake.direction!=DOWN)        //上
                   snake.direction=UP;
                   break;
                        case 6:if(snake.direction!=UP)         //下
                   snake.direction=DOWN;
                   break;               
                }   
}
蛇的移动函数:蛇身移动就让其坐标等于前一个坐标的值,蛇头则根据蛇的方向就行移动

void Move()//移动
{
    int i;
        map[snake.snake_Grid[snake.length-1][0]][snake.snake_Grid[snake.length-1][1]]=0;//清除尾巴
        if(eated)               //如果吃到了食物
    {
        snake.length++;
        eated=false;        //设置为false,不然无限变长
    }
    for(i=snake.length-1;i>0;i--)    //从尾巴开始,蛇身每一个点的位置等于它前面一个点的位置
    {
        snake.snake_Grid[0]=snake.snake_Grid[i-1][0];
        snake.snake_Grid[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;
    }       
}
生成食物函数:在游戏界面内空位生成食物点坐标,并将食物坐标存入map数组

int Chek(int i,int j)//检查地图空位
{
        if(map[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[j]=-1;//画出食物
}
判断蛇吃食物:蛇吃到食物后通过eated标记状态(传递给move函数使蛇身+1)、分数+1、重新生成食物

bool GameOver()//游戏结束
{
        bool isGameOver=false;
        int sx=snake.snake_Grid[0][0],sy=snake.snake_Grid[0][1],i;//蛇头坐标
        for(i=1; i     {
        if(snake.snake_Grid[0]==sx&&snake.snake_Grid[1]==sy)
            isGameOver=true;
    }
        if(snake.snake_Grid[0][0]==31||snake.snake_Grid[0][0]==0||
                snake.snake_Grid[0][1]==11||snake.snake_Grid[0][1]==0)        //判断有没有撞墙
                isGameOver=true;
    return isGameOver;
}
判断游戏是否结束:

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


}
显示分数:

void TIM4_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;


        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
       
        //定时器TIM4初始化
        TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值       
        TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
        TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

        TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断


        //中断优先级NVIC设置
        NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  //TIM4中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
        NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器




        TIM_Cmd(TIM4, ENABLE);  //使能TIMx                                         
}
//定时器4中断服务程序
void TIM4_IRQHandler(void)   //TIM4中断
{
          if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
                {
                 TIM_ClearITPendingBit(TIM4, TIM_IT_Update  );  //清除TIMx更新中断标志
                 Move();
                 Eat_Food();
                 drawSnake();
                }
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分