基于STC89C52RC和8X8點陣屏、獨立按鍵的小游戲《打磚塊》

目錄

  • 系列文章目錄
  • 前言
  • 一、效果展示
  • 二、原理分析
  • 三、各模塊代碼
    • 1、8X8點陣屏
    • 2、獨立按鍵
    • 3、定時器0
    • 4、定時器1
  • 四、主函數
  • 總結

系列文章目錄


前言

用的是普中A2開發板,外設有:8X8LED點陣屏、獨立按鍵。

【單片機】STC89C52RC
【頻率】12T@11.0592MHz

效果查看/操作演示:B站搜索“甘騰勝”或“gantengsheng”查看。
源代碼下載:B站對應視頻的簡介有工程文件下載鏈接。

一、效果展示

1、顯示游戲名稱。
在這里插入圖片描述

2、可以調節速度。
在這里插入圖片描述

3、游戲準備階段可以調節初始位置。
在這里插入圖片描述

4、真隨機確定發射方向。
在這里插入圖片描述

5、可以暫停游戲。
在這里插入圖片描述

6、不管初始位置在哪里,最終都可以全部打掉方塊。
在這里插入圖片描述

7、游戲結束后循環滾動顯示得分
在這里插入圖片描述

二、原理分析

1、基于單人彈球游戲進行更改

基于51單片機和8X8點陣屏、獨立按鍵的單人彈球小游戲

擋板移動、球的移動、球的碰撞等的簡單原理介紹可以看一下上面文章。

2、與磚塊的碰撞

這個稍復雜一些,但也不難。

用兩個變量DirectionX、DirectionY保存球在水平、豎直這兩個方向上運動的方向,球移動前,先檢測上下左右,如果是磚塊,則會發生碰撞,導致方向改變。

例如,假設原來X方向(水平方向)是向右運動的,如果右側有磚塊,則DirectionX的值由2變成1,2表示向右運動,1表示向左運動。其他類似。

還要考慮特殊情況,例如,球向右上方運動,如果上和右都沒磚塊,但右上方有磚塊,這個時候球要反彈,即要變成向左下方運動。

球跟磚塊以及墻發生碰撞時要注意,球的位置變量BallX和BallY不能越界,即不能超過規定的范圍。所以碰撞的各種情況都要考慮。

三、各模塊代碼

1、8X8點陣屏

h文件

#ifndef __MATRIXLED__
#define __MATRIXLED__extern unsigned char DisplayBuffer[];
void MatrixLED_Clear(void);
void MatrixLED_Init(void);
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset);
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset);
void MatrixLED_Tick(void);
void MatrixLED_DrawPoint(unsigned char X,unsigned char Y);
void MatrixLED_ClearPoint(unsigned char X,unsigned char Y);
bit MatrixLED_GetPoint(unsigned char X,unsigned char Y);#endif

c文件

#include <REGX52.H>/*引腳定義*/sbit _74HC595_DS=P3^4;		//串行數據輸入
sbit _74HC595_STCP=P3^5;	//儲存寄存器時鐘輸入,上升沿有效
sbit _74HC595_SHCP=P3^6;	//移位寄存器時鐘輸入,上升沿有效/*
每一個B對應一個燈。緩存數組DisplayBuffer的8個字節分別對應這8列,高位在下
B0	B0	B0	B0	B0	B0	B0	B0
B1	B1  B1	B1	B1	B1	B1	B1
B2	B2  B2	B2	B2	B2	B2	B2
B3	B3  B3	B3	B3	B3	B3	B3
B4	B4  B4	B4	B4	B4	B4	B4
B5	B5  B5	B5	B5	B5	B5	B5
B6	B6  B6	B6	B6	B6	B6	B6
B7	B7  B7	B7	B7	B7	B7	B7
*///想要改變顯示內容,改變數組DisplayBuffer的數據就行了,由定時器自動掃描
unsigned char DisplayBuffer[8];/*函數定義*//*** 函    數:LED點陣屏清空顯示* 參    數:無* 返 回 值:無* 說    明:直接更改緩存數組的數據就行了,由定時器自動掃描顯示*/
void MatrixLED_Clear(void)
{unsigned char i;for(i=0;i<8;i++){DisplayBuffer[i]=0;}		
}/*** 函    數:MatrixLED初始化(即74HC595初始化)* 參    數:無* 返 回 值:無*/
void MatrixLED_Init(void)
{_74HC595_SHCP=0;	//移位寄存器時鐘信號初始化_74HC595_STCP=0;	//儲存寄存器時鐘信號初始化MatrixLED_Clear();	//點陣屏清屏
}/*** 函    數:74HC595寫入字節* 參    數:Byte 要寫入的字節* 返 回 值:無*/
void _74HC595_WriteByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++)	//循環8次{_74HC595_DS=Byte&(0x01<<i);	//低位先發_74HC595_SHCP=1;	//SHCP上升沿時,DS的數據寫入移位寄存器_74HC595_SHCP=0;}_74HC595_STCP=1;	//STCP上升沿時,數據從移位寄存器轉存到儲存寄存器_74HC595_STCP=0;
}/*** 函    數:8X8LED點陣屏顯示數組內容* 參    數:Array 傳遞過來的數組的首地址(即指針),數組名就是數組的首地址* 返 回 值:Offset 偏移量,向左偏移Offset個像素* 說    明:要求逐列式取模,高位在下*/
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset)
{unsigned char i;Array+=Offset;for(i=0;i<8;i++){DisplayBuffer[i]=*Array;Array++;	//地址自增}
}/*** 函    數:8X8LED點陣屏顯示數組內容* 參    數:Array 傳遞過來的數組的首地址(即指針),數組名就是數組的首地址* 返 回 值:Offset 顯示數組數據的偏移量,向上偏移Offset個像素* 說    明:要求逐列式取模,高位在下*/
void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset)
{unsigned char i,m,n;m=Offset/8;n=Offset%8;Array+=m*8;for(i=0;i<8;i++){DisplayBuffer[i]=(*Array>>n)|(*(Array+8)<<(8-n));Array++;}	
}/*** 函    數:LED點陣屏驅動函數,中斷中調用* 參    數:無* 返 回 值:無*/
void MatrixLED_Tick(void)
{static unsigned char i=0;	//定義靜態變量P0=0xFF;	//消影_74HC595_WriteByte(DisplayBuffer[i]);	//將數據寫入到74HC595中鎖存P0=~(0x80>>i);	//位選,低電平選中i++;	//下次進中斷后掃描下一列i%=8;	//顯示完第八列后,又從第一列開始顯示
}/*** 函    數:MatrixLED在指定位置畫一個點* 參    數:X 指定點的橫坐標,范圍:0~7* 參    數:Y 指定點的縱坐標,范圍:0~7* 返 回 值:無* 說    明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
void MatrixLED_DrawPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){DisplayBuffer[X]|=0x01<<Y;}
}/*** 函    數:MatrixLED在指定位置清除一個點* 參    數:X 指定點的橫坐標,范圍:0~7* 參    數:Y 指定點的縱坐標,范圍:0~7* 返 回 值:無* 說    明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
void MatrixLED_ClearPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){DisplayBuffer[X]&=~(0x01<<Y);}
}/*** 函    數:MatrixLED獲取其中一個LED的狀態* 參    數:X 指定點的橫坐標,范圍:0~7* 參    數:Y 指定點的縱坐標,范圍:0~7* 返 回 值:LED的亮滅狀態,1:亮,0:滅* 說    明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
bit MatrixLED_GetPoint(unsigned char X,unsigned char Y)
{if(X>=0 && X<=7 && Y>=0 && Y<=7){if( DisplayBuffer[X] & (0x01<<Y) ) {return 1;}else {return 0;}}
}

2、獨立按鍵

h文件

#ifndef __KEYSCAN_H__
#define __KEYSCAN_H__extern unsigned char KeyNumber1;
unsigned char Key(void);
void Key_Tick(void);#endif

c文件

#include <REGX52.H>sbit Key1=P3^1;
sbit Key2=P3^0;
sbit Key3=P3^2;
sbit Key4=P3^3;unsigned char KeyNumber;
unsigned char KeyNumber1;/*** 函    數:獲取獨立按鍵鍵碼* 參    數:無* 返 回 值:按下按鍵的鍵碼,范圍:0~12,0表示無按鍵按下* 說    明:在下一次檢測按鍵之前,第二次獲取鍵碼一定會返回0*/
unsigned char Key(void)
{unsigned char KeyTemp=0;KeyTemp=KeyNumber;KeyNumber=0;return KeyTemp;
}/*** 函    數:按鍵驅動函數,在中斷中調用* 參    數:無* 返 回 值:無*/
void Key_Tick(void)
{static unsigned char NowState,LastState;static unsigned int KeyCount;LastState=NowState;	//保存上一次的按鍵狀態NowState=0;	//如果沒有按鍵按下,則NowState為0//獲取當前按鍵狀態if(Key1==0){NowState=1;}if(Key2==0){NowState=2;}if(Key3==0){NowState=3;}if(Key4==0){NowState=4;}//如果上個時間點按鍵未按下,這個時間點按鍵按下,則是按下瞬間if(LastState==0){switch(NowState){case 1:KeyNumber=1;break;case 2:KeyNumber=2;break;case 3:KeyNumber=3;break;case 4:KeyNumber=4;break;default:break;}}//如果上個時間點按鍵按下,這個時間點按鍵按下,則是一直按住按鍵if(LastState && NowState){KeyCount++;if(KeyCount%5==0)	//定時器中斷函數中每隔20ms檢測一次按鍵{	//長按后每隔100ms返回一次長按的鍵碼if     (LastState==1 && NowState==1){KeyNumber=5;}else if(LastState==2 && NowState==2){KeyNumber=6;}else if(LastState==3 && NowState==3){KeyNumber=7;}else if(LastState==4 && NowState==4){KeyNumber=8;}}}else{KeyCount=0;}//如果上個時間點按鍵按下,這個時間點按鍵未按下,則是松手瞬間if(NowState==0){switch(LastState){case 1:KeyNumber=9;break;case 2:KeyNumber=10;break;case 3:KeyNumber=11;break;case 4:KeyNumber=12;break;default:break;}}KeyNumber1=KeyNumber;
}

3、定時器0

h文件

#ifndef __TIMER0_H__
#define __TIMER0_H__void Timer0_Init(void);#endif

c文件

#include <REGX52.H>/*** 函    數:定時器0初始化* 參    數:無* 返 回 值:無*/
void Timer0_Init(void)
{	TMOD&=0xF0;	//設置定時器模式(高四位不變,低四位清零)TMOD|=0x01;	//設置定時器模式(通過低四位設為16位不自動重裝)TL0=0x66;	//設置定時初值,定時1ms,12T@11.0592MHzTH0=0xFC;	//設置定時初值,定時1ms,12T@11.0592MHzTF0=0;	//清除TF0標志TR0=1;	//定時器0開始計時ET0=1;	//打開定時器0中斷允許EA=1;	//打開總中斷PT0=0;	//當PT0=0時,定時器0為低優先級,當PT0=1時,定時器0為高優先級
}/*定時器中斷函數模板
void Timer0_Routine() interrupt 1	//定時器0中斷函數
{static unsigned int T0Count;	//定義靜態變量TL0=0x66;	//設置定時初值,定時1ms,12T@11.0592MHzTH0=0xFC;	//設置定時初值,定時1ms,12T@11.0592MHzT0Count++;if(T0Count>=1000){T0Count=0;}
}
*/

4、定時器1

h文件

#ifndef __TIMER1_H__
#define __TIMER1_H__void Timer1_Init(void);#endif

c文件

#include <REGX52.H>/*** 函    數:定時器1初始化* 參    數:無* 返 回 值:無*/
void Timer1_Init(void)
{TMOD&=0x0F;	//設置定時器模式(低四位不變,高四位清零)TMOD|=0x10;	//設置定時器模式(通過高四位設為16位不自動重裝的模式)TL1=0x66;	//設置定時初值,定時1ms,12T@11.0592MHzTH1=0xFC;	//設置定時初值,定時1ms,12T@11.0592MHzTF1=0;	//清除TF1標志TR1=1;	//定時器1開始計時ET1=1;	//打開定時器1中斷允許EA=1;	//打開總中斷PT1=1;	//當PT1=0時,定時器1為低優先級,當PT1=1時,定時器1為高優先級
}/*定時器中斷函數模板
void Timer1_Routine() interrupt 3	//定時器1中斷函數
{static unsigned int T1Count;	//定義靜態變量TL1=0x66;	//設置定時初值,定時1ms,12T@11.0592MHzTH1=0xFC;	//設置定時初值,定時1ms,12T@11.0592MHzT1Count++;if(T1Count>=1000){T1Count=0;}
}
*/

四、主函數

main.c

/*by甘騰勝@20250327
【效果查看/操作演示】B站搜索“甘騰勝”或“gantengsheng”查看
【單片機】STC89C52RC
【頻率】12T@11.0592MHz
【外設】8X8LED點陣屏、獨立按鍵
【簡單的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【操作說明】
(1)顯示游戲名稱界面和顯示速度的英文的界面按任意按鍵跳過
(2)速度選擇界面通過K1和K2選擇速度
(3)K4為確定鍵,K3為返回鍵
(4)玩家通過獨立按鍵K1和K2控制擋板左右移動
(5)顯示得分的英文的界面可按K4跳過
(6)循環顯示得分界面可按K3返回速度選擇界面
*/#include <REGX52.H>
#include "MatrixLED.h"	//8X8點陣屏
#include "KeyScan.h"	//獨立按鍵
#include "Timer0.h"		//定時器0
#include "Timer1.h"		//定時器1
#include <STDLIB.H>		//隨機函數unsigned char KeyNum;	//存儲獲取的獨立按鍵的鍵碼
unsigned char Mode;	//游戲模式,0:顯示游戲英文名,1:顯示速度的英文,2:速度選擇界面,3:游戲開始前的調整,
//4:游戲進行中,5:游戲結束全屏閃爍,6:顯示得分的英文,7:循環顯示二位數得分
unsigned char AllowChangeModeFlag=1;	//允許改變模式的標志,1:允許改變,0:不允許改變
unsigned char Player=4;	//玩家擋板(下擋板)的中心位置,范圍:1~6
unsigned char BallX=4;	//球的X坐標,左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向
unsigned char BallY=6;	//球的Y坐標,左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向
unsigned char DirectionX=2;	//球左右移動的方向,0:左右方向不移動,1:向左移動,2:向右移動
unsigned char DirectionY=1;	//球上下移動的方向,1:向上移動,2:向下移動
unsigned char MoveFlag;	//球移動的標志,1:移動,0:不移動
unsigned char OnceFlag;	//各模式中切換為其他模式前只執行一次的標志(類似于主函數死循環前的那部分),1:執行,0:不執行
unsigned char Offset1;	//偏移量,用來控制英文字母向左滾動顯示(切換模式后清零)
unsigned char Offset2;	//偏移量,用來控制速度對應的數字上下滾動顯示(切換模式后不清零)
unsigned char UpFlag;	//速度選擇界面,數字向上滾動顯示的標志,1:滾動,0:不滾動
unsigned char DownFlag;	//速度選擇界面,數字向下滾動顯示的標志,1:滾動,0:不滾動
unsigned char RollFlag;	//字母或數字滾動顯示的標志,1:滾動,0:不滾動
unsigned char RollCount;	//上下滾動的計次
unsigned char MoveSpeed=100;	//球移動的速度,值越小,速度越快,單位是10ms(定時器0定時10ms),默認0.5s移動一次
unsigned char GameOverFlag;	//游戲結束的標志,1:游戲結束,0:游戲未結束
unsigned char FlashFlag;	//閃爍的標志,1:不顯示,0:顯示
unsigned char T0Count1,T0Count2,T0Count3,T0Count4;	//定時器計數變量
unsigned char Score;	//游戲得分
unsigned char PauseFlag;	//暫停的標志,1:暫停,0:繼續unsigned char idata ScoreShow[]={	//二位數游戲得分(用于滾動顯示)
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
0x00,0x00,0x00,0x00,0x00,0x00,	// 得分的十位
0x00,0x00,0x00,0x00,0x00,0x00,	// 得分的個位
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
};//取模要求:陰碼(亮點為1),縱向取模,高位在下
unsigned char code Table1[]={	//游戲名稱“彈球游戲”的英文:<<BREAKOUT>>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41,	// <<	寬8高8(自定義書名號:兩個小于號)
0x00,0x7F,0x49,0x49,0x49,0x36,	//	B	寬6高8
0x00,0x7F,0x09,0x19,0x29,0x46,	//	R
0x00,0x7F,0x49,0x49,0x49,0x41,	//	E
0x00,0x7C,0x12,0x11,0x12,0x7C,	//	A
0x00,0x7F,0x08,0x14,0x22,0x41,	//	K
0x00,0x3E,0x41,0x41,0x41,0x3E,	//	O
0x00,0x3F,0x40,0x40,0x40,0x3F,	//	U
0x00,0x01,0x01,0x7F,0x01,0x01,	//	T0x00,0x41,0x22,0x14,0x49,0x22,0x14,0x08,	// >>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
};
unsigned char code Table2[]={	//“速度”的英文:SPEED,寬6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
0x00,0x46,0x49,0x49,0x49,0x31,	// S
0x00,0x7F,0x09,0x09,0x09,0x06,	// P
0x00,0x7F,0x49,0x49,0x49,0x41,	// E
0x00,0x7F,0x49,0x49,0x49,0x41,	// E
0x00,0x7F,0x41,0x41,0x22,0x1C,	// D
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1	如果不按按鍵跳過,則在顯示“1”后自動切換到下一個模式
};
unsigned char code Table3[]={	//速度選擇界面速度對應的數字:“123451”,寬8高8,數值越大,速度越快
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1
0x00,0x00,0x42,0x61,0x51,0x49,0x46,0x00,	// 2
0x00,0x00,0x21,0x41,0x45,0x4B,0x31,0x00,	// 3
0x00,0x00,0x18,0x14,0x12,0x7F,0x10,0x00,	// 4
0x00,0x00,0x27,0x45,0x45,0x45,0x39,0x00,	// 5
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1
};
unsigned char code Table4[]={	//得分的英文:“SCORE”,寬6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//無顯示
0x00,0x46,0x49,0x49,0x49,0x31,	// S
0x00,0x3E,0x41,0x41,0x41,0x22,	// C
0x00,0x3E,0x41,0x41,0x41,0x3E,	// O
0x00,0x7F,0x09,0x19,0x29,0x46,	// R
0x00,0x7F,0x49,0x49,0x49,0x41,	// E
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//無顯示
};
unsigned char code Table5[]={	//游戲得分的字模數據,寬6高8
0x00,0x3E,0x51,0x49,0x45,0x3E,	// 0
0x00,0x00,0x42,0x7F,0x40,0x00,	// 1
0x00,0x42,0x61,0x51,0x49,0x46,	// 2
0x00,0x21,0x41,0x45,0x4B,0x31,	// 3
0x00,0x18,0x14,0x12,0x7F,0x10,	// 4
0x00,0x27,0x45,0x45,0x45,0x39,	// 5
0x00,0x3C,0x4A,0x49,0x49,0x30,	// 6
0x00,0x01,0x71,0x09,0x05,0x03,	// 7
0x00,0x36,0x49,0x49,0x49,0x36,	// 8
0x00,0x06,0x49,0x49,0x29,0x1E,	// 9
};/*** 函    數:主函數(有且僅有一個)* 參    數:無* 返 回 值:無* 說    明:主函數是程序執行的起點,負責執行整個程序的主要邏輯?*/
void main()
{unsigned char i;P2_5=0;	//防止開發板上的蜂鳴器發出聲音Timer0_Init();  //定時器0初始化Timer1_Init();  //定時器1初始化MatrixLED_Init();	//點陣屏初始化while(1){KeyNum=Key();	//獲取獨立按鍵的鍵碼/*如果有按鍵按下*/if(KeyNum){if(Mode==0)	//如果是循環滾動顯示游戲英文名“<<BREAKOUT>>”的界面{if(KeyNum>=9 && KeyNum<=12 && AllowChangeModeFlag)	//如果按下任意按鍵(松手瞬間),且允許改變模式{Mode=1;	//切換到滾動顯示速度的英文“SPEED”的界面OnceFlag=1;	//切換模式前只執行一次的標志置1AllowChangeModeFlag=0;	//允許切換模式的標志置零}}else if(Mode==1)	//如果是滾動顯示速度的英文“SPEED”的界面{if(KeyNum>=9 && KeyNum<=12 && AllowChangeModeFlag)	//如果按下任意按鍵(松手瞬間),且允許改變模式{Mode=2;	//切換到難度選擇界面OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==2)	//如果是速度選擇界面{if(KeyNum==9)	//如果按了“上”鍵K1(松手瞬間){UpFlag=1;	//數字向上滾動的標志置1}if(KeyNum==10)	//如果按了“下”鍵K2(松手瞬間){DownFlag=1;	//數字向下滾動的標志置1}if(KeyNum==12 && AllowChangeModeFlag)	//如果按了確認鍵K4(松手瞬間),且允許改變模式{Mode=3;	//切換到游戲準備界面OnceFlag=1;AllowChangeModeFlag=0;MatrixLED_Clear();}}else if(Mode==3)	//如果是游戲準備界面{if(KeyNum==12 && AllowChangeModeFlag)	//如果按了開始鍵K4(松手瞬間),且允許改變模式{Mode=4;	//切換到游戲模式OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==4)	//如果是正在游戲{if(KeyNum==12)	//如果按了開始鍵K4(松手瞬間),切換暫停和繼續{PauseFlag=!PauseFlag;if(PauseFlag==0){T0Count2=0;MoveFlag=0;}}}else if(Mode==5)	//如果是游戲結束全屏閃爍的界面{if(KeyNum==12 && AllowChangeModeFlag)	//如果按了確認鍵K4(松手瞬間),且允許改變模式{Mode=6;	//切換到顯示英文“SCORE”的界面OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==6)	//如果是顯示英文“SCORE”的界面{if(KeyNum==12 && AllowChangeModeFlag)	//如果按了確認鍵K4(松手瞬間),且允許改變模式{Mode=7;	//切換到循環顯示二位數得分的界面OnceFlag=1;AllowChangeModeFlag=0;}}else if(Mode==7)	//如果是循環顯示二位數得分的界面{if(KeyNum==11 && AllowChangeModeFlag)	//如果按了返回鍵K3(松手瞬間),且允許改變模式{Mode=2;	//返回到速度選擇界面OnceFlag=1;AllowChangeModeFlag=0;}}AllowChangeModeFlag=1;	//允許改變模式的標志置1}/*循環滾動顯示游戲英文名“<<BREAKOUT>>”*/if(Mode==0){if(OnceFlag)	//切換到其他模式前,此if中的代碼只執行1次{OnceFlag=0;	//只執行一次的標志清零Offset1=0;	//偏移量清零}if(RollFlag)	//如果滾動的標志RollFlag為真(非零即真){RollFlag=0;	//滾動的標志RollFlag清零MatrixLED_MoveLeft(Table1,Offset1);	//向左滾動Offset1++;	//每次向左移動一個像素Offset1%=72;	//越界清零,循環滾動顯示}}/*滾動顯示速度的英文“SPEED”*/else if(Mode==1){if(OnceFlag){OnceFlag=0;Offset1=0;}if(RollFlag && Offset1<=46)	//只向左滾動顯示一次,不循環滾動顯示{RollFlag=0;MatrixLED_MoveLeft(Table2,Offset1);Offset1++;}else if(Offset1>46)	//顯示數字“1”之后,自動切換到速度選擇界面{Mode=2;Offset1=0;}}/*速度選擇界面*/else if(Mode==2){if(OnceFlag){OnceFlag=0;MatrixLED_MoveUp(Table3,Offset2);}if(RollFlag && UpFlag)	//如果滾動標志為1,且向上滾動的標志也為1{RollFlag=0;Offset2++;	//向上移動一個像素Offset2%=40;	//越界清零,總共5個數字,每個數字的高度是8,所以是5*8=40MatrixLED_MoveUp(Table3,Offset2);	//更新顯示RollCount++;if(RollCount==8)	//移動了8個像素后停止移動{RollCount=0;UpFlag=0;Offset2=(Offset2/8)*8;	//防止移動到一半的時候按下“上”或“下”按鍵導致數字沒有在點陣屏中間,Offset2的值必須是8的整數倍switch(Offset2/8){case 0:MoveSpeed=100;break;	//速度1,1.00s移動1次case 1:MoveSpeed= 80;break;	//速度2,0.80s移動1次case 2:MoveSpeed= 60;break;	//速度3,0.60s移動1次case 3:MoveSpeed= 40;break;	//速度4,0.40s移動1次case 4:MoveSpeed= 20;break;	//速度5,0.20s移動1次default:break;}}}if(RollFlag && DownFlag)	//如果滾動標志為1,且向下滾動的標志也為1{RollFlag=0;if(Offset2==0){Offset2=40;}Offset2--;MatrixLED_MoveUp(Table3,Offset2);RollCount++;if(RollCount==8){RollCount=0;DownFlag=0;Offset2=(Offset2/8)*8;switch(Offset2/8){case 0:MoveSpeed=100;break;	//速度1,1.00s移動1次case 1:MoveSpeed= 80;break;	//速度2,0.80s移動1次case 2:MoveSpeed= 60;break;	//速度3,0.60s移動1次case 3:MoveSpeed= 40;break;	//速度4,0.40s移動1次case 4:MoveSpeed= 20;break;	//速度5,0.20s移動1次default:break;}}}}/*游戲準備階段*/else if(Mode==3){if(OnceFlag){OnceFlag=0;//顯示前三行“磚塊”for(i=0;i<8;i++){DisplayBuffer[i]|=0x07;}//顯示擋板MatrixLED_DrawPoint(Player,7);MatrixLED_DrawPoint(Player+1,7);MatrixLED_DrawPoint(Player-1,7);//顯示球MatrixLED_ClearPoint(BallX,BallY);MatrixLED_DrawPoint(Player,6);BallX=Player;BallY=6;}}/*游戲進行中*/else if(Mode==4){if(OnceFlag){OnceFlag=0;//游戲初始化PauseFlag=0;GameOverFlag=0;MoveFlag=1;T0Count2=0;Score=0;srand(TL0);	//以定時器0的低八位做種子,從而產生真隨機數DirectionX=rand()%2+1;	//球可能向左上方發射或者向右上方發射DirectionY=1;}if(MoveFlag && PauseFlag==0)	//如果球移動的標志為真,且不是暫停{MoveFlag=0;	//球移動的標志清零if(BallY==6)	//如果球在從上往下數的第7行{if(BallX!=Player && BallX!=Player-1 && BallX!=Player+1)	//且正下方不是擋板{GameOverFlag=1;	//則游戲結束}}if(GameOverFlag==0)	//如果游戲沒結束{MatrixLED_ClearPoint(BallX,BallY);	//清除球的顯示if(BallX==7){DirectionX=1;}	//如果球向右到了第8列,則球(反彈)改成向左移動if(BallX==0){DirectionX=2;}	//如果球向左到了第1列,則球(反彈)改成向右移動if(BallY==6){DirectionY=1;}	//如果球向下到了第7行,則球(反彈)改成向上移動if(BallY==0){DirectionY=2;}	//如果球向上到了第1行,則球(反彈)改成向下移動if(DirectionY==1 && MatrixLED_GetPoint(BallX,BallY-1) && BallY>0){	//如果是向上運動,且正上方是“磚塊”,且不是在第一行DirectionY=2;	//改成向下運動MatrixLED_ClearPoint(BallX,BallY-1);	//消除正上方的“磚塊”Score++;	//分數加一}if(DirectionY==2 && MatrixLED_GetPoint(BallX,BallY+1) && BallY<3){	//如果是向下運動,且正下方是“磚塊”,且在前三行DirectionY=1;	//改成向上運動MatrixLED_ClearPoint(BallX,BallY+1);	//消除正下方的“磚塊”Score++;	//分數加一}if(DirectionX==1 && MatrixLED_GetPoint(BallX-1,BallY) && BallX>0){	//如果是向左運動,且正左方是“磚塊”,且不是在第一列DirectionX=2;	//改成向右運動MatrixLED_ClearPoint(BallX-1,BallY);	//消除正左方的“磚塊”Score++;	//分數加一}if(DirectionX==2 && MatrixLED_GetPoint(BallX+1,BallY) && BallX<7){	//如果是向右運動,且正右方是“磚塊”,且不是在第八列DirectionX=1;	//改成向左運動MatrixLED_ClearPoint(BallX+1,BallY);	//消除正右方的“磚塊”Score++;	//分數加一}if(DirectionX==1 && DirectionY==1 && MatrixLED_GetPoint(BallX-1,BallY-1) && BallX>0 && BallY>0){	//如果是向左上方運動,且左上方是“磚塊”,且不是在第一列和第一行if(BallX<7){DirectionX=2;}	//如果不是在第八列,則改成向右運動DirectionY=2;	//改成向下運動MatrixLED_ClearPoint(BallX-1,BallY-1);	//消除左上方的“磚塊”Score++;	//分數加一}if(DirectionX==2 && DirectionY==1 && MatrixLED_GetPoint(BallX+1,BallY-1) && BallX<7 && BallY>0){	//如果是向右上方運動,且右上方是“磚塊”,且不是在第八列和第一行if(BallX>0){DirectionX=1;}	//如果不是在第一列,則改成向左運動DirectionY=2;	//改成向下運動MatrixLED_ClearPoint(BallX+1,BallY-1);	//消除右上方的“磚塊”Score++;	//分數加一}if(DirectionX==1 && DirectionY==2 && MatrixLED_GetPoint(BallX-1,BallY+1) && BallX>0 && BallY<3){	//如果是向左下方運動,且左下方是“磚塊”,且不是在第一列,且在前三行if(BallX<7){DirectionX=2;}	//如果不是在第八列,則改成向右運動if(BallY>0){DirectionY=1;}	//如果不是在第一行,則改成向上運動MatrixLED_ClearPoint(BallX-1,BallY+1);	//消除左下方的“磚塊”Score++;	//分數加一}if(DirectionX==2 && DirectionY==2 && MatrixLED_GetPoint(BallX+1,BallY+1) && BallX<7 && BallY<3){	//如果是向右下方運動,且右下方是“磚塊”,且不是在第八列,且在前三行if(BallX>0){DirectionX=1;}	//如果不是在第一列,則改成向左運動if(BallY>0){DirectionY=1;}	//如果不是在第一行,則改成向上運動MatrixLED_ClearPoint(BallX+1,BallY+1);	//消除右下方的“磚塊”Score++;	//分數加一}if(Score>=24)	//如果分數達到24分,即全部“磚塊”被打掉{GameOverFlag=1;	//游戲結束Mode=5;	//切換到模式5}else	//如果游戲沒結束{/*更新球的位置*/if(DirectionX==2){BallX+=1;}else if(DirectionX==1){BallX-=1;}if(DirectionY==2){BallY+=1;}else if(DirectionY==1){BallY-=1;}}/*顯示球的下一個位置*/MatrixLED_DrawPoint(BallX,BallY);}else	//如果游戲結束{Mode=5;	//切換到模式5}}}/*游戲結束全屏閃爍*/else if(Mode==5){//在定時器中斷中實現全屏閃爍}/*顯示得分的英文“SCORE”*/else if(Mode==6){if(OnceFlag){OnceFlag=0;Offset1=0;}if(RollFlag && Offset1<=38)	//只滾動顯示一次英文“SCORE”{RollFlag=0;MatrixLED_MoveLeft(Table4,Offset1);Offset1++;}else if(Offset1>38) //滾動結束后,自動切換到循環顯示得分的模式{Mode=7;OnceFlag=1;}	}/*循環顯示得分*/else if(Mode==7){if(OnceFlag){OnceFlag=0;Offset1=0;for(i=0;i<6;i++)	//將得分的十位、個位的字模寫入數組ScoreShow中{ScoreShow[ 8+i]=Table5[(Score/10)*6+i];	//十位}for(i=0;i<6;i++){ScoreShow[14+i]=Table5[(Score%10)*6+i];	//個位}}if(RollFlag){RollFlag=0;MatrixLED_MoveLeft(ScoreShow,Offset1);Offset1++;Offset1%=20;	//循環滾動顯示}}}
}/*** 函    數:定時器0中斷函數* 參    數:無* 返 回 值:無*/
void Timer0_Routine() interrupt 1
{TL0=0x00;	//設置定時初值,定時10ms,12T@11.0592MHzTH0=0xDC;	//設置定時初值,定時10ms,12T@11.0592MHzT0Count1++;T0Count2++;T0Count3++;T0Count4++;if(T0Count1>=2)	//每隔20ms檢測一次鍵碼,且更新擋板的顯示{T0Count1=0;/*在中斷函數中更新擋板的位置*/ //在主循環中更新顯示會有卡頓的現象if(KeyNumber1 && PauseFlag==0 && (Mode==3 || Mode==4))	//如果有獨立按鍵按下且處于游戲準備階段或正在游戲且不是暫停{if(KeyNumber1==1 || KeyNumber1==5)	//如果短按K1或長按K1{Player--;	//下擋板向左移動一格if(Player<1){Player=1;}	//限制范圍MatrixLED_ClearPoint(Player+2,7);	//更新顯示MatrixLED_DrawPoint(Player-1,7);	//更新顯示if(Mode==3){OnceFlag=1;}	//限制范圍}if(KeyNumber1==2 || KeyNumber1==6)	//如果短按K2或長按K2{Player++;	//下擋板向右移動一格if(Player>6){Player=6;}MatrixLED_ClearPoint(Player-2,7);MatrixLED_DrawPoint(Player+1,7);if(Mode==3){OnceFlag=1;}}KeyNumber1=0;	//獨立按鍵的鍵碼清零}}if(T0Count2>=MoveSpeed)	//控制球移動的速度,MoveSpeed越小,球移動的速度越快{T0Count2=0;MoveFlag=1;}if(T0Count3>=10)	//每隔100ms滾動顯示一次字母或數字{T0Count3=0;RollFlag=1;}if(T0Count4>=50)	//每隔500ms置反FlashFlag{T0Count4=0;FlashFlag=!FlashFlag;}
}/*** 函    數:定時器1中斷函數* 參    數:無* 返 回 值:無*/
void Timer1_Routine() interrupt 3
{static unsigned char T1Count;TL1=0x66;	//設置定時初值,定時1ms,12T@11.0592MHzTH1=0xFC;	//設置定時初值,定時1ms,12T@11.0592MHzif(Mode==5 && FlashFlag){P0=0xFF;}	//控制游戲結束后的全屏閃爍else{MatrixLED_Tick();}T1Count++;if(T1Count>=20)	//每隔20ms檢測一次按鍵{T1Count=0;Key_Tick();}
}

總結

此游戲關鍵在于球與磚塊、墻壁碰撞的各種情況都要考慮。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/78061.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78061.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/78061.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++學習:六個月從基礎到就業——C++學習之旅:STL迭代器系統

C學習&#xff1a;六個月從基礎到就業——C學習之旅&#xff1a;STL迭代器系統 本文是我C學習之旅系列的第二十四篇技術文章&#xff0c;也是第二階段"C進階特性"的第二篇&#xff0c;主要介紹C STL迭代器系統。查看完整系列目錄了解更多內容。 引言 在上一篇文章中…

leetcode刷題——判斷對稱二叉樹(C語言版)

題目描述&#xff1a; 示例 1&#xff1a; 輸入&#xff1a;root [6,7,7,8,9,9,8] 輸出&#xff1a;true 解釋&#xff1a;從圖中可看出樹是軸對稱的。 示例 2&#xff1a; 輸入&#xff1a;root [1,2,2,null,3,null,3] 輸出&#xff1a;false 解釋&#xff1a;從圖中可看出最…

無法右鍵下載文檔?網頁PDF下載方法大全

適用場景&#xff1a;繞過付費限制/無法右鍵下載/動態加載PDF 方法1&#xff1a;瀏覽器原生下載&#xff08;成功率60%&#xff09; Chrome/Edge&#xff1a; 在PDF預覽頁點擊工具欄 ??下載圖標&#xff08;右上角&#xff09; 快捷鍵&#xff1a;CtrlS → 保存類型選PDF …

基于缺失數據的2024年山東省專項債發行報告

一、數據情況 本次報告選取了山東省財政局公開的2024年專項債數據,共計2723條,發行期數是從第1期到第58期,由于網絡原因,其中25期到32期,54到57期的數據有缺失,如下圖所示。 從上圖看出,一年52周,平均每周都有一期發布,因此持續做專項債的謀劃很重要,一定要持續謀劃…

Ubuntu數據連接訪問崩潰問題

目錄 一、分析問題 1、崩潰問題本地調試gdb調試&#xff1a; 二、解決問題 1. 停止 MySQL 服務 2. 卸載 MySQL 相關包 3. 刪除 MySQL 數據目錄 4. 清理依賴和緩存 5.重新安裝mysql數據庫 6.創建程序需要的數據庫 三、驗證 1、動態庫更新了 2、頭文件更新了 3、重新…

Linux系統編程 day10 接著線程(中期頭大,還要寫論文)

線程有點懵逼 線程之前函數回顧以及總結部分&#xff08;對不清楚的問題再思考&#xff09; 線程控制原語 進程控制原語 pthread_create(); fork(); pthread_self(); getpid(); pthread_exit(); exit(); pthread_join(); …

《潯川AI翻譯v6.1.0問題已修復公告》

《潯川AI翻譯v6.1.0問題已修復公告》 尊敬的潯川AI翻譯用戶&#xff1a; 感謝您對潯川AI翻譯的支持與反饋&#xff01;我們已針對 **v6.1.0** 版本中用戶反饋的多個問題進行了全面修復&#xff0c;并優化了系統穩定性。以下是本次修復的主要內容&#xff1a; 已修復問題 ?…

深入理解 java synchronized 關鍵字

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;…

華三(H3C)與華為(Huawei)設備配置IPsec VPN的詳細說明,涵蓋配置流程、參數設置及常見問題處理

以下是針對華三&#xff08;H3C&#xff09;與華為&#xff08;Huawei&#xff09;設備配置IPsec VPN的詳細說明&#xff0c;涵蓋配置流程、參數設置及常見問題處理&#xff1a; 一、華三&#xff08;H3C&#xff09;設備IPsec VPN配置詳解 1. 配置流程 華三IPsec VPN配置主要…

KBEngine 源代碼分析(一):pyscript 目錄文件介紹

pyscript 目錄文件 pyscript 目錄提供了 KBEngine 把 C++ 代碼中的類注冊到 Python 的機制 同時也提供了 C++ 調用 Python 方法的例子 相對現在的 C++ 17/20 ,這個目錄的分裝相對不優雅 不過不影響學習如何使用 Python 官方庫提供的 API ,實現 C++ Python 混合編程 C++ …

線程入門3

synchronized修飾方法 synchronized可以修飾代碼塊(在線程入門2中有例子)&#xff0c;也可以修飾普通方法和靜態方法。 修飾普通方法 修飾普通方法簡化寫法&#xff1a; 修飾靜態方法 修飾靜態方法簡化寫法&#xff1a; 注意&#xff1a;利用synchronized上鎖&#xff0c;鎖的…

linux上Flexlm命令

FlexLM 是一種靈活的許可證管理系統&#xff0c;廣泛用于各種軟件產品中&#xff0c;如 Autodesk 的 AutoCAD 和 Autodesk 的其他產品。它允許軟件開發商控制軟件的使用和分發&#xff0c;同時提供靈活的許可證管理策略。在 Linux 系統中使用 FlexLM 通常涉及到幾個關鍵步驟&am…

【Java學習方法】終止循環的關鍵字

終止循環的關鍵字 一、break 作用&#xff1a;跳出最近的循環&#xff08;直接結束離break最近的那層循環&#xff09; 使用場景&#xff1a;一般搭配if條件判斷&#xff0c;如果滿足某個條件&#xff0c;就結束循環&#xff0c;&#xff08;場景&#xff1a;常見于暴力枚舉中…

【論文精讀】Reformer:高效Transformer如何突破長序列處理瓶頸?

目錄 一、引言&#xff1a;當Transformer遇到長序列瓶頸二、核心技術解析&#xff1a;從暴力計算到智能優化1. 局部敏感哈希注意力&#xff08;LSH Attention&#xff09;&#xff1a;用“聚類篩選”替代“全量計算”關鍵步驟&#xff1a;數學優化&#xff1a; 2. 可逆殘差網絡…

關于在Springboot中設置時間格式問題

目錄 1-設置全局時間格式1.Date類型的時間2.JDK8時間3.使Date類和JDK8時間類統統格式化時間 2-關于DateTimeFormat注解 1-設置全局時間格式 1.Date類型的時間 對于老項目來說&#xff0c;springboot中許多類使用的是Date類型的時間&#xff0c;沒有用到LocalDateTime等JDK8時…

面試篇:Java并發與多線程

基礎概念 什么是線程&#xff1f;線程和進程的區別是什么&#xff1f; 線程 是程序執行的最小單位&#xff0c;它是 CPU 調度和執行的基本單元。一個進程可以包含多個線程&#xff0c;這些線程共享進程的資源&#xff08;如內存&#xff09;&#xff0c;但每個線程有自己的棧…

【Qt/C++】QPrinter關于QInternal::Printer的解析

1. 問題分析 QInternal::Printer在Qt框架中并不是一個直接暴露給用戶的API。相反&#xff0c;它是一個枚舉值&#xff0c;用于標識QPaintDevice的類型。在Qt中&#xff0c;QPaintDevice是一個抽象類&#xff0c;用于任何可以進行繪制的設備&#xff0c;如窗口、圖像、打印機等…

uniapp返回上一頁接口數據更新了,頁面未更新

注意&#xff1a;不是組件套組件可以不使用setTimeout延時 返回上一頁一般會走onshow&#xff0c;但是接口更新了頁面未更新 onShow(() > {// 切換城市后重新調用數據if (areaId.value) {const timer setTimeout(async () > {timer && clearTimeout(timer);…

MCU開發學習記錄11 - ADC學習與實踐(HAL庫) - 單通道ADC采集、多通道ADC采集、定時器觸發連續ADC采集 - STM32CubeMX

名詞解釋&#xff1a; ADC&#xff1a; Analog-to-Digital SAR&#xff1a;Successive Approximation Register 本文將介紹ADC的概念、相關函數以及STM32CubeMX生成ADC的配置函數。針對于ADC實踐&#xff1a;單通道采集芯片內部溫度傳感器&#xff08;ADC1_ch16&#xff09;&a…

68元撬動未來:明遠智睿2351開發板重塑嵌入式開發生態

在嵌入式開發領域&#xff0c;價格與性能的矛盾始終存在&#xff1a;高端開發板功能強大但成本高昂&#xff0c;低價產品則往往受限于性能與擴展性。明遠智睿2351開發板以68元&#xff08;含稅&#xff09;的定價打破這一僵局&#xff0c;通過四核1.4G處理器、全功能Linux系統與…