使用51单片机实现贪吃蛇游戏(基于清翔单片机)

目录

一、创作背景

二、实现过程

1.一些声明与定义

2.键盘扫描

3.定时器中断

5.蛇的初始化

6.蛇体的移动

7.食物的创建

8.食物的检测和身体的增长

9.检测死亡

10.总代码

总结


一、创作背景

由于大一的一个小作业,花了10天完成了这个小项目,中途克服了很多困难,发此文章以作纪念。

二、实现过程

1.一些声明与定义

这些后面会讲到。

#include <reg52.h>
#include <stdlib.h>
#define uint unsigned int
#define uchar unsigned char
#include <intrins.h>

sbit DIO = P3 ^ 4;
sbit S_CLK = P3 ^ 5;
sbit R_CLK = P3 ^ 6;
sbit key_s2 = P3 ^ 0;//独立键盘4个按键
sbit key_s3 = P3 ^ 1;
sbit key_s4 = P3 ^ 2;
sbit key_s5 = P3 ^ 3;

uchar coorx[8] = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe };
uchar coory[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 };
uchar snakex[20];//蛇体x坐标
uchar snakey[20];//蛇体y坐标
uchar m = 0;
uchar len = 1;//蛇体长度
uchar foodx, foody;//食物x, y坐标
uchar d, xx, yy;

2.键盘扫描

这里我用的是独立键盘,由于点阵屏的IO口和矩阵键盘的IO口冲突导致显示的内容不对(个人能力有限,暂时没办法解决这个冲突问题,只好避开),所以采用了独立键盘。同时也是因为这个原因我没有采用扫描的方式,而是一个一个判断。如果开发板不冲突可以选择矩阵键盘。

char key = 0;

void delay(uint z)
{
	uint x, y;
	for (x = z; x > 0; x--)
		for (y = 114; y > 0; y--);
}

void keyscan()
{
	if (key_s2 == 0)
	{
		delay(10);
		if (key_s2 == 0)
		{
			if (key != 4)
				key = 1;
		}
	}
	if (key_s3 == 0)
	{
		delay(10);
		if (key_s3 == 0)
		{
			if (key != 3)
				key = 2;
		}
	}
	if (key_s4 == 0)
	{
		delay(10);
		if (key_s4 == 0)
		{
			if (key != 2)
				key = 3;
		}
	}
	if (key_s5 == 0)
	{
		delay(10);
		if (key_s5 == 0)
		{
			if (key != 1)
				key = 4;
		}
	}
}

3.定时器中断

这里是相关寄存器的配置,定时为5ms,用处是为了控制蛇的移动速度已经键盘的判断。

void timer0Init()
{
	EA = 1;
	ET0 = 1;
	TR0 = 1;
	TMOD = 0X01;
	TH0 = 0XED;
	TL0 = 0XFF;
}
void SendByte(uchar x, uchar y)//用来接收行列值,这里x和y是坐标,点阵屏左下角为坐标原点
{
	uchar i, j, dat1, dat2;
	dat1 = coorx[x - 1];//这里通过前面定义的两个数组将坐标转化为了行列的十六进制数
	dat2 = coory[y - 1];
	for (i = 0; i < 8; i++)
	{
		if (dat1 & 0x01)
			DIO = 1;
		else
			DIO = 0;
		S_CLK = 1;//穿行输入
		S_CLK = 0;
		dat1 >>= 1;
	}
	for (j = 0; j < 8; j++)
	{
		if (dat2 & 0x01)
			DIO = 1;
		else
			DIO = 0;
		S_CLK = 1;//串行输入
		S_CLK = 0;
		dat2 >>= 1;
	}
	R_CLK = 1;//并行输出
	R_CLK = 0;
}

5.蛇的初始化

main函数主要内容

void main()
{
	timer0Init();//定时器0初始化
	snakex[0] = 5;//蛇体初始x坐标
	snakey[0] = 5;//蛇初始y坐标
	creat_food();
	while (1)
	{
		snakemove();
		FoodAnd();
	}
}

void timer0() interrupt 1
{
	TH0 = 0XED;
	TL0 = 0XFF;
	m++;//每加到一百蛇体移动一次,速度可以自己调
	keyscan();//键盘扫描
	for (d = 0; d < len; d++)
	{
		SendByte(snakex[d], snakey[d]);//将蛇体显示在点阵屏上,参数是xy坐标
	}
	SendByte(foodx, foody);//显示食物
	CheckDead();//检验是否死亡
}

6.蛇体的移动

void left()//上下左右都是一个道理
{
	uchar i, x1, x2, y1, y2;
	x1 = snakex[0];//保留上一结点的x轴状态
	snakex[0]--;//坐标减1
	if (snakex[0] == 0)//这里是判定是否碰到边界了,这里设定的是可以从另一边出来,当然也可以设定为碰到就死
		snakex[0] = 8;
	y1 = snakey[0];//保留上一结点的y轴状态
	for (i = 1; i < len; i++)
	{
		y2 = snakey[i];//进行各个结点的状态移动,形成蛇运动的效果
		snakey[i] = y1;
		y1 = y2;

		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;
	}
}

void up()
{
	uchar i, x1, x2, y1, y2;
	y1 = snakey[0];
	snakey[0]--;
	if (snakey[0] == 0)
		snakey[0] = 8;
	x1 = snakex[0];
	for (i = 1; i < len; i++)
	{
		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;

		y2 = snakey[i];
		snakey[i] = y1;
		y1 = y2;
	}
}

void down()
{
	uchar i, x1, x2, y1, y2;
	y1 = snakey[0];
	snakey[0]++;
	if (snakey[0] == 9)
		snakey[0] = 1;
	x1 = snakex[0];
	for (i = 1; i < len; i++)
	{
		y2 = snakey[i];
		snakey[i] = y1;
		y1 = y2;

		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;
	}
}

void right()
{
	uchar i, x1, x2, y1, y2;
	x1 = snakex[0];
	snakex[0]++;
	if (snakex[0] == 9)
		snakex[0] = 1;
	y1 = snakey[0];
	for (i = 1; i < len; i++)
	{
		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;

		y2 = snakey[i];
		snakey[i] = y1;
		y1 = y2;
	}
}

void snakemove()
{
	if (key == 1 && m == 100)//这里是判断按键值并且m要达到100,也就是500ms移动一次
	{
		left();
		m = 0;//要清一下零
	}
	if (key == 2 && m == 100)
	{
		up();
		m = 0;
	}
	if (key == 3 && m == 100)
	{
		down();
		m = 0;
	}
	if (key == 4 && m == 100)
	{
		right();
		m = 0;
	}
}

7.食物的创建

这一部分本来以为使用c语言里面srand(unsigned seed)(这个函数只要种子的值是变化的生成的就是某种意义上随机数)函数加上一个一直变化的时间戳就可以生成随机数的,但是后来发现keil里面没有time.h这个头文件,导致没法实现真正意义的随机数。这个问题暂留

uchar CheckFood()//检验食物有没有和蛇体重合
{
	uchar i;
	for (i = 0; i < len; i++)
	{
		if (foodx == snakex[i] && foody == snakey[i])
		{
			return 1;
		}
	}
	return 0;
}

void creat_food()
{
	do
	{
		foodx = (uchar)(rand() % 8 + 1);
		foody = (uchar)(rand() % 8 + 1);
	} while (CheckFood() != 0);
}

8.食物的检测和身体的增长

void FoodAnd()//检验食物有没有被吃掉
{
	if (foodx == snakex[0] && foody == snakey[0])
	{
		if (len < 20)
		{
			snakex[len] = snakex[len - 1];//和之前一样,把上一个结点的状态赋给下一个结点,用来增加长度
			snakey[len] = snakey[len - 1];
			len++;
		}
		creat_food();//被吃掉重新生成食物
	}
}

9.检测死亡

这里i = 2是因为等于1的时候会在吃食物的时候冲突,而蛇头不可能碰到碰到第二节,所以初值设定为2

void CheckDead()
{
	uchar i;
	for (i = 2; i < len; i++)
	{
		if (snakex[0] == snakex[i] && snakey[0] == snakey[i])
		{
			while (1);
		}
	}
}

10.总代码

#include <reg52.h>
#include <stdlib.h>
#define uint unsigned int
#define uchar unsigned char
#include <intrins.h>

sbit DIO = P3 ^ 4;
sbit S_CLK = P3 ^ 5;
sbit R_CLK = P3 ^ 6;
sbit key_s2 = P3 ^ 0;
sbit key_s3 = P3 ^ 1;
sbit key_s4 = P3 ^ 2;
sbit key_s5 = P3 ^ 3;

uchar coorx[8] = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe };
uchar coory[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 };
uchar snakex[20];
uchar snakey[20];
uchar m = 0;
uchar len = 1;
uchar foodx, foody;
uchar d, xx, yy;


char key = 0;

void delay(uint z)
{
	uint x, y;
	for (x = z; x > 0; x--)
		for (y = 114; y > 0; y--);
}

void keyscan()
{
	if (key_s2 == 0)
	{
		delay(10);
		if (key_s2 == 0)
		{
			if (key != 4)
				key = 1;
		}
	}
	if (key_s3 == 0)
	{
		delay(10);
		if (key_s3 == 0)
		{
			if (key != 3)
				key = 2;
		}
	}
	if (key_s4 == 0)
	{
		delay(10);
		if (key_s4 == 0)
		{
			if (key != 2)
				key = 3;
		}
	}
	if (key_s5 == 0)
	{
		delay(10);
		if (key_s5 == 0)
		{
			if (key != 1)
				key = 4;
		}
	}
}


void SendByte(uchar x, uchar y)//用来接收行列值,这里x和y是坐标,点阵屏左下角为坐标原点
{
	uchar i, j, dat1, dat2;
	dat1 = coorx[x - 1];//这里通过前面定义的两个数组将坐标转化为了行列的十六进制数
	dat2 = coory[y - 1];
	for (i = 0; i < 8; i++)
	{
		if (dat1 & 0x01)
			DIO = 1;
		else
			DIO = 0;
		S_CLK = 1;//穿行输入
		S_CLK = 0;
		dat1 >>= 1;
	}
	for (j = 0; j < 8; j++)
	{
		if (dat2 & 0x01)
			DIO = 1;
		else
			DIO = 0;
		S_CLK = 1;//串行输入
		S_CLK = 0;
		dat2 >>= 1;
	}
	R_CLK = 1;//并行输出
	R_CLK = 0;
}

uchar CheckFood()//检验食物有没有和蛇体重合
{
	uchar i;
	for (i = 0; i < len; i++)
	{
		if (foodx == snakex[i] && foody == snakey[i])
		{
			return 1;
		}
	}
	return 0;
}

void creat_food()
{
	do
	{
		foodx = (uchar)(rand() % 8 + 1);
		foody = (uchar)(rand() % 8 + 1);
	} while (CheckFood() != 0);
}

void FoodAnd()//检验食物有没有被吃掉
{
	if (foodx == snakex[0] && foody == snakey[0])
	{
		if (len < 20)
		{
			snakex[len] = snakex[len - 1];//和之前一样,把上一个结点的状态赋给下一个结点,用来增加长度
			snakey[len] = snakey[len - 1];
			len++;
		}
		creat_food();//被吃掉重新生成食物
	}
}

void left()//上下左右都是一个道理
{
	uchar i, x1, x2, y1, y2;
	x1 = snakex[0];//保留上一结点的x轴状态
	snakex[0]--;//坐标减1
	if (snakex[0] == 0)//这里是判定是否碰到边界了,这里设定的是可以从另一边出来,当然也可以设定为碰到就死
		snakex[0] = 8;
	y1 = snakey[0];//保留上一结点的y轴状态
	for (i = 1; i < len; i++)
	{
		y2 = snakey[i];//进行各个结点的状态移动,形成蛇运动的效果
		snakey[i] = y1;
		y1 = y2;

		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;
	}
}

void up()
{
	uchar i, x1, x2, y1, y2;
	y1 = snakey[0];
	snakey[0]--;
	if (snakey[0] == 0)
		snakey[0] = 8;
	x1 = snakex[0];
	for (i = 1; i < len; i++)
	{
		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;

		y2 = snakey[i];
		snakey[i] = y1;
		y1 = y2;
	}
}

void down()
{
	uchar i, x1, x2, y1, y2;
	y1 = snakey[0];
	snakey[0]++;
	if (snakey[0] == 9)
		snakey[0] = 1;
	x1 = snakex[0];
	for (i = 1; i < len; i++)
	{
		y2 = snakey[i];
		snakey[i] = y1;
		y1 = y2;

		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;
	}
}

void right()
{
	uchar i, x1, x2, y1, y2;
	x1 = snakex[0];
	snakex[0]++;
	if (snakex[0] == 9)
		snakex[0] = 1;
	y1 = snakey[0];
	for (i = 1; i < len; i++)
	{
		x2 = snakex[i];
		snakex[i] = x1;
		x1 = x2;

		y2 = snakey[i];
		snakey[i] = y1;
		y1 = y2;
	}
}

void CheckDead()
{
	uchar i;
	for (i = 2; i < len; i++)
	{
		if (snakex[0] == snakex[i] && snakey[0] == snakey[i])
		{
			while (1);
		}
	}
}

void snakemove()
{
	if (key == 1 && m == 100)//这里是判断按键值并且m要达到100,也就是500ms移动一次
	{
		left();
		m = 0;//要清一下零
	}
	if (key == 2 && m == 100)
	{
		up();
		m = 0;
	}
	if (key == 3 && m == 100)
	{
		down();
		m = 0;
	}
	if (key == 4 && m == 100)
	{
		right();
		m = 0;
	}
}

void timer0Init()
{
	EA = 1;
	ET0 = 1;
	TR0 = 1;
	TMOD = 0X01;
	TH0 = 0XED;
	TL0 = 0XFF;
}

void main()
{
	timer0Init();//定时器0初始化
	snakex[0] = 5;//蛇体初始x坐标
	snakey[0] = 5;//蛇初始y坐标
	creat_food();
	while (1)
	{
		snakemove();
		FoodAnd();
	}
}

void timer0() interrupt 1
{
	TH0 = 0XED;
	TL0 = 0XFF;
	m++;//每加到一百蛇体移动一次,速度可以自己调
	keyscan();//键盘扫描
	for (d = 0; d < len; d++)
	{
		SendByte(snakex[d], snakey[d]);//将蛇体显示在点阵屏上,参数是xy坐标
	}
	SendByte(foodx, foody);//显示食物
	CheckDead();//检验是否死亡
}

总结

代码由于本人实力问题还有部分不足之处希望支持。

物联沃分享整理
物联沃-IOTWORD物联网 » 使用51单片机实现贪吃蛇游戏(基于清翔单片机)

发表评论