单片机课程设计,基于AT89C51和LCD-12864的贪吃蛇游戏

前言

课程设计报告以及keil文件和protues仿真在文末。


一、设计内容及要求

设计一个贪吃蛇游戏,使其具有以下游戏规则:

        ①当没有改变方向时,贪吃蛇沿原来路径一直前进

        ②贪吃蛇无法回头,只能异于当前方向改变行动

        ③蛇头碰到蛇身时死亡,游戏结束

        ④贪吃蛇吃到豆子则蛇身增加

        ⑤蛇头碰到墙壁时死亡,游戏结束

        ⑥当蛇长达到17及以上时出现山洞出口,进入则游戏胜利

二、系统的硬件设计

  • 主控芯片为AT89C51
  • LCD-12864为显示器输出
  • 多个按键作为控制器

2.1  Protues仿真图

2.2  主控芯片连接

 

 其中P0口连接LCD-12864的DB0~DB7,P2.0~P2.1连接LCD两个片选端,P2.2~P2.4连接LCD的指令线,读写线,以及电源线,P1.0~P1.4连接控制的四个按键。

2.3  LCD-12864连接

 

 分别连接至AT89C51的引脚

2.4  控制按键连接

 此法连接,当按下按键后,相应端口置零

三、系统的软件设计

3.1  贪吃蛇算法介绍

贪吃蛇游戏算法的实现,即如何通过液晶屏显示蛇的移动。

        1、其实蛇看似移动的过程中,实质只有两个点再变,即蛇头前进方向增加一个点,蛇尾减少一个点。

        2、知道如何显示蛇的移动后,再一个关键问题就是蛇的转折问题,如何控制蛇尾消失的点沿着蛇的路径。通俗的说就是蛇怎么实现曲折移动,这里就用到了循环队列,每次蛇头前进方向发生变化后,队列增加一个结点,结点中包含蛇头方向变化的位置,以及蛇头转变的方向。

        3、蛇尾每次移动的过程中都会与队列头指针所储存的位置做一次判断,当蛇尾到达这一位置后,从结点中取出保存的蛇头的移动方向赋给蛇尾,结点出队列即删除这一结点。

        4、由于队列是先进先出,所以每前进一步会先push进蛇头的节点,再pop出蛇尾的节点,即实现了蛇的曲折运动。

3.2  典型算法实现

3.2.1  采用循环队列存储蛇头的转向信息

        存储蛇头转向的信息使用了step记录,即每走一步使step+1,初始化蛇头step>蛇尾step,当按下按键时代表蛇头发生转向,用循环队列记录此时蛇头step坐标入队,同时使队尾指针+1,当不停前进蛇尾到达此step时,代表之前蛇头在此发生转向,则可不用具体xy坐标记录信息,从而节省存储空间。

// 按键扫描
void Key_Scan(void){
	if (KEY0 == 0)
		pause();
	else if (KEY1 == 0) {
		if (direction != 2) { //蛇不能直接掉头
			direction = 1;
			tailtrun_step[rear] = head_step;
			tailtrun_direction[rear] = 1;
			rear = (rear + 1) % 20;
		}
	}
	else if (KEY2 == 0) {
		if (direction != 1) {
			direction = 2;
			tailtrun_step[rear] = head_step;
			tailtrun_direction[rear] = 2;
			rear = (rear + 1) % 20;
		}
	}
	else if (KEY3 == 0) {
		if (direction != 4) {
			direction = 3;
			tailtrun_step[rear] = head_step;
			tailtrun_direction[rear] = 3;
			rear = (rear + 1) % 20;
		}
	}
	else if (KEY4 == 0) {
		if (direction != 3) {
			direction = 4;
			tailtrun_step[rear] = head_step;
			tailtrun_direction[rear] = 4;
			rear = (rear + 1) % 20;
		}
	}
}

3.2.2  根据行列具体点亮或熄灭LCD-12864某一个点

         由于LCD-12864的点阵点亮是八位DB0~DB7一次性操作一个page,代表一次只能八位一起操作,无法根据某一点的line,column点亮一个点,这里采用了位与或的思想,即根据某一点的line算出此坐标点对应的page中所在的位,后读取这点所在整列的数据与此位进行位或,即可在不改变原始数据的情况下点亮此点,熄灭相似,先对位信息进行取反,后与所在整列的数据与此位进行位与,即可熄灭某一具体点。

// 点亮一个点,行列
void show_p(uint8 screen, uint8 line, uint8 column) {
	SelectScreen(screen);
	
	dat = read_LCD_data(line / 8, column);
	Set_page(line/8);	
  Set_column(column);
	write_LCD_data(dat | bitTo16(line % 8));
}
// 熄灭一个点,行列
void out_p(uint8 screen, uint8 line, uint8 column) {
	SelectScreen(screen);
	
	dat = read_LCD_data(line / 8, column);
	Set_page(line/8);	
  Set_column(column);
	write_LCD_data(dat & ~(bitTo16(line % 8)));
}

3.2.3  碰撞检测

        同样采取位与的思想,函数传入具体点的line,column坐标,读取此列的信息,后进行位与操作,若结果不为零,则代表此点已被点亮,表示发生碰撞(蛇身,墙壁)。

// 碰撞检测
void hit_block(uint8 line, uint8 column) {
	dat = read_LCD_data(line * 2 / 8, column * 2);
	
	Set_page(line * 2 /8);	
	Set_column(column * 2);
	if ((dat & bitTo16(line * 2 % 8)) != 0)
		over();
}

3.2.4  2×2点扩大

      由于一次只亮一个点,导致贪吃蛇和豆子会很小,对视觉和操作是一个很大的考验,于是可以使用比例扩大到一个点为2×2,使得游戏界面变得更加亲和。对于原先的1点扩大到2个点,我使用了每个点对应一个坐标,使得4个点是一体的,为后续的函数书写提供极大便利。

 

 

// 2×2合并为一个点
void show_bp(uint8 screen,uint8 line,uint8 column){
	SelectScreen(screen);
	
	start_line = line * 2;start_column = column * 2;
	for(j=0;j<2;j++){
		dat = read_LCD_data(start_line / 8, start_column + j);
		write_LCD_data(dat | bitTo16(start_line % 8));
		dat = read_LCD_data(start_line / 8, start_column + j);
		write_LCD_data(dat | bitTo16(start_line % 8 + 1));
	}
	Set_page(start_line / 8);	//前面设置完会自动+1,需重新设置回来
	Set_column(start_column + 1);
}
void out_bp(uint8 screen,uint8 line,uint8 column){
	SelectScreen(screen);
	
	start_line = line * 2;start_column = column * 2;
	for(j=0;j<2;j++){
		dat = read_LCD_data(start_line / 8, start_column + j);
		write_LCD_data(dat & ~( bitTo16(start_line % 8)));
		dat = read_LCD_data(start_line / 8, start_column + j);
		write_LCD_data(dat & ~( bitTo16(start_line % 8 + 1)));
	}
	Set_page(start_line / 8);	//前面设置完会自动+1,需重新设置回来
	Set_column(start_column + 1);
}

3.2.5  字模算法

        由于LCD-12864采用的是点阵点亮,若想显示具体汉字信息,则需使用16×16的字模运算。可使用“PCtoLCD2002”软件进行字模提取。 

 在具体调用时,应设计相应算法,适应具体字模,例如16×16,由于一次只能书写8位,16则需分两次书写,先写上面的8×16,再写下方的8×16。

// 16×16字模
void show_ch(uint8 screen,uint8 page,uint8 column,uint8 *p)
{
	uint8 i;
	SelectScreen(screen);
	Set_page(page);
	Set_column(column);
	
	for(i=0;i<16;i++)	//采用16*16的字模
	{
		write_LCD_data(p[i]);
	}
	
	Set_page(page+1);
	Set_column(column);
	for(i=0;i<16;i++)	//采用16*16的字模,"小四号字"
	{
		write_LCD_data(p[i+16]);
	}
}
//游戏开始结束字模
uint8 code ch[]=
{
0x10,0x60,0x02,0x8C,0x00,0x08,0xF9,0x4E,0xC8,0x20,0x58,0x4F,0x48,0xC8,0x08,0x00,
0x04,0x04,0x7E,0x81,0x40,0x30,0x0F,0x40,0x7F,0x00,0x44,0x84,0x7F,0x04,0x04,0x00,/*"游",0*/

0x00,0x08,0x48,0x88,0x08,0xC8,0x38,0x40,0x40,0x40,0xFF,0x20,0x22,0xAC,0x20,0x00,
0x00,0x20,0x10,0x0C,0x03,0x04,0x18,0x80,0x40,0x20,0x17,0x18,0x26,0x41,0xF0,0x00,/*"戏",1*/

0x80,0x82,0x82,0x82,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x82,0x82,0x82,0x80,0x00,
0x00,0x80,0x40,0x30,0x0F,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,/*"开",2*/

0x10,0x10,0xF0,0x1F,0x10,0xF0,0x00,0x40,0xE0,0x58,0x47,0x40,0x50,0x60,0xC0,0x00,
0x40,0x22,0x15,0x08,0x16,0x21,0x00,0x00,0xFE,0x42,0x42,0x42,0x42,0xFE,0x00,0x00,/*"始",3*/

0x20,0x30,0xAC,0x63,0x20,0x18,0x08,0x48,0x48,0x48,0x7F,0x48,0x48,0x48,0x08,0x00,
0x22,0x67,0x22,0x12,0x12,0x12,0x00,0xFE,0x42,0x42,0x42,0x42,0x42,0xFE,0x00,0x00,/*"结",4*/

0x04,0x04,0xE4,0x24,0x24,0x24,0x24,0xFF,0x24,0x24,0x24,0x24,0xE4,0x04,0x04,0x00,
0x40,0x40,0x27,0x22,0x12,0x0A,0x06,0xFF,0x06,0x0A,0x12,0x22,0x27,0x40,0x40,0x00,/*"束",5*/

0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*"!",6*/
};
// 游戏开始界面
void start(){
	show_im(1, 2, 0 * 16, image1);
	show_ch(1, 4, 2 * 16, ch + 32 * 0);
	show_ch(1, 4, 3 * 16, ch + 32 * 1);
	show_ch(2, 4, 0 * 16, ch + 32 * 2);
	show_ch(2, 4, 1 * 16, ch + 32 * 3);
	show_ch(2, 4, 2 * 16, ch + 32 * 6);
	show_ch(2, 4, 3 * 16, ch + 32 * 6);
	pause();
	ClearScreen(0);
}

附录

百度网盘,提取码:r70l

  • 10
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sumzeek丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值