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

目錄

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

系列文章目錄


前言

用的是普中A2開發板。

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

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

一、效果展示

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

二、原理分析

1、8X8點陣屏的驅動

用定時器1專門掃描顯示點陣屏,要注意設置定時器1比定時器0的優先級高,否則顯示可能會有閃爍的現象。

很簡單,跟數碼管的掃描的原理是一樣的。

寫幾個專門的函數,通過與、或、非、移位等操作,能隨意控制任意一個LED亮和滅,以及獲取任意一個LED的亮滅狀態,就OK了,這是最基本的。

2、獨立按鍵的檢測

如果通過延時來消抖,會阻塞程序的運行,所以要用定時器來掃描檢測。本代碼設置每隔20ms檢測一次,時間間隔大點小點都問題不大。

為了讓控制不出現問題,代碼設置了短按或長按按鍵都能控制點的移動。

3、分數和加速的次數的顯示

為了充分利用屏幕,第一列用二進制顯示加速的總次數,高位在上。最后一列用二進制顯示分數除以256的余數,同樣是高位在上。

4、障礙物的創造和顯示

通過一個數組保存產生的障礙物,為了產生真隨機的障礙物,每次獲取非零的鍵碼后,都以定時器0的低八位作為隨機數的種子,通過隨機函數rand()來產生隨機數。

5、游戲結束的判定

移動前檢測玩家控制的點如果移動后是否與障礙物的點重合,如果是的話,則不移動,并且判定游戲結束,全屏閃爍。

6、移動的時間間隔的最小值

分數每增加3,都讓移動的時間間隔變為當前的9/10,但是要控制其最小值,否則可玩性會降低。

三、各模塊代碼

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__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;/*** 函    數:獲取獨立按鍵鍵碼* 參    數:無* 返 回 值:按下按鍵的鍵碼,范圍: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;}}}

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甘騰勝@20250520
【效果查看/操作演示】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:游戲未結束
unsigned char Offset;	//偏移量,用來控制字母或數字向左滾動顯示
bit RollFlag;	//字母或數字滾動一個像素的標志,1:滾動,0:不滾動
bit MoveFlag;	//障礙物向下移動一個像素的標志,1:移動,0:不移動
unsigned int Duration=500;	//移動的時間間隔,單位是1ms,初始500ms移動一次
unsigned char Speed;	//用于屏幕顯示的加速的次數
unsigned int Score;	//游戲得分
bit PauseFlag;	//暫停的標志,1:暫停,0:繼續
unsigned char T0Count;	//定時器0計數全局變量
bit NowPosition;	//玩家現在位置,0:左邊,1:右邊
bit LastPosition;	//玩家上次位置,0:左邊,1:右邊
bit DodgedFlag;	//已經移動了的標志,1:已移動,0:未移動
unsigned char idata Obstacles[32];	//保存生成的障礙物,1:障礙物,0:空氣,索引0~15為左列,索引16~31為右列
unsigned char NowPointer;	//現在顯示的障礙物對應數組Obstacles中的位置,范圍:0~15,0:對應數組索引0和16,1:對應數組索引1和17,以此類推
unsigned char CreatPointer;	//即將要創造的障礙物對應數組Obstacles中的位置,范圍:0~15,0:對應數組索引0和16,1:對應數組索引1和17,以此類推
unsigned char ObstacleLength;	//障礙物的長度
unsigned char GapLength;	//障礙物與障礙物之間間隙的長度
bit LeftOrRight;	//左或右,0:左,1:右
unsigned char OneSideTimes;	//障礙物連續在同一側的次數
bit NowOtherSideState;	//玩家另外一側是否為障礙物,1:是,0:否
bit LastOtherSideState;	//記錄上一次玩家另外一側是否為障礙物,1:是,0:否
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[]={	//游戲名稱“躲閃或者死亡”的英文:<<DODGE OR DIE>>,寬6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// 無顯示
0x00,0x08,0x14,0x22,0x49,0x14,0x22,0x41,	// <<	寬8高8(自定義書名號:兩個小于號)
0x00,0x7F,0x41,0x41,0x22,0x1C,	// D
0x00,0x3E,0x41,0x41,0x41,0x3E,	// O
0x00,0x7F,0x41,0x41,0x22,0x1C,	// D
0x00,0x3E,0x41,0x49,0x49,0x7A,	// G
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==1 || KeyNum==5) && PauseFlag==0 )	//如果是游戲進行中并且不是暫停{	//短按K1或長按K1,玩家移動到左邊NowPosition=0;if(NowPosition!=LastPosition)	//由右邊移到左邊時,躲閃了的標志置1{	//如果已經在左邊了,再按K1,躲閃了的標志不置1,防止另一側玩家正對的點顯示不正常DodgedFlag=1;	//用來更新玩家的位置}LastPosition=NowPosition;	//更新變量的值}if( (KeyNum==2 || KeyNum==6) && PauseFlag==0 )	//如果是游戲進行中并且不是暫停{	//短按K2或長按K2,玩家移動到右邊NowPosition=1;if(NowPosition!=LastPosition)	//由左邊移到右邊時,躲閃了的標志置1{	//如果已經在右邊了,再按K2,躲閃了的標志不置1,防止另一側玩家正對的點顯示不正常DodgedFlag=1;	//用來更新玩家的位置}LastPosition=NowPosition;	//更新變量的值}if(KeyNum==12)	//如果按下K4(松手瞬間){PauseFlag=!PauseFlag;	//置反暫停的標志MoveFlag=0;	//障礙物向下移動的標志置0T0Count=0;	//定時器0的全局計數變量清零}}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;	//得分清零NowPosition=rand()%2;	//確定游戲開始時玩家的隨機位置LastPosition=NowPosition;	//確定游戲開始時玩家的隨機位置PauseFlag=0;	//暫停的標志置0NowPointer=0;	//屏幕正在顯示的內容對應緩存數組中的數據的位置CreatPointer=8;		//即將創造的障礙物對應緩存數組中的數據的位置LeftOrRight=rand()%2;	//用來隨機確定第一個障礙物的左右位置OneSideTimes=0;	//連續在同一側的次數清零ObstacleLength=0;	//障礙物的長度清零GapLength=0;	//障礙物之間的間隙長度清零Duration=500;	//初始每隔500ms移動一次障礙物NowOtherSideState=0;	//玩家另一側障礙物狀態置0LastOtherSideState=0;	//玩家另一側障礙物狀態置0Speed=0;	//用于屏幕顯示的加速的次數清零for(i=0;i<8;i++)	//顯示第3列和第6列,作為邊界{MatrixLED_DrawPoint(2,i);MatrixLED_DrawPoint(5,i);}if(NowPosition==0){MatrixLED_DrawPoint(3,6);}	//顯示玩家位置else{MatrixLED_DrawPoint(4,6);}for(i=0;i<32;i++){Obstacles[i]=0;}	//清空數組的數據MoveFlag=0;	//障礙物移動的標志置0T0Count=0;	//定時器0全局計數變量清零}while(( (CreatPointer>NowPointer) && ((NowPointer+16-CreatPointer)>(ObstacleLength+GapLength)) ) || ( (CreatPointer<NowPointer) && ((NowPointer-CreatPointer)>(ObstacleLength+GapLength)) ) ){	//如果緩存數組中未創造障礙物的區域足夠多while(ObstacleLength)	//障礙物{	//一直創造障礙物,并保存到數組中,直到障礙物長度減為0if(LeftOrRight==0){Obstacles[CreatPointer]=1;Obstacles[CreatPointer+16]=0;}else{Obstacles[CreatPointer]=0;Obstacles[CreatPointer+16]=1;}CreatPointer++;CreatPointer%=16;ObstacleLength--;	//障礙物長度ObstacleLength自減}while(GapLength)	//障礙物之間的間隙{	//一直創造障礙物之間的間隙,并保存到數組中,直到間隙長度減為0Obstacles[CreatPointer]=0;Obstacles[CreatPointer+16]=0;CreatPointer++;CreatPointer%=16;GapLength--;	//障礙物之間的間隙長度GapLength自減}ObstacleLength=rand()%2+1;	//障礙物長度范圍:1~2GapLength=rand()%2+2;	//障礙物之間的間隙長度:2~3if(OneSideTimes==0){OneSideTimes=rand()%3+1;	//障礙物連續在同一側的次數UpOrDownLength的范圍:1~3LeftOrRight=!LeftOrRight;}OneSideTimes--;}if(PauseFlag==0)	//如果不是暫停狀態{if(DodgedFlag)	//如果移動了的標志為1,則更新玩家的位置{DodgedFlag=0;	//躲閃了的標志置0if( (NowPosition==0 && Obstacles[(1+NowPointer)%16]) || (NowPosition==1 && Obstacles[(1+NowPointer)%16+16]) ){	//如果碰撞到了障礙物Mode=2;	//切換到游戲結束全屏閃爍模式GameOverFlag=1;	//游戲結束標志置1}else	//如果沒碰撞到障礙物{if(NowPosition==0)	//如果在左邊{MatrixLED_ClearPoint(4,6);MatrixLED_DrawPoint(3,6);}else	//如果在右邊{MatrixLED_ClearPoint(3,6);MatrixLED_DrawPoint(4,6);}}}if(MoveFlag && GameOverFlag==0)	//如果移動的標志為真,且游戲還未結束{MoveFlag=0;NowPointer++;NowPointer%=16;if( (NowPosition==0 && Obstacles[(1+NowPointer)%16]) || (NowPosition==1 && Obstacles[(1+NowPointer)%16+16]) ){	//如果碰撞到了障礙物Mode=2;	//切換到游戲結束全屏閃爍模式GameOverFlag=1;	//游戲結束標志置1}else{if(NowPosition==0){NowOtherSideState=Obstacles[(1+NowPointer)%16+16];}	//獲取玩家對側的狀態,看是否為障礙物else{NowOtherSideState=Obstacles[(1+NowPointer)%16];}if(LastOtherSideState==1 && NowOtherSideState==0)	//如果玩家的另一側由障礙物變成空隙{Score++;	//則分數加1if(Score%3==0)	//每經過三次障礙物,速度增加一次,變為原來的10/9{Duration=Duration*9/10;	//移動的時間間隔變為原來的9/10if(Duration<90)	//控制Duration的最小值為90{Duration=90;}else{Speed++;	//加速的次數自增}}for(i=0;i<8;i++)	//最后一列以二進制的方式顯示分數(高位在上){if(Score & (0x80>>i)){MatrixLED_DrawPoint(7,i);}else{MatrixLED_ClearPoint(7,i);}}for(i=0;i<8;i++)	//第一列以二進制的方式顯示已加速的次數(高位在上){if(Speed & (0x80>>i)){MatrixLED_DrawPoint(0,i);}else{MatrixLED_ClearPoint(0,i);}}}LastOtherSideState=NowOtherSideState;	//更新變量for(i=0;i<8;i++)	//更新障礙物和玩家的顯示{if(i==1 && NowPosition==0)	//玩家位置{MatrixLED_DrawPoint(3,6);}else	//障礙物和間隙{if(Obstacles[(NowPointer+i)%16]){MatrixLED_DrawPoint(3,7-i);}else{MatrixLED_ClearPoint(3,7-i);}}if(i==1 && NowPosition==1)	//玩家位置{MatrixLED_DrawPoint(4,6);}else	//障礙物和間隙{if(Obstacles[(NowPointer+i)%16+16]){MatrixLED_DrawPoint(4,7-i);}else{MatrixLED_ClearPoint(4,7-i);}}}}}}else	//如果是暫停狀態,則閃爍玩家控制的“點”{if(FlashFlag)	//如果閃爍的標志為真{	//則不顯示玩家控制的點if(NowPosition==0){MatrixLED_ClearPoint(3,6);}else{MatrixLED_ClearPoint(4,6);}}else	//否則顯示{if(NowPosition==0){MatrixLED_DrawPoint(3,6);}else{MatrixLED_DrawPoint(4,6);}}}}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(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();}
}

總結

之前做過一個LCD1602版本的躲閃類游戲,是按照之前的思路將代碼移植過來的。

感覺用點陣屏玩這個游戲效果更好,LCD1602版本的,由于硬件問題,速度較快時會有重影。

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

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

相關文章

區塊鏈可投會議CCF C--APSEC 2025 截止7.13 附錄用率

Conference&#xff1a;32nd Asia-Pacific Software Engineering Conference (APSEC 2025) CCF level&#xff1a;CCF C Categories&#xff1a;軟件工程/系統軟件/程序設計語言 Year&#xff1a;2025 Conference time&#xff1a;December 2-5, 2025 in Macao SAR, China …

pdf圖片導出(Visio\Origin\PPT)

一、Visio 導入pdf格式圖片 1. 設計->大小&#xff0c;適應繪圖。 2. 文件->導出&#xff0c;導出為pdf格式。 上面兩部即可得到只包含圖的部分的pdf格式。 如果出現的有默認白邊&#xff0c;可以通過以下方式設置&#xff1a; 1. 文件->選項->自定義功能區->…

vector的實現

介紹 1. 本質與存儲結構 動態數組實現&#xff1a;vector 本質是動態分配的數組&#xff0c;采用連續內存空間存儲元素&#xff0c;支持下標訪問&#xff08;如 vec[i]&#xff09;&#xff0c;訪問效率與普通數組一致&#xff08;時間復雜度 O (1)&#xff09;。動態擴容機制&…

【Linux筆記】防火墻firewall與相關實驗(iptables、firewall-cmd、firewalld)

一、概念 1、防火墻firewall Linux 防火墻用于控制進出系統的網絡流量&#xff0c;保護系統免受未授權訪問。常見的防火墻工具包括 iptables、nftables、UFW 和 firewalld。 防火墻類型 包過濾防火墻&#xff1a;基于網絡層&#xff08;IP、端口、協議&#xff09;過濾流量&a…

el-date-picker 前端時間范圍選擇器

控制臺參數&#xff1a; 前端代碼&#xff1a;用數組去接受&#xff0c;同時用 value-format"YYYY-MM-DD" 格式化值為&#xff1a;年月日格式 <!-- 查詢區域 --><transition name"fade"><div class"search" v-show"showSe…

在 macOS 上安裝 jenv 管理 JDK 版本

在 macOS 上安裝 jenv 并管理 JDK 版本 在開發 Java 應用程序時&#xff0c;你可能需要在不同的項目中使用不同版本的 JDK。手動切換 JDK 版本可能會很繁瑣&#xff0c;但幸運的是&#xff0c;有一個工具可以簡化這個過程&#xff1a;jenv。jenv 是一個流行的 Java 版本管理工…

2025年全國青少年信息素養大賽復賽C++集訓(16):吃糖果2(題目及解析)

2025年全國青少年信息素養大賽復賽C集訓&#xff08;16&#xff09;&#xff1a;吃糖果2&#xff08;題目及解析&#xff09; 題目描述 現有n(50 > n > 0)個糖果,每天只能吃2個或者3個&#xff0c;請計算共有多少種不同的吃法吃完糖果。 時間限制&#xff1a;1000 內存…

ARM筆記-嵌入式系統基礎

第一章 嵌入式系統基礎 1.1嵌入式系統簡介 1.1.1嵌入式系統定義 嵌入式系統定義&#xff1a; 嵌入式系統是以應用為中心&#xff0c;以計算機技術為基礎&#xff0c;軟硬件可剪裁&#xff0c;對功能、可靠性、成本、體積、功耗等有嚴格要求的專用計算機系統 ------Any devic…

大語言模型(LLM)入門項目推薦

推薦大語言模型(LLM)的入門項目 TiaoYu-1。 https://github.com/tiaoyu1122/TiaoYu-1 項目優點&#xff1a; 幾乎每一行代碼(一些重復的代碼除外)都添加了注釋&#xff0c;詳細介紹了代碼的作用&#xff0c;方便閱讀與理解。基本上覆蓋了常見 LLM 模型的全部訓練流程&#x…

Linux里more 和 less的區別

在 Linux/Unix 系統中&#xff0c;more 和 less 都是用于分頁查看文本文件的命令&#xff0c;但 less 是 more 的增強版&#xff0c;功能更強大。以下是它們的核心區別和用法對比&#xff1a; 1. 基礎功能對比 特性moreless&#xff08;更強大&#xff09;向前翻頁? 僅支持向…

基于PDF流式渲染的Word文檔在線預覽技術

一、背景介紹 在系統開發中&#xff0c;實現在線文檔預覽與編輯功能是許多項目的核心需求&#xff0c;但在實際的開發過程中&#xff0c;我們經常會面臨以下難點&#xff1a; 1&#xff09;格式兼容性問題&#xff1a;瀏覽器原生不支持解析Word二進制格式&#xff0c;直接渲染會…

ai學習--python部分-1.變量名及命名空間的存儲

初學代碼時總有一個問題困擾我&#xff1a;a 10 # a指向地址0x1234&#xff08;存儲10&#xff09; 變量a的值10存儲在0x1234&#xff0c;那么變量a需要存儲嗎&#xff1f;a又存儲在什么地址呢 目錄 1. ??命名空間的本質?? 2. ??命名空間的內存占用?? 3. ??…

Leetcode 3563. Lexicographically Smallest String After Adjacent Removals

Leetcode 3563. Lexicographically Smallest String After Adjacent Removals 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3563. Lexicographically Smallest String After Adjacent Removals 1. 解題思路 這次的最后一題同樣沒有自力搞定&#xff0c;簡直了…… 這道題還…

微信小程序之Promise-Promise初始用

我們來嘗試使用Promise。 1、需求&#xff0c;做個抽獎的按鈕&#xff0c; 抽獎規則&#xff1a; 30%的幾率中獎&#xff0c;中獎會提示恭喜恭喜&#xff0c;獎品為10萬 RMB 勞斯萊斯優惠券&#xff0c;沒中獎會提示再接再厲。 2、先搭界面&#xff1a; <view class&qu…

spring-boot-starter-data-redis應用詳解

一、依賴引入與基礎配置 添加依賴 在 pom.xml 中引入 Spring Data Redis 的 Starter 依賴&#xff0c;默認使用 Lettuce 客戶端&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis<…

全能郵箱全能郵箱:實現郵件管理的自動化!

全能郵箱全能郵箱&#xff1a;實現郵件管理的自動化&#xff01; 全能郵箱全能郵箱的配置教程&#xff1f;如何注冊烽火域名郵箱&#xff1f; 全能郵箱全能郵箱作為一種創新的郵件管理解決方案&#xff0c;正逐漸改變我們處理郵件的方式。蜂郵EDM將圍繞全能郵箱全能郵箱&…

Real2Render2Real:無需動力學仿真或機器人硬件即可擴展機器人數據

25年5月來自UC Berkeley 和 TRI 的論文“Real2Render2Real: Scaling Robot Data Without Dynamics Simulation or Robot Hardware”。 擴展機器人學習需要大量且多樣化的數據集。然而&#xff0c;現行的數據收集范式——人類遙操作——仍然成本高昂&#xff0c;且受到手動操作…

Cadence學習筆記之---PCB的布線與鋪銅

目錄 01 | 引 言 02 | 環境描述 03 | 布 線 04 | 鋪 銅 05 | 總 結 01 | 引 言 在上一篇文章中介紹了Cadence元件放置和布局相關的操作方法和步驟&#xff0c;當完成全部的器件布局后&#xff0c;就可以進行下一步&#xff1b; 本篇文章主要介紹Cadence中布線和鋪銅相關的…

redis-7.4.2 通過 systemd管理,rpmbuild spec文件參考

redis-7 和 redis 5 版本在配置為systemd 方式管理時&#xff0c;配置關于有些許區別&#xff0c;否則會報systemctl status redis 如下錯誤&#xff1a; redis.service: control process exited, codeexited status1 Failed to start Redis persistent key-value database. Un…

2025-05-26 什么是“AI 全棧”

AI全棧&#xff1a;模型 表示學習 向量庫 API UI 一句話定義&#xff1a; ? AI 全棧開發&#xff0c;是指開發者從原始文本/語音/圖像開始&#xff0c;結合大模型能力&#xff0c;構建完整應用閉環的技術能力棧。 AI全棧應用的過程 AI應用 ≠ 一個GPT接口&#xff0c;…