系列文章目录


前言

《贪吃蛇》,一款经典的、怀旧的小游戏,单片机入门必写程序。

用到普中A2开发板,用矩阵按键控制,单片机芯片为STC89C52RC,晶振@11.0592MHz。8X8LED点阵屏通过板上的74HC595和P0口驱动。

8位独立按键控制的可以看下面这个版本:
基于51单片机和8X8LED点阵屏(MAX7219驱动)的自制独立按键控制的小游戏《贪吃蛇》

效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
工程文件下载:B站对应视频的简介有下载链接。

一、效果展示

在这里插入图片描述

二、原理分析

1、矩阵键盘按键的检测

不能用延时来消抖,那样会阻塞程序运行。

用定时器定时,每隔20ms在中断函数中调用检测函数(以此跳过消抖),检测哪个按键按下,并判断短按长按还是松手,并将对应的键码值保存到一个变量中,再在主函数中获取键码值来控制游戏的操作。游戏中短按和长按方向键都让蛇头改变方向,这样控制才会更加准确。

2、蛇身数据的保存

用一个字节保存蛇身一个点所在的位置,高四位(0~7)表示所在的列,从左到右分别是1~8列,低四位(0~7)表示所在的行,从上到下分别是1~8行。8X8点阵屏共64个点,所以总共需要64个字节变量来保存,可以定义一个一维的数组SnakeBody来保存蛇身的数据。

用一个变量Head来保存蛇头在数组SnakeBody中对应数据的索引(范围:0~63)。

再用一个变量Length保存蛇的长度。

3、蛇的移动及显示

点阵屏有64个点,一个字节可以控制8个点,总共需要8个字节来存储要显示的数据。定义一个显示缓存数组DisplayBuffer来保存需要显示的数据,蛇移动后,或者蛇吃掉食物,产生了新的食物,需要将数据更新到显示缓存数组DisplayBuffer中,再由定时器自动扫描显示就行了。同时,保存蛇身数据的数组SnakeBody中的数据也要更新。

举个例子,代码中蛇初始长度为Length=2,初始位置是第二列第二行(蛇尾),和第三列第二行(蛇头),蛇头Head=1(对应数组中的第2个数据,索引为1)。如果没吃到食物且向右移动一格,则Length不变,Head自增变成2(对应数组的第三个数据),并且数组SnakeBody的第三个数据在MoveSnake函数中被赋值为42,第一个数据(索引为0)不用理会,暂时用不到,再移动62次后会被重新赋值。

如果没吃到食物,蛇的移动其实可以看成蛇尾对应的点移到了原来蛇头的前一个位置并且成为新的蛇头。

如果吃到食物,则整个蛇身不变,原来蛇头的前一个位置成为蛇的一部分,并且成为新的蛇头。

所以更新缓存数组的数据的时候,不用全部更新,只需要操作蛇头和蛇尾对应那部分就行了。每次移动时,蛇头的前一个LED点亮。如果没吃到食物,则蛇尾的那个LED熄灭,如果吃到了食物,则蛇尾的那个LED不熄灭,保持亮的状态。

我们是循环使用数组SnakeBody中的数据,即蛇头变量Head=63(对应数组的最后一个数据,即第64个数据)后再自增,让其变成0(此时蛇头对应数组的第1个数据)。

4、蛇头撞墙和撞到身体的检测

撞墙检测:移动前进行检测,如果高四位为0,说明此时蛇头在第一列,如果移动方向向左,说明撞墙了,将GameOverFlag这个标志置1,游戏结束,在主循环中作相应的处理。

撞到身体检测:如果蛇头的新位置对应显示缓存数组中的那一Bit为1,则说明撞到了身体。游戏结束。

5、真随机食物的创造

每次按下按键都以定时器0的低八位(TL0)做种,通过随机函数rand产生两个随机数来确定点阵屏的一个点,判断此位置是不是蛇身,如果不是,则此处为创造出来的食物的位置,如果是,则在此位置周围寻找,直到找到空位置为止。

6、蛇头方向的控制

按下按键之后,如果按键对应的方向不是蛇头移动的反方向,则改变蛇头方向变量NowDirection的值。这需要再定义一个变量LastDirection,记录上次移动的方向,注意要在蛇移动后再将NowDirection的值赋给LastDirection,这样就可以在按键检测中进行判断了。

7、上下左右滚动显示

滚动显示函数的输入参数为:数组首地址(即数组名)和偏移量Offset,利用移位、与等操作处理数据后写入显示缓存数组。

三、各模块代码

1、定时器0

h文件

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);

#endif

c文件

#include <REGX52.H>

/**
  * @brief	定时器0初始化,1毫秒@11.0592MHz
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
	TMOD&=0xF0;	//设置定时器模式(高四位不变,低四位清零)
	TMOD|=0x01;	//设置定时器模式(通过低四位设为“定时器0工作方式1”的模式)
	TL0=0x66;	//设置定时初值,定时1ms,晶振@11.0592MHz
	TH0=0xFC;	//设置定时初值,定时1ms,晶振@11.0592MHz
	TF0=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,晶振@11.0592MHz
	TH0=0xFC;	//设置定时初值,定时1ms,晶振@11.0592MHz
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

2、矩阵键盘按键

h文件

#ifndef __MATRIXKEYSCAN_H__
#define __MATRIXKEYSCAN_H__

unsigned char Key(void);
void Key_Tick(void);

#endif

c文件

#include <REGX52.H>
#include <INTRINS.H>

#define Matrix_Port P1	//矩阵按键接口

unsigned char KeyNumber;

/**
  * @brief  获取矩阵按键键码,扫描周期内第二次获取键码值,会返回0
  * @param  无
  * @retval 按下按键的键码,范围:0~48,0表示无按键按下
  */
unsigned char Key(void)
{
	unsigned char KeyTemp=0;
	KeyTemp=KeyNumber;
	KeyNumber=0;
	return KeyTemp;
}

/**
  * @brief  检测当前按下按键的状态,无消抖及松手检测
  * @param  无
  * @retval 按下按键的键值,范围:0~16,无按键按下时返回值为0
  */
unsigned char Key_GetState()
{
	unsigned char KeyValue=0;
	
	Matrix_Port=0x0F;	//给所有行赋值0,列全为1
	_nop_();	//适当延时
	if(Matrix_Port!=0x0F)
	{
		Matrix_Port=0x0F;	//测试列
		_nop_();
		switch(Matrix_Port)	//保存行为0时,按键按下后的列值
		{
			case 0x07:KeyValue=1;break;
			case 0x0B:KeyValue=2;break;
			case 0x0D:KeyValue=3;break;
			case 0x0E:KeyValue=4;break;
			default:break;
		}
		Matrix_Port=0xF0;	//测试行
		_nop_();
		switch(Matrix_Port)	//保存列为0时,按键按下后的键值
		{
			case 0x70:KeyValue=KeyValue;break;
			case 0xB0:KeyValue=KeyValue+4;break;
			case 0xD0:KeyValue=KeyValue+8;break;
			case 0xE0:KeyValue=KeyValue+12;break;
			default:break;
		}
	}
	else
	{
		KeyValue=0;
	}

	return KeyValue;
}

/**
  * @brief  矩阵按键驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Key_Tick(void)
{
	static unsigned char NowState,LastState;
	LastState=NowState;	//按键状态更新
	NowState=Key_GetState();	//获取当前按键状态
	//如果上个时间点按键未按下,这个时间点按键按下,则是按下瞬间
	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;
			case 5:KeyNumber=5;break;
			case 6:KeyNumber=6;break;
			case 7:KeyNumber=7;break;
			case 8:KeyNumber=8;break;
			case 9:KeyNumber=9;break;
			case 10:KeyNumber=10;break;
			case 11:KeyNumber=11;break;
			case 12:KeyNumber=12;break;
			case 13:KeyNumber=13;break;
			case 14:KeyNumber=14;break;
			case 15:KeyNumber=15;break;
			case 16:KeyNumber=16;break;
			default:break;
		}
	}
	
	//如果上个时间点按键按下,这个时间点按键还是按下,则是长按
	if(LastState && NowState)
	{
		if(LastState==1 && NowState==1){KeyNumber=17;}
		if(LastState==2 && NowState==2){KeyNumber=18;}
		if(LastState==3 && NowState==3){KeyNumber=19;}
		if(LastState==4 && NowState==4){KeyNumber=20;}
		if(LastState==5 && NowState==5){KeyNumber=21;}
		if(LastState==6 && NowState==6){KeyNumber=22;}
		if(LastState==7 && NowState==7){KeyNumber=23;}
		if(LastState==8 && NowState==8){KeyNumber=24;}
		if(LastState==9 && NowState==9){KeyNumber=25;}
		if(LastState==10 && NowState==10){KeyNumber=26;}
		if(LastState==11 && NowState==11){KeyNumber=27;}
		if(LastState==12 && NowState==12){KeyNumber=28;}
		if(LastState==13 && NowState==13){KeyNumber=29;}
		if(LastState==14 && NowState==14){KeyNumber=30;}
		if(LastState==15 && NowState==15){KeyNumber=31;}
		if(LastState==16 && NowState==16){KeyNumber=32;}
	}

	//如果上个时间点按键按下,这个时间点按键未按下,则是松手瞬间
	if(NowState==0)
	{
		switch(LastState)
		{
			case 1:KeyNumber=33;break;
			case 2:KeyNumber=34;break;
			case 3:KeyNumber=35;break;
			case 4:KeyNumber=36;break;
			case 5:KeyNumber=37;break;
			case 6:KeyNumber=38;break;
			case 7:KeyNumber=39;break;
			case 8:KeyNumber=40;break;
			case 9:KeyNumber=41;break;
			case 10:KeyNumber=42;break;
			case 11:KeyNumber=43;break;
			case 12:KeyNumber=44;break;
			case 13:KeyNumber=45;break;
			case 14:KeyNumber=46;break;
			case 15:KeyNumber=47;break;
			case 16:KeyNumber=48;break;
			default:break;
		}
	}
}

3、8X8LED点阵屏

h文件

#ifndef __MATRIXLED__
#define __MATRIXLED__

extern unsigned char DisplayBuffer[];	//声明数组,外部可以使用
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);

#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];

//函数定义:
/**
  * @brief	MatrixLED初始化(即74HC595初始化)
  * @param	无
  * @retval	无
  */
void MatrixLED_Init(void)
{
	_74HC595_SHCP=0;	//移位寄存器时钟信号初始化
	_74HC595_STCP=0;	//储存寄存器时钟信号初始化
}

/**
  * @brief	74HC595写入字节
  * @param	Byte 要写入的字节
  * @retval	无
  */
void _74HC595_WriteByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)	//循环8次(8位移位寄存器)
	{
		_74HC595_DS=Byte&(0x01<<i);	//低位先发
		_74HC595_SHCP=1;	//SHCP上升沿时,DS的数据写入移位寄存器
		_74HC595_SHCP=0;
	}
	_74HC595_STCP=1;	//STCP上升沿时,数据从移位寄存器转存到储存寄存器
	_74HC595_STCP=0;
}

/**
  * @brief  8X8LED点阵屏向左滚动显示数组内容(要求数组数据逐列式取模,高位在下)
  * @param  Array 传递过来的数组的指针(地址),数组名就是数组的首地址
  * @param  Offset 偏移量,向左偏移Offset个像素
  * @retval 无
  */
void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset)
{
	unsigned char i;
	Array+=Offset;
	for(i=0;i<8;i++)
	{
		DisplayBuffer[i]=*Array;
		Array++;	//地址自增
	}
}

/**
  * @brief  8X8LED点阵屏向上滚动显示数组内容(要求数组数据逐列式取模,高位在下)
  * @param  *Array Array为传递过来的数组的首地址(数组名对应的就是数组的首地址)
  * @param  Offset 显示数组数据的偏移量,向上偏移Offset个像素
  * @retval 无
  */
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++;
	}	
}

/**
  * @brief	LED点阵屏驱动函数,中断中调用
  * @param	无
  * @retval	无
  */
void MatrixLED_Tick(void)
{
	static unsigned char i=0;	//定义静态变量
	P0=0xFF;	//消影
	_74HC595_WriteByte(DisplayBuffer[i]);	//将数据写入到74HC595中锁存
	P0=~(0x80>>i);	//位选,低电平选中
	i++;	//下次进中断后扫描下一列
	i%=8;	//显示完第八列后,又从第一列开始显示
}

四 、主函数

main.c

/*
by甘腾胜@20241202
效果查看/操作演示:可以在B站搜索“甘腾胜”或“gantengsheng”查看
单片机:STC89C52RC
晶振:12T@11.0592MHz
外设:普中开发板板载矩阵键盘按键、8X8LED点阵屏(板载74HC595驱动)
原理分析:https://blog.csdn.net/gantengsheng/article/details/143581157
注意:点阵屏旁边的跳线帽需要接在左边点阵屏才能正常显示
最后修改:20250101

操作说明:

(1)自制独立按键版本

		K7				K2				上:K7
                                        下:K6
	K8		K5		K4		K1          左:K8
                                        右:K5
		K6				K3              开始/暂停/继续:K1
                                        返回:K2

(2)普中开发板矩阵按键版本

	S1		S2		S3		S4			上:S10				
										下:S14		
	S5		S6		S7		S8      	左:S13
										右:S15
	S9		S10		S11		S12     	开始/暂停/继续:S16
										返回:S12
	S13		S14		S15		S16     

缓存数组说明:
每一个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

*/

#include <REGX52.H>	//安装目录下包含寄存器定义的头文件
#include <STDLIB.H>	//安装目录下包含随机函数声明的头文件
#include "Timer0.h"	//定时器模块(工程目录下的头文件)
#include "MatrixKeyScan.h"	//矩阵按键模块
#include "MatrixLED.h"	//点阵屏模块

unsigned char KeyNum;	//存储获得的键码值
unsigned char Mode;	//游戏模式,0:滚动显示游戏英文名“<<SNAKE>>”,1:滚动显示“难度”的英文“DIFFICULTY”,
					//2:难度选择界面(数字的范围是1~5),3:游戏模式,4:游戏结束全屏闪烁,
					//5:滚动显示“得分”的英文“SCORE”,6:循环滚动显示得分
unsigned char MoveFlag;	//移动蛇身的标志,1:移动,0:不移动
unsigned char NowDirection=1;	//蛇头移动的方向,1:向右,2:向上,3:向左,4:向下,游戏开始时默认向右移动(此处可以不赋初值)
unsigned char LastDirection=1;	//蛇头上一次移动的方向,1:向右,2:向上,3:向左,4:向下,游戏开始时默认向右移动(此处可以不赋初值)
unsigned char Length=2;	//蛇的长度,初始值为2(此处可以不赋初值,因为每次游戏开始前会重新赋值一次)
unsigned char Head=1;	//保存整条蛇的位置数据的数组SnakeBody(共64个数据)中,蛇头对应的数据的索引(0~63),蛇的初始长度为2,
						//初始只用了两个数据(数组的第1个数据和第2个数据),蛇头对应的是第2个数据,索引为1,Head的范围:0~63
unsigned char GameOverFlag;	//游戏结束的标志,1:游戏结束,0:游戏未结束
unsigned char FlashFlag;	//闪烁的标志,1:不显示,0:显示
unsigned char Food;	//保存创造出来的食物的位置,高四位(0~7)对应1~8列(从左到右),低四位(0~7)对应1~8行(从上到下)
unsigned char Offset1;	//偏移量,用来控制英文字母和得分向左滚动显示(切换模式后清零)
unsigned char Offset2;	//偏移量,用来控制难度对应的数字上下滚动显示(切换模式后不清零)
unsigned char RollFlag;	//滚动的标志,1:滚动,0:不滚动
unsigned char RollUpFlag;	//难度选择界面,数字向上滚动显示的标志,1:滚动,0:不滚动
unsigned char RollDownFlag;	//难度选择界面,数字向下滚动显示的标志,1:滚动,0:不滚动
unsigned char RollCount;	//上下滚动的计次
unsigned char ExecuteOnceFlag;	//各模式中只执行一次的标志,1:执行,0:不执行
unsigned int MoveSpeed=1000;	//蛇移动的速度,值越小,速度越快,上电默认1s移动一次(定时器定时1ms)
unsigned int T0Count0,T0Count1,T0Count2,T0Count3,T0Count4;	//定时器计数的变量
unsigned char PauseFlag;	//暂停的标志,1:暂停,0:不暂停

//点阵屏共有8*8=64个点,需要64个字节来保存蛇身的数据
//字节的高四位(0~7)对应1~8列(从左到右),低四位(0~7)对应1~8行(从上到下)
//用idata修饰,表示需要用到片内RAM的间接寻址区
unsigned char idata SnakeBody[64];

unsigned char idata ScoreShow[]={	//游戏结束后用来循环滚动显示得分
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x00,0x00,0x00,0x00,0x00,	//游戏结束后将蛇身长度Length的十位的数字字模写入到这一行
0x00,0x00,0x00,0x00,0x00,0x00,	//游戏结束后将蛇身长度Length的个位的数字字模写入到这一行
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
};

//阴码(亮点为1),纵向取模,高位在下(所有取模都必须按这个格式,否则需要修改函数)
//用code修饰,表示保存到ROM中
//宽6高8的ASCII字符字模在我分享的文件中有,需要显示什么字符,复制过来用就行了
unsigned char code Table1[]={	//游戏名称的英文:“<<SNAKE>>”
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 51	宽6高8
0x00,0x7F,0x04,0x08,0x10,0x7F,	// N 46
0x00,0x7C,0x12,0x11,0x12,0x7C,	// A 33
0x00,0x7F,0x08,0x14,0x22,0x41,	// K 43
0x00,0x7F,0x49,0x49,0x49,0x41,	// E 37
0x00,0x41,0x22,0x14,0x49,0x22,0x14,0x08,	// >>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
};
unsigned char code Table2[]={	//难度的英文:“DIFFICULTY”,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x7F,0x41,0x41,0x22,0x1C,	// D 36
0x00,0x00,0x41,0x7F,0x41,0x00,	// I 41
0x00,0x7F,0x09,0x09,0x09,0x01,	// F 38
0x00,0x7F,0x09,0x09,0x09,0x01,	// F 38
0x00,0x00,0x41,0x7F,0x41,0x00,	// I 41
0x00,0x3E,0x41,0x41,0x41,0x22,	// C 35
0x00,0x3F,0x40,0x40,0x40,0x3F,	// U 53
0x00,0x7F,0x40,0x40,0x40,0x40,	// L 44
0x00,0x01,0x01,0x7F,0x01,0x01,	// T 52
0x00,0x07,0x08,0x70,0x08,0x07,	// Y 57
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1 17 如果不按按键跳过,则在显示“1”后自动切换到下一个模式
};
unsigned char code Table3[]={	//难度选择界面难度对应的数字:“123451”,宽8高8
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1 17
0x00,0x00,0x42,0x61,0x51,0x49,0x46,0x00,	// 2 18
0x00,0x00,0x21,0x41,0x45,0x4B,0x31,0x00,	// 3 19
0x00,0x00,0x18,0x14,0x12,0x7F,0x10,0x00,	// 4 20
0x00,0x00,0x27,0x45,0x45,0x45,0x39,0x00,	// 5 21
0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00,	// 1 17
};
unsigned char code Table4[]={	//得分的英文:“SCORE”,宽6高8
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
0x00,0x46,0x49,0x49,0x49,0x31,	// S 51
0x00,0x3E,0x41,0x41,0x41,0x22,	// C 35
0x00,0x3E,0x41,0x41,0x41,0x3E,	// O 47
0x00,0x7F,0x09,0x19,0x29,0x46,	// R 50
0x00,0x7F,0x49,0x49,0x49,0x41,	// E 37
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	//无显示
};
unsigned char code Table5[]={	//“0~9”的字模,宽6高8
0x00,0x3E,0x51,0x49,0x45,0x3E,	// 0 16
0x00,0x00,0x42,0x7F,0x40,0x00,	// 1 17
0x00,0x42,0x61,0x51,0x49,0x46,	// 2 18
0x00,0x21,0x41,0x45,0x4B,0x31,	// 3 19
0x00,0x18,0x14,0x12,0x7F,0x10,	// 4 20
0x00,0x27,0x45,0x45,0x45,0x39,	// 5 21
0x00,0x3C,0x4A,0x49,0x49,0x30,	// 6 22
0x00,0x01,0x71,0x09,0x05,0x03,	// 7 23
0x00,0x36,0x49,0x49,0x49,0x36,	// 8 24
0x00,0x06,0x49,0x49,0x29,0x1E,	// 9 25
};

/**
  * @brief  创造出随机位置的食物
  * @param  无
  * @retval 创造出的食物位置的数据,字节的高四位(0~7)对应1~8列(从左到右),低四位(0~7)对应1~8行(从上到下)
  */
unsigned char CreateFood(void)
{
	unsigned char FoodTemp;
	unsigned char i,j,m,n;
	m=rand()%8;	//产生一个0~7的随机数
	n=rand()%8;	//产生一个0~7的随机数
	for(i=0;i<8;i++)	//产生一个随机位置,判断该位置是否是蛇身,如果不是,就返回该位置所对应的数据
	{	//如果是蛇身的位置,则从该点向周围寻找不是蛇身的空位置
		for(j=0;j<8;j++)
		{
			if( ( DisplayBuffer[(m+i)%8] & (0x01<<(n+j)%8) ) ==0 )
			{
				FoodTemp=(m+i)%8*16+(n+j)%8;
				break;	//找到了空位置就退出循环
			}
		}
	}
	return FoodTemp;
}

/**
  * @brief  改变缓存数组DisplayBuffer的数据
  * @param  Position 要设置的位置,高四位(0~7)对应1~8列(从左到右),低四位(0~7)对应1~8行(从上到下)
  * @param  State 要修改成的状态,范围:0~1,0:对应的那个Bit置0,1:对应的那个Bit置1
  * @retval 无
  */
void UpdateDisplayBuffer(unsigned char Position,unsigned char State)
{
	if(State)
	{
		DisplayBuffer[Position/16] |= (0x01<<(Position%16));
	}
	else
	{
		DisplayBuffer[Position/16] &= ~(0x01<<(Position%16));
	}
}

/**
  * @brief  控制蛇的移动
  * @param  无
  * @retval 无
  */
void MoveSnake(void)
{
	if(NowDirection==1)	//如果向右移动
	{
		//移动前判断一下移动后是否撞墙,如果是,则游戏结束,游戏结束的标志置1
		if(SnakeBody[Head]/16==7){GameOverFlag=1;}
		
		//(Head+1)%64,取余的目的是为了防止越界,SnakeBody数组的索引范围是:0~63,索引为63后再加1,就越界了
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)加16(高四位加1),即蛇头移动到了右边这一列
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]+16;}
	}
	if(NowDirection==2)	//如果向上移动
	{
		if(SnakeBody[Head]%16==0){GameOverFlag=1;}
		
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)减1(低四位减1),即蛇头移动到了上边这一行
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]-1;}
	}
	if(NowDirection==3)	//如果向左移动
	{
		if(SnakeBody[Head]/16==0){GameOverFlag=1;}
		
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)减16(高四位减1),即蛇头移动到了左边这一列
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]-16;}
	}
	if(NowDirection==4)	//如果向下移动
	{
		if(SnakeBody[Head]%16==7){GameOverFlag=1;}
		
		//SnakeBody数组中索引为(Head+1)%64的数据(准蛇头)等于索引为Head的数据(现蛇头)加1(低四位减1),即蛇头移动到了下边这一行
		else{SnakeBody[(Head+1)%64]=SnakeBody[Head]+1;}
	}
	
	Head++;	//SnakeBody数组中,蛇头对应的数据的索引加1
	Head%=64;	//蛇头变量Head的范围是0~63
	
	if(GameOverFlag==0)	//如果没撞墙
	{
		if(SnakeBody[Head]==Food)	//判断蛇头移动后的位置是否是食物所在的位置
		{	//如果是
			Length++;	//蛇身长度加1
			UpdateDisplayBuffer(Food,1);	//更新缓存数组DisplayBuffer的数据
			if(Length<64)	//如果蛇身长度没有达到最大值64
			{
				Food=CreateFood();	//重新创造一个食物
			}
			else	//如果蛇身长度达到了最大值64
			{
				GameOverFlag=1;	//游戏结束
			}
			FlashFlag=0;	//创造出新的食物时,食物暂不闪烁
			T0Count3=0;	//定时器计数变量T0Count3重新计数
		}
		else if( DisplayBuffer[SnakeBody[Head]/16] & (0x01<<(SnakeBody[Head]%16)) )
		{	//如果蛇头移动后的位置不是食物,且撞在蛇身上,则游戏结束
			GameOverFlag=1;	//游戏结束的标志置1
		}
		else	//如果蛇头移动后的位置不是食物,也不是撞墙,也不是撞到蛇身的话
		{
			//此时相当于原来的蛇尾跑到了原来的蛇头的前一格,变成新的蛇头,原来的蛇尾消失,原来的蛇头变成蛇身
			//只需更改蛇头蛇尾的显示就行了,中间部分不用处理
			UpdateDisplayBuffer(SnakeBody[Head],1);	//原来的蛇头的前一格变成新的蛇头
			UpdateDisplayBuffer(SnakeBody[(Head+64-Length)%64],0);	//原来的蛇尾消失
		}
	}

}

void main()
{
	unsigned char i;
	Timer0_Init();	//定时器初始化
	P2_5=0;	//防止开发板的蜂鸣器导通
	while(1)
	{
		KeyNum=Key();	//获取键码值
		
		if(KeyNum)	//如果有按键按下
		{
			srand(TL0);	//以定时器0的低八位数据作为随机数的种子,用来产生真随机的数据

			if(Mode==6)	//如果是循环滚动显示得分的界面
			{
				if(KeyNum==44)	//如果按下K12(松手瞬间)
				{
					Mode=2;	//返回难度选择界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==5)	//如果是滚动显示“得分”的英文“SCORE”的界面
			{
				if(KeyNum!=12 && KeyNum!=28 && KeyNum!=44)
				{	//如果按下K12以外的按键(按下瞬间、长按、松手瞬间)
					Mode=6;	//跳过英文显示,切换到循环滚动显示得分的界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==4)	//如果是游戏结束全屏闪烁界面
			{
				if(KeyNum==48)	//如果按下K16(松手瞬间)
				{
					Mode=5;	//切换到滚动显示“得分”的英文“SCORE”的界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==3)	//如果是游戏模式
			{
				if(KeyNum==48)	//按下K16暂停或继续(松手瞬间)
				{
					PauseFlag=!PauseFlag;
					if(PauseFlag==0){T0Count3=0;}
				}
				if(PauseFlag==0)	//如果不是暂停
				{	//按下瞬间、长按、松手瞬间都进行检测,这样控制方向更有效,防止按键没检测出来导致没能改变方向
					if((KeyNum==13 || KeyNum==29 || KeyNum==45) && LastDirection!=1)
					{	//如果按了“左”键,且蛇头原来的移动方向不是向右
						NowDirection=3;	//则方向蛇头方向改为向左
					}
					if((KeyNum==10 || KeyNum==26 || KeyNum==42) && LastDirection!=4)
					{	//如果按了“上”键,且蛇头原来的移动方向不是向下
						NowDirection=2;	//则方向蛇头方向改为向上
					}
					if((KeyNum==14 || KeyNum==30 || KeyNum==46) && LastDirection!=2)
					{	//如果按了“下”键,且蛇头原来的移动方向不是向上
						NowDirection=4;	//则方向蛇头方向改为向左
					}
					if((KeyNum==15 || KeyNum==31 || KeyNum==47) && LastDirection!=3)
					{	//如果按了“右”键,且蛇头原来的移动方向不是向左
						NowDirection=1;	//则方向蛇头方向改为向左
					}
				}
			}

			if(Mode==2)	//如果是难度选择界面
			{
				if(KeyNum==42)	//如果按了“上”键K10(松手瞬间)
				{
					RollUpFlag=1;	//数字向上滚动的标志置1
				}
				if(KeyNum==46)	//如果按了“下”键K14(松手瞬间)
				{
					RollDownFlag=1;	//数字向下滚动的标志置1
				}
				if(KeyNum==48)	//如果按了开始键K16(松手瞬间)
				{
					Mode=3;	//切换到游戏模式
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==1)	//如果是滚动显示难度的英文“DIFFICULTY”的界面
			{
				if(KeyNum>=33 && KeyNum<=48)	//如果按下任意按键(松手瞬间)
				{
					Mode=2;	//切换到难度选择界面
					ExecuteOnceFlag=1;
				}
			}

			if(Mode==0)	//如果是循环滚动显示游戏英文名“<<SNAKE>>”的界面
			{
				if(KeyNum>=33 && KeyNum<=48)	//如果按下任意按键(松手瞬间)
				{
					Mode=1;	//切换到滚动显示难度的英文“DIFFICULTY”的界面
					ExecuteOnceFlag=1;
				}
			}
		}
		
		if(Mode==0)	//如果是循环滚动显示游戏英文名“<<SNAKE>>”的界面
		{
			if(ExecuteOnceFlag)	//切换到其他模式前,此if中的代码只执行1次
			{
				ExecuteOnceFlag=0;
				Offset1=0;
			}
			if(RollFlag)	//如果滚动的标志RollFlag为真(非零即真)
			{
				RollFlag=0;	//滚动的标志RollFlag清零
				MatrixLED_MoveLeft(Table1,Offset1);	//向左滚动
				Offset1++;	//每次向左移动一个像素
				Offset1%=54;	//越界清零,循环滚动显示
			}
		}

		if(Mode==1)	//如果是滚动显示难度的英文“DIFFICULTY”的界面
		{
			if(ExecuteOnceFlag)	//切换到其他模式前,此if中的代码只执行1次
			{
				ExecuteOnceFlag=0;
				Offset1=0;
			}
			if(RollFlag && Offset1<=76)	//只向左滚动显示一次,不循环滚动显示
			{
				RollFlag=0;
				MatrixLED_MoveLeft(Table2,Offset1);
				Offset1++;
			}
			else if(Offset1>76)	//显示数字“1”之后,自动切换到难度选择界面
			{
				Mode=2;
				Offset1=0;
			}
		}

		if(Mode==2)	//如果是难度选择界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				MatrixLED_MoveUp(Table3,Offset2);
			}
			if(RollFlag && RollUpFlag)	//如果滚动标志为1,且向上滚动的标志也为1
			{
				RollFlag=0;	//定时器中每个50ms将RollFlag1标志置1
				Offset2++;	//向上移动一个像素
				Offset2%=40;	//越界清零,总共5个数字,每个数字的高度是8,所以是5*8=40
				MatrixLED_MoveUp(Table3,Offset2);	//更新显示
				RollCount++;
				if(RollCount==8)	//移动了8个像素后停止移动
				{
					RollCount=0;
					RollUpFlag=0;
					Offset2=(Offset2/8)*8;	//防止移动到一半的时候按下“上”或“下”按键导致数字没有在点阵屏中间
											//Offset2的值必须是8的整数倍
					switch(Offset2/8)
					{
						case 0:MoveSpeed=1000;break;	//难度1,1.00s移动1次
						case 1:MoveSpeed=750;break;	//难度2,0.75s移动1次
						case 2:MoveSpeed=500;break;	//难度3,0.50s移动1次
						case 3:MoveSpeed=250;break;	//难度4,0.25s移动1次
						case 4:MoveSpeed=120;break;	//难度5,0.12s移动1次
						default:break;
					}
				}
			}
			if(RollFlag && RollDownFlag)	//如果滚动标志为1,且向下滚动的标志也为1
			{
				RollFlag=0;
				if(Offset2==0){Offset2=40;}
				Offset2--;
				MatrixLED_MoveUp(Table3,Offset2);
				RollCount++;
				if(RollCount==8)
				{
					RollCount=0;
					RollDownFlag=0;
					Offset2=(Offset2/8)*8;
					switch(Offset2/8)
					{
						case 0:MoveSpeed=1000;break;
						case 1:MoveSpeed=750;break;
						case 2:MoveSpeed=500;break;
						case 3:MoveSpeed=250;break;
						case 4:MoveSpeed=120;break;
						default:break;
					}
				}
			}
		}
		
		if(Mode==3)	//如果是游戏模式
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				GameOverFlag=0;	//游戏结束标志清零
				PauseFlag=0;	//游戏暂停标志清零
				NowDirection=1;	//蛇头默认向右移动
				LastDirection=1;	//上一次蛇头默认向右移动
				Length=2;	//蛇的初始长度为2
				Head=1;	//蛇头对应数组中的第2个数据(索引为1)
				for(i=0;i<8;i++){DisplayBuffer[i]=0;}	//清空显示缓存数据
				SnakeBody[0]=1*16+1;	//数组SnakeBody中写入蛇身初始的两个数据,开始时蛇尾在2行2列
				SnakeBody[1]=2*16+1;	//数组SnakeBody中写入蛇身初始的两个数据,开始时蛇头在2行3列
				UpdateDisplayBuffer(SnakeBody[0],1);
				UpdateDisplayBuffer(SnakeBody[1],1);
				Food=CreateFood();	//进入游戏前,先创造出一个食物
				UpdateDisplayBuffer(Food,1);
				MoveFlag=0;	//蛇移动的标志清零
				T0Count2=0;	//定时器计数变量T0Count1清零,重新计数
			}
			
			if(GameOverFlag==0)	//如果游戏没结束
			{
				if(MoveFlag && PauseFlag==0)	//如果蛇移动的标志为1,且没暂停
				{
					MoveFlag=0;	//移动标志清零
					MoveSnake();	//移动一次
					LastDirection=NowDirection;	//保存上一次移动的方向,用于按键的判断(控制蛇不能往后移动)
				}
				if(PauseFlag)	//如果暂停了
				{
					UpdateDisplayBuffer(Food,1);	//食物不闪烁,一直显示
				}			
				else if(FlashFlag)	//如果不暂停,且闪烁标志为1
				{
					UpdateDisplayBuffer(Food,0);	//不显示食物
				}
				else	//如果不暂停,且闪烁标志为0
				{
					UpdateDisplayBuffer(Food,1);	//显示食物
				}
			}
			else	//如果游戏结束
			{
				UpdateDisplayBuffer(Food,1);	//显示食物
				Mode=4;	//切换到全屏闪烁模式
				ExecuteOnceFlag=1;
			}
		}
		
		if(Mode==4)	//如果是游戏结束全屏闪烁界面
		{
			//在定时器中断中实现全屏闪烁
		}
		
		if(Mode==5)	//如果是滚动显示“得分”的英文“SCORE”的界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				Offset1=0;
			}
			if(RollFlag && Offset1<=38)	//只滚动显示一次英文“SCORE”
			{
				RollFlag=0;
				MatrixLED_MoveLeft(Table4,Offset1);
				Offset1++;
			}
			else if(Offset1>38) //滚动结束后,自动切换到循环显示得分的模式
			{
				Mode=6;
				ExecuteOnceFlag=1;
			}	
		}
		
		if(Mode==6)	//如果是循环滚动显示得分的界面
		{
			if(ExecuteOnceFlag)
			{
				ExecuteOnceFlag=0;
				Offset1=0;
				for(i=0;i<6;i++)	//将得分(即蛇身的长度)的十位和个位的字模写入数组ScoreShow中
				{
					ScoreShow[8+i]=Table5[(Length/10)*6+i];
				}
				for(i=0;i<6;i++)
				{
					ScoreShow[14+i]=Table5[(Length%10)*6+i];
				}
			}
			if(RollFlag)
			{
				RollFlag=0;
				MatrixLED_MoveLeft(ScoreShow,Offset1);
				Offset1++;
				Offset1%=20;	//循环滚动
			}
		}
	}
}

void Timer0_Routine() interrupt 1	//定时器0中断函数
{
	TL0=0x66;	//设置定时初值,定时1ms,晶振@11.0592MHz
	TH0=0xFC;	//设置定时初值,定时1ms,晶振@11.0592MHz
	T0Count0++;
	T0Count1++;
	if(PauseFlag==0){T0Count2++;}	//不暂停时,T0Count2才计数
	else{T0Count2=0;}	//暂停后继续游戏时,T0Count2重新计数
	T0Count3++;
	T0Count4++;
	if(T0Count0>=1)	//每隔1ms显示LED点阵屏的下一列
	{
		T0Count0=0;
		if(Mode==4 && FlashFlag){P0=0xFF;}
		else{MatrixLED_Tick();}
	}
	if(T0Count1>=20)	//每隔20ms检测一次按键
	{
		T0Count1=0;
		Key_Tick();
	}
	if(T0Count2>=MoveSpeed)	//用来控制蛇移动的速度,MoveSpeed的值越小,蛇移动的速度越快
	{
		T0Count2=0;
		MoveFlag=1;
	}
	if(T0Count3>=250)	//每隔0.25s改变FlashFlag的真假
	{
		T0Count3=0;
		FlashFlag=!FlashFlag;
	}
	if(T0Count4>=100)	//控制滚动显示的速度
	{
		T0Count4=0;
		RollFlag=1;	//100ms
	}
}

附录A:ASCII字符字模(6*8像素)

(来源:江科大的共享代码)

	/*宽6像素,高8像素*/
	//纵向取模,高位在下
	0x00,0x00,0x00,0x00,0x00,0x00,//   0
	0x00,0x00,0x00,0x2F,0x00,0x00,// ! 1
	0x00,0x00,0x07,0x00,0x07,0x00,// " 2
	0x00,0x14,0x7F,0x14,0x7F,0x14,// # 3
	0x00,0x24,0x2A,0x7F,0x2A,0x12,// $ 4
	0x00,0x23,0x13,0x08,0x64,0x62,// % 5
	0x00,0x36,0x49,0x55,0x22,0x50,// & 6
	0x00,0x00,0x00,0x07,0x00,0x00,// ' 7
	0x00,0x00,0x1C,0x22,0x41,0x00,// ( 8
	0x00,0x00,0x41,0x22,0x1C,0x00,// ) 9
	0x00,0x14,0x08,0x3E,0x08,0x14,// * 10
	0x00,0x08,0x08,0x3E,0x08,0x08,// + 11
	0x00,0x00,0x00,0xA0,0x60,0x00,// , 12
	0x00,0x08,0x08,0x08,0x08,0x08,// - 13
	0x00,0x00,0x60,0x60,0x00,0x00,// . 14
	0x00,0x20,0x10,0x08,0x04,0x02,// / 15
	0x00,0x3E,0x51,0x49,0x45,0x3E,// 0 16
	0x00,0x00,0x42,0x7F,0x40,0x00,// 1 17
	0x00,0x42,0x61,0x51,0x49,0x46,// 2 18
	0x00,0x21,0x41,0x45,0x4B,0x31,// 3 19
	0x00,0x18,0x14,0x12,0x7F,0x10,// 4 20
	0x00,0x27,0x45,0x45,0x45,0x39,// 5 21
	0x00,0x3C,0x4A,0x49,0x49,0x30,// 6 22
	0x00,0x01,0x71,0x09,0x05,0x03,// 7 23
	0x00,0x36,0x49,0x49,0x49,0x36,// 8 24
	0x00,0x06,0x49,0x49,0x29,0x1E,// 9 25
	0x00,0x00,0x36,0x36,0x00,0x00,// : 26
	0x00,0x00,0x56,0x36,0x00,0x00,// ; 27
	0x00,0x08,0x14,0x22,0x41,0x00,// < 28
	0x00,0x14,0x14,0x14,0x14,0x14,// = 29
	0x00,0x00,0x41,0x22,0x14,0x08,// > 30
	0x00,0x02,0x01,0x51,0x09,0x06,// ? 31
	0x00,0x3E,0x49,0x55,0x59,0x2E,// @ 32
	0x00,0x7C,0x12,0x11,0x12,0x7C,// A 33
	0x00,0x7F,0x49,0x49,0x49,0x36,// B 34
	0x00,0x3E,0x41,0x41,0x41,0x22,// C 35
	0x00,0x7F,0x41,0x41,0x22,0x1C,// D 36
	0x00,0x7F,0x49,0x49,0x49,0x41,// E 37
	0x00,0x7F,0x09,0x09,0x09,0x01,// F 38
	0x00,0x3E,0x41,0x49,0x49,0x7A,// G 39
	0x00,0x7F,0x08,0x08,0x08,0x7F,// H 40
	0x00,0x00,0x41,0x7F,0x41,0x00,// I 41
	0x00,0x20,0x40,0x41,0x3F,0x01,// J 42
	0x00,0x7F,0x08,0x14,0x22,0x41,// K 43
	0x00,0x7F,0x40,0x40,0x40,0x40,// L 44
	0x00,0x7F,0x02,0x0C,0x02,0x7F,// M 45
	0x00,0x7F,0x04,0x08,0x10,0x7F,// N 46
	0x00,0x3E,0x41,0x41,0x41,0x3E,// O 47
	0x00,0x7F,0x09,0x09,0x09,0x06,// P 48
	0x00,0x3E,0x41,0x51,0x21,0x5E,// Q 49
	0x00,0x7F,0x09,0x19,0x29,0x46,// R 50
	0x00,0x46,0x49,0x49,0x49,0x31,// S 51
	0x00,0x01,0x01,0x7F,0x01,0x01,// T 52
	0x00,0x3F,0x40,0x40,0x40,0x3F,// U 53
	0x00,0x1F,0x20,0x40,0x20,0x1F,// V 54
	0x00,0x3F,0x40,0x38,0x40,0x3F,// W 55
	0x00,0x63,0x14,0x08,0x14,0x63,// X 56
	0x00,0x07,0x08,0x70,0x08,0x07,// Y 57
	0x00,0x61,0x51,0x49,0x45,0x43,// Z 58
	0x00,0x00,0x7F,0x41,0x41,0x00,// [ 59
	0x00,0x02,0x04,0x08,0x10,0x20,// \ 60
	0x00,0x00,0x41,0x41,0x7F,0x00,// ] 61
	0x00,0x04,0x02,0x01,0x02,0x04,// ^ 62
	0x00,0x40,0x40,0x40,0x40,0x40,// _ 63
	0x00,0x00,0x01,0x02,0x04,0x00,// ` 64
	0x00,0x20,0x54,0x54,0x54,0x78,// a 65
	0x00,0x7F,0x48,0x44,0x44,0x38,// b 66
	0x00,0x38,0x44,0x44,0x44,0x20,// c 67
	0x00,0x38,0x44,0x44,0x48,0x7F,// d 68
	0x00,0x38,0x54,0x54,0x54,0x18,// e 69
	0x00,0x08,0x7E,0x09,0x01,0x02,// f 70
	0x00,0x18,0xA4,0xA4,0xA4,0x7C,// g 71
	0x00,0x7F,0x08,0x04,0x04,0x78,// h 72
	0x00,0x00,0x44,0x7D,0x40,0x00,// i 73
	0x00,0x40,0x80,0x84,0x7D,0x00,// j 74
	0x00,0x7F,0x10,0x28,0x44,0x00,// k 75
	0x00,0x00,0x41,0x7F,0x40,0x00,// l 76
	0x00,0x7C,0x04,0x18,0x04,0x78,// m 77
	0x00,0x7C,0x08,0x04,0x04,0x78,// n 78
	0x00,0x38,0x44,0x44,0x44,0x38,// o 79
	0x00,0xFC,0x24,0x24,0x24,0x18,// p 80
	0x00,0x18,0x24,0x24,0x18,0xFC,// q 81
	0x00,0x7C,0x08,0x04,0x04,0x08,// r 82
	0x00,0x48,0x54,0x54,0x54,0x20,// s 83
	0x00,0x04,0x3F,0x44,0x40,0x20,// t 84
	0x00,0x3C,0x40,0x40,0x20,0x7C,// u 85
	0x00,0x1C,0x20,0x40,0x20,0x1C,// v 86
	0x00,0x3C,0x40,0x30,0x40,0x3C,// w 87
	0x00,0x44,0x28,0x10,0x28,0x44,// x 88
	0x00,0x1C,0xA0,0xA0,0xA0,0x7C,// y 89
	0x00,0x44,0x64,0x54,0x4C,0x44,// z 90
	0x00,0x00,0x08,0x7F,0x41,0x00,// { 91
	0x00,0x00,0x00,0x7F,0x00,0x00,// | 92
	0x00,0x00,0x41,0x7F,0x08,0x00,// } 93
	0x00,0x08,0x04,0x08,0x10,0x08,// ~ 94

纵向取模,高位在上的数据还没弄好

总结

这个小游戏的关键在于真随机数的产生,蛇身数据的保存,蛇身的移动,碰撞检测、按键的检测、LED点阵屏的扫描显示、字符的滚动显示、各种标志的使用。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐