基于51單片機和8X8點陣屏、獨立按鍵的滑動躲閃類小游戲

目錄

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

系列文章目錄


前言

用的是普中A2開發板。

【單片機】STC89C52RC
【頻率】12T@11.0592MHz
【外設】8X8點陣屏、獨立按鍵

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

一、效果展示

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

二、原理分析

1、8X8點陣屏的顯示

普中開發板上的64個LED通過74HC595和P0口來驅動,通過P0口來選擇導通哪一列(低電平導通),通過74HC595串行移位寄存器來確定每一列顯示的數據。

跟8位數碼管(同樣有64個LED)一樣,用掃描的方法來驅動顯示,每隔1ms切換下一列顯示。本代碼中專門用定時器1來掃描點陣屏,并設置定時器1的優先級比定時器0的高。

2、8X8點陣屏的控制

關鍵是下面這三個函數,通過與、或、移位、取反等操作,控制任意一個LED的亮滅,或者獲取任意一個LED的亮滅狀態。

/*** 函    數: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:滅,2:說明超出了屏幕范圍* 說    明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
unsigned char 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;}}else {return 2;}
}

3、玩家控制的點的移動

利用上邊的三個函數中的前兩個,即void MatrixLED_DrawPoint(unsigned char X,unsigned char Y)和void MatrixLED_ClearPoint(unsigned char X,unsigned char Y),移動的時候,先清除原來位置LED的顯示,再點亮新的位置的LED。

4、障礙物的下落

緩存數組的8個字節對應的是8列,對緩存數組的字節進行移位即可實現全屏的LED向下移動一個像素,這樣會讓玩家控制的點熄滅,但是問題不大,移位之后立刻又點亮玩家控制的那個點的LED即可。

5、隨機障礙物的產生

本代碼中產生的障礙物的尺寸都是寬1高2,如何實現呢?通過函數unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y)來獲取LED亮滅狀態,緩存數組的8個字節移位之后,檢測每一列,如果這一列的LED的第二行為亮,第三行為滅,則點亮這一列的第一行的那個LED,否則不點亮。

但是要注意,讓障礙物之間有起碼一個像素的空隙,否則容易堵死,降低可玩性。同樣用到函數unsigned char MatrixLED_GetPoint(unsigned char X,unsigned char Y)。緩存數組的8個字節移位之后,先產生一個0~7的隨機數(對應1~8列),檢測該列、前一列(如果不是第一列的話)、后一列(如果不是最后一列的話)的第二行的LED是否都是熄滅狀態,如果是的話,就在此列產生新的障礙物,否則向左或向右尋找滿足條件的列。

6、獨立按鍵的檢測

獨立按鍵的檢測跟點陣屏的驅動類似,利用定時器每隔一段時間檢測一次,然后跟上次的結果對比,從而判斷是按下瞬間、長按還是松手瞬間。

三、各模塊代碼

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);
unsigned char 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:滅,2:說明超出了屏幕范圍* 說    明:左上角的LED為原點(0,0),向右為X軸正方向,向下為Y軸正方向*/
unsigned char 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;}}else {return 2;}
}

2、獨立按鍵

h文件

#ifndef __KEYSCAN_H__
#define __KEYSCAN_H__extern unsigned char KeyNumber1;	//在main.c里使用
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;	//在外部的main.c里使用/*** 函    數:獲取獨立按鍵鍵碼* 參    數:無* 返 回 值:按下按鍵的鍵碼,范圍: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甘騰勝@20250521
【效果查看/操作演示】B站搜索“甘騰勝”或“gantengsheng”查看
【單片機】STC89C52RC
【頻率】12T@11.0592MHz
【外設】8X8LED點陣屏、獨立按鍵
【簡單的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】點陣屏旁邊的跳線帽要接三個排針的左邊兩個
【操作說明】
(1)循環滾動顯示游戲英文名的界面按任意按鍵開始游戲
(2)K1、K2兩個按鍵控制左右移動
(3)游戲結束全屏閃爍界面按K4進入滾動顯示得分的英文的界面
(4)滾動顯示得分的英文的界面可按K4跳過
(5)循環顯示得分界面可按K3返回,重新開始游戲
(6)游戲時按K4可以暫停或繼續游戲
*/#include <REGX52.H>		//51單片機頭文件
#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:循環滾動顯示得分
bit OnceFlag;	//各模式中切換為其他模式前只執行一次的標志(類似于主函數主循環前的那部分,用于該模式的初始化),1:執行,0:不執行
bit FlashFlag;	//閃爍的標志,1:不顯示,0:顯示
bit GameOverFlag;	//游戲結束的標志,1:游戲結束,0:游戲未結束
bit PauseFlag;	//暫停的標志,1:暫停,0:繼續
bit RollFlag;	//字母或數字滾動一個像素的標志,1:滾動,0:不滾動
bit MoveFlag;	//障礙物向下移動一個像素的標志,1:移動,0:不移動
unsigned char Offset;	//偏移量,用來控制字母或數字向左滾動顯示
unsigned int Score;	//游戲得分
unsigned char T0Count;	//定時器0計數全局變量
unsigned int Duration=500;	//移動的時間間隔,單位是1ms,初始500ms移動一次
unsigned char Player;	//玩家位置,范圍:0~7,對應1~8列
unsigned char RandomColumn;	//產生的隨機的列,范圍:0~7
unsigned char FallCount;	//下落的像素的計數unsigned char idata ScoreShow[]={	//二位數游戲得分(用于滾動顯示),idata:變量保存在片內的簡介尋址區
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,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
};//取模要求:陰碼(亮點為1),縱向取模,高位在下
//我分享的工程文件夾中有6X8像素的ASCII字符字模
//code:數據保存在flash中
unsigned char code Table1[]={	//游戲名稱“滑動或者死亡”的英文:<<SLIDE OR DIE>>,寬6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41,	// <<	寬8高8(自定義書名號:兩個小于號)
0x00,0x46,0x49,0x49,0x49,0x31,	// S
0x00,0x7F,0x40,0x40,0x40,0x40,	// L
0x00,0x00,0x41,0x7F,0x41,0x00,	// I
0x00,0x7F,0x41,0x41,0x22,0x1C,	// D
0x00,0x7F,0x49,0x49,0x49,0x41,	// E
0x00,0x00,0x00,0x00,0x00,0x00,	//  
0x00,0x3E,0x41,0x41,0x41,0x3E,	// O
0x00,0x7F,0x09,0x19,0x29,0x46,	// R
0x00,0x00,0x00,0x00,0x00,0x00,	//  
0x00,0x7F,0x41,0x41,0x22,0x1C,	// D
0x00,0x00,0x41,0x7F,0x41,0x00,	// I
0x00,0x7F,0x49,0x49,0x49,0x41,	// E
0x00,0x41,0x22,0x14,0x49,0x22,0x14,0x08,	// >>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
};
unsigned char code Table2[]={	//“得分”的英文:“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 Table3[]={	//游戲得分的字模數據,寬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){srand(TL0);	//每次獲取非零鍵碼都用定時器0的低8位做種子,從而產生真隨機數if(Mode==0)	//如果是循環滾動顯示游戲英文名的界面{if(KeyNum>=9 && KeyNum<=12)	//如果按下任意按鍵(松手瞬間){Mode=1;	//切換到模式1OnceFlag=1;	//切換模式前只執行一次的標志置1}}else if(Mode==1)	//如果是正在游戲的界面{if(KeyNum==12)	//如果按下K4(松手瞬間){PauseFlag=!PauseFlag;	//置反暫停的標志MoveFlag=0;	//移動的標志置0T0Count=0;	//定時器0全局技術變量清零if(PauseFlag==0){MatrixLED_DrawPoint(Player,7);}	//由暫停變成繼續,則重新顯示玩家位置(防止顯示不計時)}}else if(Mode==2)	//如果是游戲結束全屏閃爍的界面{if(KeyNum==12)	//如果按下K4(松手瞬間){Mode=3;OnceFlag=1;}}else if(Mode==3)	//如果是滾動顯示英文“SCORE”的界面{if(KeyNum==12)	//如果按下K4(松手瞬間){Mode=4;OnceFlag=1;}}else if(Mode==4)	//如果是循環滾動顯示得分的界面{if(KeyNum==11)	//如果按下K3(松手瞬間){Mode=1;	//重新開始游戲OnceFlag=1;}}}/*游戲處理*/if(Mode==0)	//循環滾動顯示游戲英文名{if(OnceFlag)	//切換到其他模式前,此if中的代碼只執行1次{OnceFlag=0;	//只執行一次的標志清零Offset=0;	//滾動顯示的偏移量清零}if(RollFlag)	//如果滾動的標志RollFlag為真(非零即真){RollFlag=0;	//滾動的標志RollFlag清零MatrixLED_MoveLeft(Table1,Offset);	//向左滾動Offset++;	//每次向左移動一個像素Offset%=96;	//越界清零,循環滾動顯示}}else if(Mode==1)	//游戲進行中{if(OnceFlag){OnceFlag=0;//游戲初始化MatrixLED_Clear();	//清屏GameOverFlag=0;	//游戲結束的標志置0Score=0;	//得分清零PauseFlag=0;	//暫停的標志置0Duration=500;	//初始每隔500ms移動一次障礙物Player=rand()%8;	//隨機確定玩家的初始位置MatrixLED_DrawPoint(Player,7);	//顯示玩家位置MoveFlag=0;	//移動的標志置0T0Count=0;	//定時器0全局計數變量清零}if(PauseFlag==0)	//如果不是暫停狀態{if(MoveFlag)	//如果移動的標志為真{MoveFlag=0;	//移動的標志置0if(MatrixLED_GetPoint(Player,6))	//如果玩家正上方的點是障礙物{	//則游戲結束Mode=2;	//切換到模式2GameOverFlag=1;	//游戲結束的標志置1}else	//如果游戲未結束{for(i=0;i<8;i++)	//整個屏幕的顯示向下平移一個像素{	//會導致玩家控制的點熄滅DisplayBuffer[i]<<=1;}MatrixLED_DrawPoint(Player,7);	//重新顯示玩家位置for(i=0;i<8;i++)	//每個障礙物都是寬1高2的條狀物{if( MatrixLED_GetPoint(i,1)==1 && MatrixLED_GetPoint(i,2)==0 ){MatrixLED_DrawPoint(i,0);}}RandomColumn=rand()%8;	//產生0~7的隨機數(用來投放障礙物)if(rand()%2)	//通過隨機數確定向左或向右尋找合適的列投放障礙物{for(i=0;i<8;i++)	//向右尋找{if( MatrixLED_GetPoint((RandomColumn+i)%8,1)==0 && MatrixLED_GetPoint((RandomColumn+i+1)%8,1)==0&& MatrixLED_GetPoint((RandomColumn+i+7)%8,1)==0 )	//保證障礙物之間起碼有一個像素的空隙{MatrixLED_DrawPoint((RandomColumn+i)%8,0);break;	//找到合適的位置則退出循環}}}else	//向左尋找{for(i=8;i>0;i--){if( MatrixLED_GetPoint((RandomColumn+i)%8,1)==0 && MatrixLED_GetPoint((RandomColumn+i+1)%8,1)==0&& MatrixLED_GetPoint((RandomColumn+i+7)%8,1)==0 ){MatrixLED_DrawPoint((RandomColumn+i)%8,0);break;}}}FallCount++;	//記錄下落的像素數if(FallCount%8==0)	//每下落8個像素,分數加1{FallCount=0;	//變量清零Score++;Duration=Duration*9/10;	//每增加一分,速度變為10/9,即時間間隔變為9/10if(Duration<100){Duration=100;}	//控制時間間隔的最小值為100ms}}}}else	//如果是暫停狀態{if(FlashFlag)	//如果閃爍的標志為真{MatrixLED_ClearPoint(Player,7);	//不顯示}else{MatrixLED_DrawPoint(Player,7);	//顯示}}}else if(Mode==2)	//游戲結束全屏閃爍{//在定時器1中實現全屏閃爍}else if(Mode==3)	//滾動顯示得分的英文“SCORE”{if(OnceFlag){OnceFlag=0;Offset=0;}if(RollFlag && Offset<=38)	//只滾動顯示一次英文{RollFlag=0;MatrixLED_MoveLeft(Table2,Offset);Offset++;}else if(Offset>38) //滾動結束后,自動切換到循環滾動顯示得分的模式{Mode=4;OnceFlag=1;}	}else if(Mode==4)	//循環滾動顯示得分{if(OnceFlag){OnceFlag=0;Offset=0;//將得分的十位、個位的字模寫入數組ScoreShow中for(i=0;i<6;i++){ScoreShow[ 8+i]=Table3[(Score/100%10)*6+i];	//百位}for(i=0;i<6;i++){ScoreShow[14+i]=Table3[(Score/10%10)*6+i];	//十位}for(i=0;i<6;i++){ScoreShow[20+i]=Table3[(Score%10)*6+i];	//個位}}if(RollFlag){RollFlag=0;MatrixLED_MoveLeft(ScoreShow,Offset);Offset++;Offset%=26;	//循環滾動顯示}}}
}/*** 函    數:定時器0中斷函數* 參    數:無* 返 回 值:無*/
void Timer0_Routine() interrupt 1
{static unsigned char T0Count1,T0Count2,T0Count3;	//定時器計數變量TL0=0x00;	//設置定時初值,定時10ms,12T@11.0592MHzTH0=0xDC;	//設置定時初值,定時10ms,12T@11.0592MHzT0Count1++;T0Count2++;T0Count3++;T0Count++;if(T0Count1>=2)	//每隔20ms檢測一次鍵碼,且更新玩家位置{T0Count1=0;Key_Tick();/*在中斷函數中更新玩家的位置*/ //在主循環中更新顯示會受代碼影響,導致控制不流暢if(KeyNumber1 && Mode==1)	//如果有按鍵按下且處于游戲進行中{if(KeyNumber1==1 && PauseFlag==0)	//如果短按K1,且不是暫停狀態{if(Player>0)	//如果不是在最左{if(MatrixLED_GetPoint(Player-1,7))	//如果向左移動后,左側為障礙物{	//則游戲結束Mode=2;	//切換到全屏閃爍界面GameOverFlag=1;	//游戲結束的標志置1}else	//如果游戲未結束{MatrixLED_ClearPoint(Player,7);	//清除原來位置的顯示Player--;	//向左移動一個像素MatrixLED_DrawPoint(Player,7);	//顯示移動后的新位置}}}if(KeyNumber1==2 && PauseFlag==0)	//如果短按K2,且不是暫停狀態{if(Player<7)	//如果不是在最右{if(MatrixLED_GetPoint(Player+1,7))	//如果向右移動后,右側為障礙物{	//則游戲結束Mode=2;	//切換到全屏閃爍界面GameOverFlag=1;	//游戲結束的標志置1}else{MatrixLED_ClearPoint(Player,7);	//清除原來位置的顯示Player++;	//向右移動一個像素MatrixLED_DrawPoint(Player,7);	//顯示移動后的新位置}}}KeyNumber1=0;	//獨立按鍵的鍵碼清零}}if(T0Count2>=50)	//每隔500ms置反FlashFlag{T0Count2=0;FlashFlag=!FlashFlag;}if(T0Count3>=10)	//每隔100ms滾動顯示一次字母或數字{T0Count3=0;RollFlag=1;}if(T0Count>=Duration/10)	//每隔Duration ms移動一次障礙物{T0Count=0;MoveFlag=1;}
}/*** 函    數:定時器1中斷函數* 參    數:無* 返 回 值:無* 說    明:專門用定時器1來掃描顯示LED點陣屏,定時器1的優先級要比定時器0的高,否則顯示會有閃爍現象*/
void Timer1_Routine() interrupt 3
{TL1=0x66;	//設置定時初值,定時1ms,12T@11.0592MHzTH1=0xFC;	//設置定時初值,定時1ms,12T@11.0592MHzif(Mode==2 && FlashFlag){P0=0xFF;}	//控制游戲結束后的全屏閃爍else{MatrixLED_Tick();}
}

總結

難在隨機的障礙物的產生,要保證障礙物之間有空隙,防止全部堵死。

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

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

相關文章

Java面向對象 一

系列文章目錄 Java面向對象 二-CSDN博客 Java面向對象 三-CSDN博客 目錄 系列文章目錄 前言 一、初步認識面向對象 1.類和對象的簡單理解 2.類的構成 二、類的實例化 1.對象的創建 2.對象的初始化 三、this引用的作用 四、構造方法 1.構造方法的提供 2.對象的構…

深度學習Y8周:yolov8.yaml文件解讀

&#x1f368; 本文為&#x1f517;365天深度學習訓練營中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 本周任務&#xff1a;根據yolov8n、yolov8s模型的結構輸出&#xff0c;手寫出yolov8l的模型輸出、 文件位置&#xff1a;./ultralytics/cfg/models/v8/yolov8.…

【RocketMQ 生產者和消費者】- 生產者啟動源碼 - MQClientInstance 定時任務(4)

文章目錄 1. 前言2. startScheduledTask 啟動定時任務2.1 fetchNameServerAddr 拉取名稱服務地址2.2 updateTopicRouteInfoFromNameServer 更新 topic 路由信息2.2.1 topic 路由信息2.2.2 updateTopicRouteInfoFromNameServer 獲取 topic2.2.3 updateTopicRouteInfoFromNameSer…

解決Docker容器內yum: not found、apt: not found、apk: command not found等命令找不到問題

Linux有很多發行版&#xff0c;各發行版的包管理工具不一定相同。 Alpine的包管理工具是 apk Debian/Ubuntu的包管理工具是 apt Centos/RHEL的包管理工具是 yum 在安裝軟件之前&#xff0c;需要先查看Docker容器內的Linux是什么發行版&#xff0c;可使用 cat /etc/os-rele…

每日c/c++題 備戰藍橋杯(修理牛棚 Barn Repair)

修理牛棚 Barn Repair 題解 問題背景與挑戰 在一個暴風雨交加的夜晚&#xff0c;Farmer John 的牛棚遭受了嚴重的破壞。屋頂被掀飛&#xff0c;大門也不翼而飛。幸運的是&#xff0c;許多牛正在度假&#xff0c;牛棚并未住滿。然而&#xff0c;為了保護那些還在牛棚里的牛&am…

鴻蒙版Flutter庫torch_light手電筒功能深度適配

鴻蒙版Flutter庫torch_light手電筒功能深度適配&#xff1a;跨平臺開發者的光明之路 本項目作者&#xff1a;kirk/堅果 適配倉庫地址 作者倉庫&#xff1a;https://github.com/svprdga/torch_light# 在數字化浪潮的推動下&#xff0c;跨平臺開發框架如 Flutter 憑借其高效、…

【信息系統項目管理師】一文掌握高項常考題型-項目進度類計算

更多內容請見: 備考信息系統項目管理師-專欄介紹和目錄 文章目錄 一、進度類計算的基本概念1.1 前導圖法1.2 箭線圖法1.3 時標網絡圖1.4 確定依賴關系1.5 提前量與滯后量1.6 關鍵路徑法1.7 總浮動時間1.8 自由浮動時間1.9 關鍵鏈法1.10 資源優化技術1.11 進度壓縮二、基本公式…

深入了解linux系統—— 操作系統的路徑緩沖與鏈接機制

前言 在之前學習當中&#xff0c;我們了解了被打開的文件是如何管理的&#xff1b;磁盤&#xff0c;以及ext2文件系統是如何存儲文件的。 那我們要打開一個文件&#xff0c;首先要先找到這個文件&#xff0c;操作系統又是如何去查找的呢&#xff1f; 理解操作系統搜索文件 …

Docker Hub倉庫介紹

Docker Hub倉庫全解析&#xff1a;從公共市場到私有化部署指南 一、Docker Hub公共鏡像市場 1.1 核心功能解析 全球最大容器鏡像庫&#xff1a;累計托管超500萬鏡像核心服務矩陣&#xff1a; #mermaid-svg-CAMkhmtSWKEUw7z0 {font-family:"trebuchet ms",verdana,a…

redis使用RDB文件恢復數據

設置存盤間隔為120秒且10個key改變數據自動存盤使用RDB文件恢復數據 IP地址主機名192.168.10.170redis170 [rootredis170 ~]# yum install -y redis [rootredis170 ~]# systemctl start redis步驟一&#xff1a;設置存盤間隔為120秒且10個key改變自動存盤 [rootredis170 ~]#…

SpringBoot多環境配置文件切換

resources下application.yml、application-dev.yml、application-prod.yml多個配置文件。 spring:profiles:active: devspring:profiles:active: prod一般都是通過修改spring.profiles.active值來修改加載不同環境的配置信息&#xff0c;可以把切換的dev/prod放到pom.xml文件來…

Java 并發編程高級技巧:CyclicBarrier、CountDownLatch 和 Semaphore 的高級應用

Java 并發編程高級技巧&#xff1a;CyclicBarrier、CountDownLatch 和 Semaphore 的高級應用 一、引言 在 Java 并發編程中&#xff0c;CyclicBarrier、CountDownLatch 和 Semaphore 是三個常用且強大的并發工具類。它們在多線程場景下能夠幫助我們實現復雜的線程協調與資源控…

【Java多線程】多線程狀態下如何安全使用ArrayList以及哈希表

&#x1f50d; 開發者資源導航 &#x1f50d;&#x1f3f7;? 博客主頁&#xff1a; 個人主頁&#x1f4da; 專欄訂閱&#xff1a; JavaEE全棧專欄 多線程安全使用ArrayList 手動加鎖 日常中最常用的方法&#xff0c;使用synchronized進行加鎖&#xff0c;把代碼打包成一份&a…

InnoDB引擎底層解析(二)之InnoDB的Buffer Pool(三)

Buffer Pool 實例 我們上邊說過&#xff0c;Buffer Pool 本質是 InnoDB 向操作系統申請的一塊連續的內存空間&#xff0c;在多線程環境下&#xff0c;訪問 Buffer Pool 中的各種鏈表都需要加鎖處理&#xff0c;在Buffer Pool特別大而且多線程并發訪問特別高的情況下&#xff0…

Netty學習專欄(三):Netty重要組件詳解(Future、ByteBuf、Bootstrap)

文章目錄 前言一、Future & Promise&#xff1a;異步編程的救星1.1 傳統NIO的問題1.2 Netty的解決方案1.3 代碼示例&#xff1a;鏈式異步操作 二、ByteBuf&#xff1a;重新定義數據緩沖區2.1 傳統NIO ByteBuffer的缺陷2.2 Netty ByteBuf的解決方案2.3 代碼示例&#xff1a;…

Vue3逐步拋棄虛擬Dom,React如何抉擇

虛擬DOM&#xff1a;前端界的替死鬼 這玩意兒就是個前端開發的充氣娃娃&#xff01; 你以為它很牛逼&#xff1f;無非是給真DOM當替死鬼&#xff01; 每次數據變&#xff0c;虛擬DOM先擱內存里自嗨一頓&#xff0c;diff算法跟便秘似的算半天&#xff0c;最后才敢碰真DOM。 說白…

分布式鎖總結

文章目錄 分布式鎖什么是分布式鎖&#xff1f;分布式鎖的實現方式基于數據庫(mysql)實現基于緩存(redis)多實例并發訪問問題演示項目代碼(使用redis)配置nginx.confjmeter壓測復現問題并發是1&#xff0c;即不產生并發問題并發30測試,產生并發問題(雖然單實例是synchronized&am…

解決自簽名證書HTTPS告警:強制使用SHA-256算法生成證書

解決自簽名證書HTTPS告警&#xff1a;強制使用SHA-256算法生成證書 一、問題場景 在使用OpenSSL生成和配置自簽名證書時&#xff0c;常遇到以下現象&#xff1a; 瀏覽器已正確導入根證書&#xff08;.pem文件&#xff09;&#xff0c;但訪問HTTPS站點時仍提示不安全連接或證…

線上 Linux 環境 MySQL 磁盤 IO 高負載深度排查與性能優化實戰

目錄 一、線上告警 二、問題診斷 1. 系統層面排查 2. 數據庫層面分析 三、參數調優 1. sync_binlog 參數優化 2. innodb_flush_log_at_trx_commit 參數調整 四、其他優化建議 1. 日志文件位置調整 2. 生產環境核心參數配置模板 3. 突發 IO 高負載應急響應方案 五、…

window 顯示驅動開發-初始化和 DMA 緩沖區創建

若要指示 GPU 支持 GDI 硬件加速&#xff0c;顯示微型端口驅動程序的 DriverEntry 函數實現必須使用指向驅動程序實現的 DxgkDdiRenderKm 函數的指針填充 DRIVER_INITIALIZATION_DATA 結構的 DxgkDdiRenderKm 成員。 DirectX 圖形內核子系統調用 DxgkDdiRenderKm 函數&#xf…