找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1716|回复: 1
收起左侧

STM32单片机打砖块游戏机制作 附源程序

[复制链接]
ID:970990 发表于 2022-12-14 10:44 | 显示全部楼层 |阅读模式
用STM32做一个游戏机

游戏视频地址:https://www.bilibili.com/video/BV1i84y1t7ro
制作出来的实物图如下:
游戏运行图片如下:
51hei.jpg

单片机主函数如下:

  1. int main()
  2. {  

  3.    
  4.     OLED_Init();
  5.     Key_Init();
  6.     InitAllVariable();
  7.     OLED_ShowString(1,1,"Press any key to");
  8.     OLED_ShowString(2,1,"start game");
  9.     while(1)
  10.     {
  11.         Game();
  12.     }
  13. }
复制代码




主函数中的 “Game()”用于开始游戏、暂停游戏以及游戏重新开始,内容如下:

  1. void Game(void)
  2. {
  3.     i=KEY_ValueReturn();
  4.     if(i)
  5.     {
  6.         i=0;
  7.         OLED_Clear();
  8.         while(1)
  9.         {
  10.             GameBody();
  11.             i=KEY_ValueReturn();
  12.             if(ReturnGameOverFlag()==1)//检测到游戏结束标志
  13.             {
  14.                 OLED_Clear();
  15.                 while(1)
  16.                 {
  17.                     if(Counter_1_FullFlagReturn()){j=!j;}
  18.                     if(j)
  19.                     {
  20.                         OLED_Clear();
  21.                     }else
  22.                     {
  23.                         OLED_ShowString(1,1,"SCORE:");
  24.                         OLED_ShowNum(1,7,GameScore,6);
  25.                         OLED_ShowString(3,1,"Press key 3 to");
  26.                         OLED_ShowString(4,1,"restart");
  27.                     }
  28.                     i=KEY_ValueReturn();
  29.                     if(i==3){ OLED_Clear();Restart=1; break;}
  30.                 }
  31.             }
  32.             if(ReturnGameOverFlag()==0 && Restart==0)

  33.             {
  34.                 if(i==3 )
  35.                 {
  36.                     OLED_Clear();
  37.                     while(1)
  38.                     {
  39.                         if(Counter_1_FullFlagReturn()){j=!j;}
  40.                         if(j){
  41.                             OLED_Clear();
  42.                         }else
  43.                         {
  44.                             OLED_ShowString(1,1,"     PAUSED ");
  45.                         }
  46.                         i=KEY_ValueReturn();
  47.                         if(i==3){OLED_Clear(); break;}
  48.                     }
  49.                 }
  50.             }      
  51.         }
  52.     }  
  53. }
复制代码



游戏主体Gamebody用于实现整个游戏的运行,代码如下:

  1. #include "stm32f10x.h"                  // Device header

  2. #include "KEY.h"

  3. #include "OLED.h"



  4. int8_t First_Enemy[9]={16,16,16,16,16,16,16,16,16};//每队第一个敌人的坐标
  5. int8_t Bullet_X[15],Bullet_Y[15];//储存子弹坐标
  6. uint32_t GameScore=0,LastScore;
  7. int8_t Restart=0;//重新开始标志位
  8. typedef struct {
  9.     uint8_t FireFlag;
  10.     int8_t ShowBullet;
  11.     uint8_t ShowEnemy;
  12.     uint8_t GameOver;//
  13.     uint8_t BulletSpeedUp;
  14. }FlagDef;

  15. typedef struct {
  16.     int8_t X;
  17.     int8_t Y;
  18. }PlayerDef;

  19. typedef struct {
  20.     int8_t X;
  21.     int8_t Y;
  22. }BulletDef;

  23. typedef struct {
  24.     uint8_t X;
  25.     uint8_t Y;

  26. }EnemyDef;

  27. PlayerDef My_loaction;

  28. FlagDef Flag;
  29. BulletDef Bullet_Location;

  30. BulletDef BulletNum;

  31. EnemyDef EnemyTarget;

  32. /****
  33.     *@brief 初始化所有结构体变量
  34.     *@parameter 无
  35.     *@ReturnValue 无
  36.     */
  37. void InitAllVariable(void)
  38. {
  39.     My_loaction.X=4;//初始化玩家X坐标
  40.     My_loaction.Y=1;//初始化玩家Y坐标
  41.     Flag.FireFlag=0;//初始化开火标志位
  42.     Flag.ShowBullet=0;//子弹显示
  43.     Flag.GameOver=0;//游戏结束标志
  44.     Flag.BulletSpeedUp=0;//子弹加速标志位
  45.     Bullet_Location.X=My_loaction.X;//初始化子弹位置
  46.     Bullet_Location.Y=My_loaction.Y;//初始化子弹位置
  47.     EnemyTarget.X=8;//首位敌军的位置
  48.     EnemyTarget.Y=16;//首位敌军的位置
  49. }

  50. /****
  51.     *@brief 按键返回值处理
  52.     *@parameter 无
  53.     *@ReturnValue 无
  54.     */
  55. void KeyNumReturn(void)
  56. {
  57.     int8_t i,temp=0;
  58.     temp=KEY_ValueReturn();
  59.     if(temp){i=temp;}
  60.     if(i==1){ My_loaction.X--; i=0; if(My_loaction.X<=1)My_loaction.X=1;}//1号键按下,玩//家上移一格
  61.     if(i==2){ My_loaction.X++; i=0; if(My_loaction.X>=8)My_loaction.X=8; }//2号键按下,玩//家下移一格
  62.     if(i==3) { i=0; }
  63.     if(i==4) { Flag.FireFlag=1; i=0; }  //四号键按下,开火标志位置一
  64. }


  65. /****
  66.     *@brief 显示玩家
  67.     *@parameter
  68.     *@ReturnValue
  69.     */
  70. void ShowPlayer(void)
  71. {
  72.     uint8_t i;
  73.     OLED_ShowBlock(My_loaction.X,My_loaction.Y);//根据坐标显示玩家
  74.     for(i=1;i<=8;i++)//清除玩家移动后,上一次留下的影子(oled不会自动清屏,玩家移动后,上一次的方块//依然会显示)
  75.     {
  76.         if(i!=My_loaction.X)
  77.         {
  78.             OLED_DeleteBlock(i,My_loaction.Y);
  79.         }
  80.     }
  81.    
  82. }

  83. /****
  84.     *@brief 子弹处理
  85.     *@parameter 无
  86.     *@ReturnValue 无
  87.     */
  88. void Bullet_process(void)
  89. {
  90.     if(Flag.FireFlag==1)//开火标志位
  91.     {
  92.         if(Flag.ShowBullet==0)
  93.         {
  94.             Bullet_Location.X=My_loaction.X;
  95.             Bullet_Location.Y=My_loaction.Y+1;
  96.             Flag.ShowBullet=1;//子弹显示标志位  

  97.         }else if(Flag.ShowBullet==1)
  98.         {
  99.             Flag.BulletSpeedUp=1;
  100.         }  
  101.     }  
  102.     Flag.FireFlag=0;
  103.    
  104.     LastScore=GameScore;
  105.     if(Flag.ShowBullet)
  106.     {
  107.         if(Bullet_Location.Y >=1 && Bullet_Location.Y <17)
  108.         {
  109.             if(Counter_0_FullFlagReturn() || Flag.BulletSpeedUp==1)
  110.             {
  111.                 Bullet_Location.Y++;
  112.                
  113.                 Flag.BulletSpeedUp=0;
  114.             }
  115.             if(Bullet_Location.Y>2)//手动清除上一次子弹所在位置,OLED不会自动清屏。
  116.             {
  117.                 OLED_DeleteBlock(Bullet_Location.X,Bullet_Location.Y-1);
  118.             }
  119.         }
  120.         EnemyTarget.Y=First_Enemy[Bullet_Location.X];//
  121.         if(Bullet_Location.Y<EnemyTarget.Y+1)//子弹没有碰到敌人或墙壁则根据坐标显示子弹
  122.         {
  123.             OLED_ShowBullet(Bullet_Location.X,Bullet_Location.Y);  
  124.         }else //子弹碰到敌人或墙壁则清除子弹
  125.         {
  126.             if(Bullet_Location.X<16){First_Enemy[Bullet_Location.X]++;}
  127.             Flag.ShowBullet=0;
  128.             GameScore++;//增加游戏分数
  129.         }
  130.         if(Bullet_Location.Y>=17)Flag.ShowBullet=0;
  131.     }

  132. }
  133. /****
  134.     *@brief 返回游戏结束标志位
  135.     *@parameter 无
  136.     *@ReturnValue 无
  137.     */
  138. uint8_t ReturnGameOverFlag(void)
  139. {
  140.     return Flag.GameOver;
  141. }

  142. /****
  143.     *@brief 根据关卡显示敌人
  144.     *@parameter 无
  145.     *@ReturnValue 无
  146.     */
  147. void Enemy_Process(void)
  148. {
  149.     uint8_t i,j;
  150.     if(Counter_EnemyMovingFlag()==1 )//时间到,让所有敌人往前走一步
  151.     {
  152.         for(i=1;i<=8;i++)
  153.         {
  154.             First_Enemy[i]--;
  155.         }
  156.         for(i=1;i<=8;i++)
  157.         {
  158.             for(j=First_Enemy[i];j<17;j++)
  159.             {
  160.                 OLED_ShowAnemy(i,j);
  161.             }
  162.         }
  163.     }else if(Counter_EnemyMovingFlag()==0)//时间没到,所有敌人原地不动
  164.     {
  165.         for(i=1;i<=8;i++)
  166.         {
  167.             for(j=First_Enemy[i];j<17;j++)
  168.             {
  169.                 OLED_ShowAnemy(i,j);
  170.             }
  171.         }
  172.     }
  173.     for(i=1;i<=8;i++)//判断是否有敌人走到了玩家面前
  174.     {
  175.         if(First_Enemy[i]<=2)
  176.         {
  177.             Flag.GameOver=1;//将游戏结束位置一;
  178.             for(j=1;j<=8;j++)
  179.             {
  180.                 First_Enemy[j]=16;
  181.                
  182.             }
  183.         }
  184.     }
  185. }

  186. /****
  187.     *@brief 根据分数控制蜂鸣器以及灯光
  188.     *@parameter 无
  189.     *@ReturnValue 无
  190.     */
  191. void Score_Process(void)
  192. {
  193.     if(GameScore>LastScore)
  194.     {
  195.         GPIO_ResetBits(GPIOC,GPIO_Pin_13);
  196.     }else
  197.     {
  198.         GPIO_SetBits(GPIOC,GPIO_Pin_13);
  199.     }
  200.    
  201.     if(Counter_0_FullFlagReturn())
  202.     {
  203.         GPIO_SetBits(GPIOC,GPIO_Pin_13);
  204.     }
  205. }

  206. /****
  207.     *@brief 游戏重新开始处理函数
  208.     *@parameter 无
  209.     *@ReturnValue 无
  210.     */
  211. void GameRestart(void)
  212. {
  213.     if(Restart==1 && Flag.GameOver==1)
  214.     {
  215.         OLED_Clear();
  216.         GameScore=0;
  217.         Flag.GameOver=0;
  218.         Restart=0;
  219.     }

  220. }

  221. /****
  222.     *@brief //游戏主体,在主函数中循环调用该函数,运行当前游戏。
  223.     *@parameter 无
  224.     *@ReturnValue 无
  225.     */
  226. void GameBody(void)
  227. {
  228.     KeyNumReturn();
  229.     ShowPlayer();
  230.     Enemy_Process();
  231.     GameRestart();
  232.     Bullet_process();
  233.     Score_Process();   
  234. }
复制代码

按键处理Key.c用于处理按键,让按键按下后产生对应的动作,本次的按键处理在《单片机定时器中断扫描按键程序思路详解》(http://www.51hei.com/bbs/dpj-225674-1.html)内容上进行修改,修改后Key.c内容如下:

  1. #include "stm32f10x.h"                  // Device header

  2. #include "delay.h"


  3. uint8_t KEY_Num,KEY_Value;
  4. static uint8_t KEY_LastState,KEY_NowState;
  5. uint8_t Counter_0,Counter_0_FullFlag;
  6. uint8_t Counter_1,Counter_1_FullFlag;
  7. //uint8_t TEST_i;

  8. uint16_t Counter_2=0,Counter_EnemyMoving;
  9. /****
  10.     *@brief 初始化按键
  11.     *@parameter 无
  12.     *@ReturnValue 无
  13.     */
  14. void Key_Init(void)
  15. {
  16.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);// 开启GPIOB时钟
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);// 开启GPIOB时钟
  18.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启TIM2时钟
  19.    
  20.     GPIO_InitTypeDef GPIO_InitStructure;//GPIO初始化
  21.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
  22.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  23.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  24.     GPIO_Init(GPIOB,&GPIO_InitStructure);
  25.    
  26.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
  27.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
  28.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  29.     GPIO_Init(GPIOC,&GPIO_InitStructure);
  30.     GPIO_SetBits(GPIOC,GPIO_Pin_13);
  31.    
  32.     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//TIM2定时器初始化

  33.     TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//不对内部时钟分频
  34.     TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
  35.     TIM_TimeBaseInitStructure.TIM_Period=20-1;
  36.     TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;//设置时钟计数周期为72000000/7200=10000us=1
  37.     TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//通用定时器没有重复计数器,设置为0
  38.     TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
  39.    
  40.     TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//打开TIM2的更新中断
  41.    
  42.     TIM_InternalClockConfig(TIM2);//选择内部时钟源
  43.    
  44.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组
  45.    
  46.     NVIC_InitTypeDef NVIC_InitStructure;
  47.     NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
  48.     NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
  49.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
  50.     NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
  51.     NVIC_Init(&NVIC_InitStructure);
  52.    
  53.     TIM_Cmd(TIM2,ENABLE);
  54. }

  55. //uint8_t Test_return(void)

  56. //{
  57. //  return TEST_i;
  58. //}
  59. /****
  60.     *@brief 判断哪个按键被按下
  61.     *@parameter 无
  62.     *@ReturnValue 无
  63.     */
  64. uint8_t KEY_Numble(void)
  65. {
  66.     uint8_t i=0;
  67.     if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==0) { i=1; }
  68.     if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==0) { i=2; }
  69.     if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0) { i=3; }
  70.     if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)==0) { i=4; }
  71. //  TEST_i=i;
  72.     return i;
  73. }
  74. /****
  75.     *@brief 返回按键的值
  76.     *@parameter 无
  77.     *@ReturnValue 根据对应的按键返回i(1~4)
  78.     */
  79. uint8_t KEY_ValueReturn(void)
  80. {
  81.     uint8_t i;
  82.     i=KEY_Value;
  83.     KEY_Value=0;
  84.     return i;
  85. }
  86. /****
  87.     *@brief 返回计数器Counter_0的状态
  88.     *@parameter 无
  89.     *@ReturnValue i:Counter_0计数满则返回1,否则返回0
  90.     */
  91. uint8_t Counter_0_FullFlagReturn(void)
  92. {
  93.     uint8_t i;
  94.     i=Counter_0_FullFlag;
  95.     Counter_0_FullFlag=0;
  96.     return i;
  97. }
  98. /****
  99.     *@brief 每间隔一定时间返回敌军前移标志位,控制敌军前移一格
  100.     *@parameter无
  101.     *@ReturnValue i:计数器满则返回1,否则返回0;
  102.     */
  103. uint8_t Counter_EnemyMovingFlag(void)
  104. {
  105.     uint8_t i;
  106.     i=Counter_EnemyMoving;
  107.     Counter_EnemyMoving=0;
  108.     return i;
  109. }
  110. /****
  111.     *@brief 返回计数器Counter_1的状态
  112.     *@parameter 无
  113.     *@ReturnValue i:Counter_1计满则为1,否则为0
  114.     */
  115. uint8_t Counter_1_FullFlagReturn(void)

  116. {
  117.     uint8_t i;
  118.     i=Counter_1_FullFlag;
  119.     Counter_1_FullFlag=0;
  120.     return i;
  121. }

  122. /****
  123.     *@brief //TIM2定时器中断函数
  124.     *@parameter 无
  125.     *@ReturnValue 无
  126.     */
  127. void TIM2_IRQHandler(void)//TIM2定时器中断函数
  128. {
  129.     KEY_LastState=KEY_NowState;//保存好按键上一次状态
  130.     KEY_NowState=KEY_Numble();//刷新按键当前状态
  131.    
  132.     Counter_0++;
  133.     Counter_2++;
  134.     if(Counter_0==25){Counter_0=0;Counter_0_FullFlag=1;Counter_1++;}

  135.     if(Counter_1==30){Counter_1=0;Counter_1_FullFlag=1;}
  136.     if(Counter_2==2500){Counter_EnemyMoving=1;Counter_2=0;}

  137.    
  138.     if(KEY_LastState ==1 && KEY_NowState ==0)//根据前后两次状态来决定返回值

  139.     {
  140.         KEY_Value=1;
  141.     }
  142.     if(KEY_LastState ==2 && KEY_NowState ==0)

  143.     {
  144.         KEY_Value=2;
  145.     }
  146.     if(KEY_LastState ==3 && KEY_NowState ==0)

  147.     {
  148.         KEY_Value=3;
  149.     }
  150.     if(KEY_LastState ==4 && KEY_NowState ==0)

  151.     {
  152.         KEY_Value=4;
  153.     }
  154.     TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
  155. }
复制代码

Keil代码下载: 23-打砖块游戏.7z (180.88 KB, 下载次数: 25)

评分

参与人数 1黑币 +90 收起 理由
admin + 90 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:1082094 发表于 2023-6-5 14:31 来自手机 | 显示全部楼层
为什么不能左右移动
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表