目錄
- 系列文章目錄
- 前言
- 一、效果展示
- 二、原理分析
- 三、各模塊代碼
- 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();}
}
總結
此游戲關鍵在于球與磚塊、墻壁碰撞的各種情況都要考慮。