⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 slace.c

📁 这是一个手机吞食蛇程序
💻 C
字号:
//贪吃蛇游戏程序,屏左半部用于游戏活动,右半部为分数显示
//游戏屏为16*16游戏点阵,可容纳蛇身块数256。每个游戏点阵又由4*4个LCD基本点阵组成
//蛇行标志在定时器上置位,这里为游戏的主体部分。


#define uchar unsigned char
#define uint  unsigned int
#define ulong unsigned long

//#define LCMD	XBYTE[0xAfff]		// 液晶数据口
//#define LCMC	XBYTE[0xAbff]		// 液晶命令口

#define TIME_RUN 10					//定时器分品系数

#include "study.h"
#include "reg51.h"
#include "absacc.h"
#include "intrins.h"

//游戏部分
//x,y最大极限
#define MAX_GAME_X 15
#define MAX_GAME_Y 12


//#define lcd_no_read 1				//编译选项,把这项屏蔽掉就采用LCD读出方式,否则采用显存形式


xdata uchar snake_flag,			//蛇头标志  7  6  5  4  3  2         1     0 
				 			//          上 下 左 右 x  gameover  food  run
	        snake_len,			//蛇身长度
	        snake_food;			//食物位置,高4位Y,低4位x
	
xdata uchar snake_body[256];	//蛇身每个部分的数据
							//          7  6  5  4  3  2  1  0
							//			高4位Y方向  低4位X方向	

#ifdef lcd_no_read
xdata uchar lcd_buf[8][64];//lcd缓冲,用于记录LCD内部的点阵,可以理解为显存
							//当LCD无读出功能时,就要采用显示缓冲。本LCD为可读,一般不用这个功能
							//缓冲只记录蛇身活动的部分,即LCD左半屏
#endif

extern uchar xdata set_time;			//贪吃蛇游戏频率计数值

/******************************************************
*	游戏LCD部分,根据游戏的特点把LCD分成16*16块
*	用作游戏点阵,
*******************************************************/

//
//函数名:clr_game_dot
//功能:清一个游戏点
//输入参数:游戏点的X,Y坐标
//注意事项:这里的X,Y坐标和LCD底层的X,Y坐标不同,他最大只能是MAX_GAME_X,MAX_GAME_Y
//使用方式:内部调用
void clr_game_dot(uchar x,uchar y)
{
	xdata uchar lcd_x,i,tmp;
	while(x>MAX_GAME_X)x-=(MAX_GAME_X+1);	//这个是写程序习惯的保护措施,预防输入范围过大
	while(y>MAX_GAME_Y)y-=(MAX_GAME_Y+1);
	lcd_x=x<<2;
	wricmd(0xb0+y/2);
    wricmd(lcd_x&0x0f);
	wricmd(0x10|(lcd_x>>4));
	wricmd(0xe0);
	if(y%2)//行的下半部
	{
		for(i=0;i<4;i++)
		{
#ifdef lcd_no_read				//以下是显存法的清点程序,其他例如亮点的部分和这个原理一样
	
			tmp=lcd_buf[y>>1][(x<<2)+i];			//先从缓冲读出要修改的LCD片的数据
			tmp&=0x0f;							//清对应的游戏点
			wridata(tmp);
			_nop_();
			lcd_buf[y>>1][(x<<2)+i]=tmp;			//把新数据写回缓冲
#else
		tmp=readdata();
		tmp=readdata();						//读LCD的方法,要求连读2次
		wridata(tmp&0x0f);

#endif
		}
	}
	else			//行的上半部,下同
	{
		for(i=0;i<4;i++)
		{
#ifdef lcd_no_read
		
			tmp=lcd_buf[y>>1][(x<<2)+i];
			tmp&=0xf0;
			wridata(tmp);
			_nop_();
			lcd_buf[y>>1][(x<<2)+i]=tmp;
#else
		tmp=readdata();
		tmp=readdata();					
		wridata(tmp&0xf0);
#endif
		}
	}
	wricmd(0xee);
}

//函数名:fill_game_dot
//功能:亮一个游戏点
//输入参数:游戏坐标的X,Y坐标				
//注意事项:这里的X,Y坐标和LCD底层的X,Y坐标不同,他最大只能是MAX_GAME_X,MAX_GAME_Y
//	   这个函数和上面的clr_game_dot基本相同,只是在写LCD数据的时候是全1而不是0	
//使用方式:内部调用
void fill_game_dot(uchar x,uchar y)
{
	xdata uchar lcd_x,i,tmp;
	while(x>MAX_GAME_X)x-=(MAX_GAME_X+1);
	while(y>MAX_GAME_Y)y-=(MAX_GAME_Y+1);
	lcd_x=x<<2;
	wricmd(0xb0+y/2);
    wricmd(lcd_x&0x0f);
	wricmd(0x10|(lcd_x>>4));
	wricmd(0xe0);
	if(y%2)//行的下半部
	{
		for(i=0;i<4;i++)
		{
#ifdef lcd_no_read		
			tmp=lcd_buf[y>>1][(x<<2)+i];
			tmp|=0xf0;
			wridata(tmp);
			_nop_();
			lcd_buf[y>>1][(x<<2)+i]=tmp;		
#else
		tmp=readdata();
		tmp=readdata();						
		wridata(tmp|0xf0);
#endif
		}
	}
	else
	{
		for(i=0;i<4;i++)
		{
#ifdef lcd_no_read		
			tmp=lcd_buf[y>>1][(x<<2)+i];
			tmp|=0x0f;
			wridata(tmp);
			_nop_();
			lcd_buf[y>>1][(x<<2)+i]=tmp;
#else
		tmp=readdata();
		tmp=readdata();						
		wridata(tmp|0x0f);
#endif
		}
	}
	wricmd(0xee);
}

//函数名:fill_game_dot2
//功能:亮一个游戏点(另一种方式,这里用来显示食物用)
//输入参数:X,Y
//注意事项:X,Y为游戏的点阵,非LCD点阵...还有LCD填充数据是0x05或0x50
//使用方式:内部调用,显示蛇的食物的时候用这个函数,区分开蛇身和食物.
void fill_game_dot2(uchar x,uchar y)
{
	xdata uchar lcd_x,i,tmp;
	while(x>MAX_GAME_X)x-=(MAX_GAME_X+1);
	while(y>MAX_GAME_Y)y-=(MAX_GAME_Y+1);
	lcd_x=x<<2;
	wricmd(0xb0+y/2);
    wricmd(lcd_x&0x0f);
	wricmd(0x10|(lcd_x>>4));
	wricmd(0xe0);
	if(y%2)
	{
		for(i=0;i<4;i++)
		{	
#ifdef lcd_no_read		
			tmp=lcd_buf[y>>1][(x<<2)+i];
			tmp|=0x50;
			wridata(tmp);
			_nop_();
			lcd_buf[y>>1][(x<<2)+i]=tmp;			
#else
		tmp=readdata();
		tmp=readdata();						
		wridata(tmp|0x50);
#endif
		}
	}
	else
	{
		for(i=0;i<4;i++)
		{
#ifdef lcd_no_read		
			tmp=lcd_buf[y>>1][(x<<2)+i];
			tmp|=0x05;
			wridata(tmp);
			_nop_();
			lcd_buf[y>>1][(x<<2)+i]=tmp;
#else
		tmp=readdata();
		tmp=readdata();						
		wridata(tmp|0x05);
#endif
		}
	}
	wricmd(0xee);
}


/************************************************************
*
*					游戏算法部分(4*4LCD)
*
**************************************************************/

//函数名 game_init()
//功能:游戏开始的时候初始化画面的,这里只是简单地把132*64LCD用一条中间线划分开来
//注意事项:暂时在中间画条线用来划分游戏空间
//使用方式:内部调用,
void game_init()
{
	xdata uchar i;

#ifdef lcd_no_read			//如果用显存,需要先置初值为0
	xdata uchar j;
	for(j=0;j<8;j++)
	for(i=0;i<64;i++)
	lcd_buf[j][i]=0;
#endif

	cls(9);					
	initlcd();
	//画竖线
	for(i=0;i<7;i++)
	{
		wricmd(0xb0+i);
		wricmd(64&0x0f);
		wricmd(0x10|(64>>4));
		wricmd(0xe0);
		wridata(0xff);
	}
	wricmd(0xee);
}


//函数名:snake_init
//功能:蛇初始化
//注意事项:初始化只有3节蛇身,向右跑
//使用情况:内部调用
void snake_init()
{
	fill_game_dot(0,0);		//显示射身
	fill_game_dot(1,0);
	fill_game_dot(2,0);
	snake_len=2;
	snake_flag=0x10;		//蛇的初始化,3个身.向右跑
	snake_body[0]=0x02;		//装入射身数据
	snake_body[1]=0x01;
	snake_body[2]=0x00;
	//一开始游戏时的文字部分
	setcursor(8,2);
	lcddigit(snake_len-2);
}

//函数名:show_mark
//功能:显示当前分数,暂时以蛇身个数为分数
//参数说明:0,和非0, 0代表游戏中的显示,!0代表挂了的显示
//注意事项:调用到LCD.c显示函数,并需要汉字库的支持.
//			返回值在GAMEOVER时候有效,返回0退出游戏,1从新游戏			
//使用情况:snake_run()在蛇吃到食物的时候调用,在GAMEOVER后调用
uchar show_mark(uchar mode)
{
	xdata uchar ch;
	setcursor(8,2);
	lcddigit(snake_len-2);

	if(mode)//gameover中显示
	{	
		setcursor(8,0);
		lcdstring("完");
		do	ch=getkey(1000);
		while( (ch!='C') && (ch!='Y') );			//游戏结束了会在这里死等,直到用户按键
		if(ch=='Y')
			return(1);
		else 
			return(0);
	}
	
	return(0);	

}

//函数名:snake_run
//功能:蛇运行函数
//输入参数:一个全局变量flag_snake,蛇根据这个变量判断运动方向
//注意事项:蛇跑动函数,用于判断路径,食物,长大,死亡
//使用情况:内部调用
void snake_run()
{
	xdata uchar tmp_head_x,tmp_head_y;
	xdata uchar i;
	switch(snake_flag&0xf0)		//取蛇头方向
		{
		case 0x80://向上走 y--
					tmp_head_x=snake_body[0]&0x0f;
					tmp_head_y=(snake_body[0]>>4);						
					if(tmp_head_y==0)snake_flag|=0x04;//这个代表撞墙了,就置GAMEOVER标志,下同
					else tmp_head_y--;
					break;
		case 0x40://向下走 y++
					tmp_head_x=snake_body[0]&0x0f;
					tmp_head_y=(snake_body[0]>>4);
					if(tmp_head_y==MAX_GAME_Y)snake_flag|=0x04;
					else tmp_head_y++;
					
					break;
		case 0x20://向左走 x--
					tmp_head_y=snake_body[0]>>4;
					tmp_head_x=snake_body[0]&0x0f;
					if(tmp_head_x==0)snake_flag|=0x04;
					else tmp_head_x--;
					break;
		case 0x10://向右走 x++						
					tmp_head_y=snake_body[0]>>4;
					tmp_head_x=snake_body[0]&0x0f;
					if(tmp_head_x==MAX_GAME_X)snake_flag|=0x04;
					else tmp_head_x++;
					break;
		default:break;
		}
		if(!(snake_flag&0x04))			//如果在之前没有撞墙,就可以进行下一步判断
		{			
			//以下是得到食物的判断。

			if(snake_food!=( (tmp_head_y<<4)+tmp_head_x ))//蛇头和食物坐标没重叠就代表没有吃到食物
			{//得不到食物的处理
				clr_game_dot(snake_body[snake_len]&0x0f,snake_body[snake_len]>>4);//灭蛇尾巴

				for(i=snake_len;i>0;i--)											//柔体传动
				snake_body[i]=snake_body[i-1];
				snake_body[0]=( tmp_head_y<<4 ) + tmp_head_x;
			}
			else
			{//得到食物的处理			
				snake_body[snake_len+1]=snake_body[snake_len];				//保留蛇尾巴(这是增长型柔体传动)
				for(i=snake_len;i>0;i--)
				snake_body[i]=snake_body[i-1];

				snake_body[0]=( tmp_head_y<<4 ) + tmp_head_x;				//新蛇头
				snake_len++;//长度增加1
				snake_flag&=~0x02;//清食物标志
				show_mark(0);//显示分数
			}	
			
			fill_game_dot(tmp_head_x,tmp_head_y);//显示新蛇头
		}
		for(i=1;i<snake_len+1;i++)						//判断是否撞中自己
		{
			if(snake_body[0]==snake_body[i])				
			{
				snake_flag|=0x04;						//撞中了就置GAMEOVER标志
				break;
			}
		}
}



//函数名:set_food
//功能:放食物
//注意事项:这个函数在被调用前会先判断是否需要放食物,
//			这里用自己编写的随机数来产生食物,随机数和蛇身位置,定时器有关
//			每次放食物的时候必须先判断是否和蛇身重叠了,重叠了要重新放
//			这里设定了如果蛇长度达到某值了就不再放食物.
//影响变量:snake_food
//使用情况:内部调用
void set_food()
{
	
	xdata uchar seed0,seed1,i=0;
	seed0=snake_body[snake_len-2];

	if(snake_len==100)
	{
   	return;//蛇都快满屏了,就不放食物了,事实上我还没玩过超过100的呢:)
	}
	seed0=((seed1>>3)*4+seed0+TL0);//随机数的生成,其实乱做就可以了:)
	seed1=(snake_body[0]>>4)+seed0;
	snake_food=(seed1+seed0*3);
	while( (snake_food&0xf0)>0xc0)
	snake_food+=0x30;				//Y位置不得超过12
	
	
food:	
	for(i=0;i<snake_len+1;i++)	//食物不能和蛇身重叠
	{
		if( snake_body[i]==snake_food )
		{
		
			snake_food=(snake_food+0x01);		//如果重叠了,位置就+1,然后
			if((snake_food&0xf0)>0xc0)
				snake_food&=0x0f;				//Y方向只能到12,超过12就要回0
			goto food;							//重新比较,这里可以换成i=255,效果一样
		}
 	}

	fill_game_dot2(snake_food&0x0f,snake_food>>4);//放食物
	snake_flag|=0x02;//置有食物标志
}

//	函数名:定时器1初始化程序
//	晶振22.1184,定时时间35MS
void snake_init_timer0(void)
{
	TMOD=0x1;
 	TH0=0x0;
 	TL0=0x0;
}




//函数名:snake_game
//功能:整个游戏的主要函数
//注意事项:游戏利用了定时器产生蛇的运行速速度
//			调用前应该先初始化定时器
//使用情况:外部调用
//

extern data uchar time_service;				//这个东东是使定时器公用的。
void snake_game()
{
	xdata uchar tmp_snake_flag,ch;
	cls(8);
	time_service=1;							//切换定时器为贪吃蛇所用
	set_time=0;							
	lcdstring("贪吃蛇游戏\r\nC退出,任意键进入");
	while((ch=getkey(1000))==0);
	if(ch=='C')	return;
	ch=0;

		
begin_game:
	game_init();	//一堆初始化
	snake_init();
	snake_init_timer0();//timer0_init();
	TR0=1;
	ET0=1;
	EA=1;
	
	tmp_snake_flag=snake_flag;
	while(1)
	{
		ch=getkey(1000);
		switch(ch)
		{
			case '2'://按了上按键
					if( (snake_flag&0x40) || (snake_flag&0x80) )break;//向上走的时候,上下键盘都无效,下同
					tmp_snake_flag&=0x0f;tmp_snake_flag|=0x80;
					break;
			case '5'://按了下按键
					if( (snake_flag&0x80) || (snake_flag&0x40) )break;
					tmp_snake_flag&=0x0f;tmp_snake_flag|=0x40;
					break;
			case '4'://按了左按键
					if( (snake_flag&0x10 || (snake_flag&0x20) ))break;
					tmp_snake_flag&=0x0f;tmp_snake_flag|=0x20;
					break;
			case '6'://按了右按键
					if( (snake_flag&0x20 || (snake_flag&0x10) ))break;
					tmp_snake_flag&=0x0f;tmp_snake_flag|=0x10;
					break;
			case 'C'://任何时候,按C就结束游戏
					
					return;
			default:
					break;
	 	}

		if(!(snake_flag&0x02))	//如果图上已经没食物了,就
			set_food();			//放食物	

		if((snake_flag&0x01))								//判断是否够时间跑一步
		{
			snake_flag=( snake_flag&0x0e ) | tmp_snake_flag;//取消跑动标志,置新方向
			snake_run();
			tmp_snake_flag=snake_flag;
		}
	
		if((snake_flag&0x04))			//判断游戏结束标志
		{
			if(show_mark(1))
			{
				snake_flag&=~0x04;
				goto begin_game;
			}
			else
			{
				time_service=0;
				return;						//游戏结束			
			}
			
		}						
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -