为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

贪吃蛇

2011-11-08 25页 pdf 394KB 56阅读

用户头像

is_563186

暂无简介

举报
贪吃蛇 单片机游戏设计 1.概念 对于大部分单片机+LCD的游戏设计,基本采用前后台方式,就是一个台中 断,一个台循环 (哪个前哪个后忘了),LCD部分基本是以固定点阵形式设计,什么叫固定点 阵 首先先 明确,我们设计的游戏不是什 么魔兽争霸或 CS,而是黑白形式的固定点阵游戏,例如常见著名游戏贪吃 蛇或俄罗斯方 块.他们的每个点 都是预先就固定下来的,而且是比较大的点,这类专门的游戏机玻璃是经过 厂家开模出来 的,有固定的 COM,SEG线,然后接到专门的单片...
贪吃蛇
单片机游戏设计 1.概念 对于大部分单片机+LCD的游戏设计,基本采用前后台方式,就是一个台中 断,一个台循环 (哪个前哪个后忘了),LCD部分基本是以固定点阵形式设计,什么叫固定点 阵 首先先 明确,我们设计的游戏不是什 么魔兽争霸或 CS,而是黑白形式的固定点阵游戏,例如常见著名游戏贪吃 蛇或俄罗斯方 块.他们的每个点 都是预先就固定下来的,而且是比较大的点,这类专门的游戏机玻璃是经过 厂家开模出来 的,有固定的 COM,SEG线,然后接到专门的单片机上,例如常用的 6502指 令集合的单片 机,呵呵,我以前就用 6502设计过一个. 对于业余设计的游戏,我们一般用如 128*64的 LCD来显示,那么我们设计 的时候首先应该把 这个 128*64的 LCD分块,也就是分出固定点阵出来.LCD的基本点阵是 128*64,就是 8192 总之就是好多个点啦,但我们事实上不一定要运算这么多个点,除非你做的 游戏很 有看头.例如你只用左边 64*64的地方来做贪吃蛇,那么你打算你的贪吃蛇 的活动空间是多 少呢 如果是 8*8个点的话,算一下就是每个点 64/8,64/8,也就是 8*8个基 本点阵,不过 想好玩一点,当然就是要有 16*16个点的活动空间啦,那么每个固定点阵就 要占 4*4的基本 点阵了.要注意,这些 4*4的东西在 64*64LCD上共 16*16个,每个都要用来 独立运算. 2.时钟 这个其实是游戏的速度,对于一般的弱智类游戏机,他也代了难度,物体 在每个时钟到 达的时候就传动一次,例如俄罗斯方块没个时刻向下跑一层.赛车游戏每个 时刻想前走一 步.一般这类时钟的时间在 0.X秒到 1秒之间,物体有规律地匀速运动,让人 看到感觉是连 动. 3.运动 在这里,我先介绍两种比较普遍的弱智游戏机的物体运动规则:柔体传动, 刚体传动. 刚体传动 代表作是俄罗斯方块,所谓刚体,就是硬硬的一个东东,运动的时候也不怎 么旋转(注 意,俄罗斯方块是会旋转,但其实他是没有经过算法的旋转,纯提取数组的 方式,也就是 把一个放块做成 4个模式的点阵结构,其实就是 4个方向,呵呵)对于刚体的 传动,在每个 时钟到达的时候向一个方向(很可能是用户输入的)运动一个固定点阵.如 果以坐标来表 达,就是物体的所有基本点阵同时向一个方向(X或 Y)移动一个单位. 柔体传动 代表作是贪吃蛇,贪吃蛇跑动的时候并不是整条蛇向一个方向动的(呵呵, 蛇蛇身体僵硬 了),而是在每个时钟的到来,物体由能量头带动(如蛇头),每个点的方向都 向下一个 点传播,然后自己向新的方向走动一步,走动后,下一个点由于得到了上一 个点的方向并 同样地运动一步,所以,他会马上填补上一个点的地方,如此类推. 说的好象没说,看不懂没关系,因为实际的算法可以简化(傻瓜才会一个个 点来走的), 实际上在设计贪吃蛇的时候,只需要把蛇尾巴的那个点阵去掉,然后在蛇头 的新方向放一 个点阵就是了.期间需要记录下每个蛇身的固定点阵的位置,并且在每个运 动时刻过后刷 新一次每个点的位置. 4.显示接口 我们用的一般是点阵式 LCD,就是一大片点点,128*64,132*64,240*128等 等等等啦,这 些又叫条屏,就是一写就写一条――8个点(有的也提供写一个点的功能,但 贵,至少我没 有),那么如果你只想写一个点怎么办 那就得先把这个点所在的条读出来, 然后通过 与,或,的运算后,再放回到 LCD上,这时候就要涉及到一个读 LCD的问题 了,有的 LCD提供 读的功能,你写过什么在上面他记的很清楚(就好象老丁实验板上的 LCD), 但有的便宜货 就不行了,那么我们怎么办 没关系,你在内存中提取出一片空间,虚拟一个 LCD出来,每 次写在真实 LCD上面的时候,也同时写到内存的哪个虚拟 LCD上,那么你 要读出 LCD的值的时 候实际就是读出虚拟LD上的数据,然后与或后,再重新写到LCD上,记得也 要写到虚拟 LCD 上哦.你可以把这片缓冲叫做显存(COOL吧 ) 5.流程 这是成功设计游戏的灵魂,你在设计游戏之前必须能正确构思到一个基本 模型出来.这个 基本是菜鸟和虾米的一个区别,有了构思,其他的其实都是时间问题了. 以贪吃蛇为例,我们需要有这样的基本思路 普通手机上的那种) 蛇运动处理,吃到食物的处理,放新食物的处理,死亡的处理. 以上是基本的思路,至于那些记录分数,音乐效果,玩到一定分数会自动加 速度等不是游 戏的必须,可以在后期处理! 分析下来: 运动:根据用户输入按键进行柔体传动. 吃到食物:置没有食物标志了,蛇长大一个点阵. 放新食物:判断食物标志,如果没有食物,就要放食物,判断放的食物是否和 蛇身重叠, 重叠了要重放. 死亡处理:判断是否撞中自己或撞墙. 这就是基本要做的东西,实际上就是程序要做的东西,那么把上面的东西连 成一个流程是 怎样的呢 我以文字表达: 蛇向一个固定方向进行柔体传动,没个运动时钟到达要做:1.判断食物标志, 没有食物了 就放一个,放的时候判断,不能和蛇身重叠 2.得到用户按键值,蛇走一步,并 判断是否 撞死了,没撞死,再判断是否吃到东西了,没有吃到,就等下一个运动时钟,吃 了 就增 长一点.置一个没有食物的标志.然后等待下一个时刻的来临. 呵呵,其实程序就是这么简单,基本设计只有 LCD部分和按键部分是和单 片机有关的,其他 都是程序思维和算法.对于菜鸟来说,难度在于思维,而不是单片机. 说了屁话一堆,还得放上个能玩的,这里我介绍我的贪吃蛇程序,在丁丁的 DX实验板上跑 的,很久以前就写的了,老丁也玩过,基本和手机上的那个区别不是很大. 程序注意:这是在 DX51板子上跑的程序,有些函数部分采用了丁丁写的底 层:例如键盘 扫描,汉字显示,LCD显示等,为了保障老丁的利益,我没有完全给出所有的 底层部分,其 实他们和贪吃蛇本身没有太大关系.贪婪者别以为拿来就用,我只希望大家 用来交流学 习.其实改改就能玩的了. 注释应该很详尽,有不懂自己想啦. 还有,我有点反感有些人公布程序了,但却把很多注释去掉,这个不知道是 什么心态 呢 希望大家能大方点,要给,就要给最好的!! //////////////////////////////////////////////////////////// /*snake_flag是游戏标志,第一位是跑动标志,在定时器中断上设置,下面 程序没有定时 器中断函数,因为定时器函数在丁板上是给很多个程序共用的,函数根据标 志判断当前是 为那个进程服务*/ //贪吃蛇游戏程序,屏左半部用于游戏活动,右半部为分数显示 //游戏屏为 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 15 #define lcd_no_read 1 //编译选项,把这项屏蔽掉就 采用 LCD读出方式,否则采用显存形式 uchar snake_flag, //蛇头标志 7 6 5 4 3 2 1 0 // 上 下 左 右 x gameover food run snake_len, //蛇身长度 snake_food; //食物位置,高 4位 Y,低 4位 x uchar xdata snake_body[256]; //蛇身每个部分的数据 // 7 6 5 4 3 2 1 0 // 高 4位 Y方向 低 4位 X方向 #ifdef lcd_no_read uchar xdata lcd_buf[8][64];//lcd缓冲,用于记录LCD内部的点阵,可以理 解为显存 //当 LCD无读出功能时,就要采用显示缓冲.本 LCD为可读,一般不用这个 功能 //缓冲只记录蛇身活动的部分,即 LCD左半屏 #endif /****************************************************** * 游戏 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) { 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<>4)|0x10; LCMC=0xb0+y/2; //设置 Y位置 LCMC=0xe0; if(y%2)//行的下半部 { for(i=0;i>1][(x<>1][(x<<2)+i]=tmp; //把新数据写回缓冲 #else tmp=LCMD;tmp=LCMD; //读 LCD的,要求连读 2次 LCMD=tmp&0x0f; #endif } } else //行的上半部,下同 { for(i=0;i>1][(x<>1][(xwhile(y>MAX_GAME_Y)y-=(MAX_GAME_Y+1); lcd_x=x<>4)|0x10; LCMC=0xb0+y/2; //设置 Y位置 LCMC=0xe0; if(y%2)//行的下半部 { for(i=0;i>1][(x<>1][(x<<2)+i]=tmp; #else tmp=LCMD;tmp=LCMD; LCMD=tmp|0xf0; #endif } } else { for(i=0;i>1][(x<>1][(xwhile(y>MAX_GAME_Y)y-=(MAX_GAME_Y+1); lcd_x=x<>4)|0x10; LCMC=0xb0+y/2; //设置 Y位置 LCMC=0xe0; if(y%2) { for(i=0;i>1][(x<>1][(x<<2)+i]=tmp; #else tmp=LCMD;tmp=LCMD; LCMD=tmp|0x50; #endif } } else { for(i=0;i>1][(x<>1][(x<<2)+i]=tmp; #else tmp=LCMD;tmp=LCMD; LCMD=tmp|0x05; #endif } } LCMC=0xee; } /************************************************************ * * 游戏算法部分(8*8LCD) * **************************************************************/ //函数名 game_init() //功能:游戏开始的时候初始化画面的,这里只是简单地把 132*64LCD用一 条中间线划分开来 //注意事项:暂时在中间画条线用来划分游戏空间 //使用方式:内部调用, void game_init() { uchar i; uchar xdata *da; for(da=0;da<0x8000;da++)//清空 xDATA, *da=0x0; cls(9); //丁丁的清屏函数 initlcd(); for(i=0;i>4)|0x10; //线在 x=64, LCMC=0xb0+i; //y=(0-15)的地方 LCMC=0xe0; //把 LCD划分,左边用来游戏 LCMD=0xff; } LCMC=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,0); lcdstring("分数为:\r\n"); 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) { uchar ch; setcursor(8,0); lcdstring("分数为:\r\n"); setcursor(8,2); lcddigit(snake_len-2); if(mode)//gameover中显示 { setcursor(8,0); lcddigit(snake_len-2); lcdstring(" 分."); setcursor(8,2); lcdstring("C退出"); setcursor(8,4); 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() { uchar tmp_head_x,tmp_head_y; 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);//灭蛇尾巴 for(i=snake_len;i>0;i--) //柔体传动 snake_body=snake_body[i-1]; snake_body[0]=( tmp_head_y<0;i--) snake_body=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>3)*4+seed0+TL0);//随机数的生成,其实乱做就可以了 seed1=(snake_body[0]>>4)+seed0; snake_food=(seed1+seed0*3); food: for(i=0;i>4);//放食物 snake_flag|=0x02;//置有食物标志 } // 函数名:定时器 1初始化程序 // 晶振 22.1184,定时时间 35MS void timer0_init(void) { TMOD=0x1; TH0=0x0; TL0=0x0; } //函数名:snake_game //功能:整个游戏的主要函数 //注意事项:游戏利用了定时器产生蛇的运行速速度 // 调用前应该先初始化定时器 //使用情况:外部调用 // extern uchar time_service; //这个东东是使定时器公用的. void snake_game() { uchar tmp_snake_flag,ch; cls(8); time_service=1; lcdstring("贪吃蛇游戏\r\nC退出,任意键进入\r\n游戏说明:\r\n2:上 5:下 4: 左 6:右"); while((ch=getkey(1000))==0); if(ch=='C') return; ch=0; begin_game: game_init(); //一堆初始化 snake_init(); 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 return; //游戏结束 } } } 对于 DX学习板,有两个版本的,区别在于 LCD的大小,以下分别是大屏和 小屏的程序,其实大致是一样的,只是改变了一下蛇的活动空间而已 另外当然少不了的就是时钟节拍程序,他决定了蛇蛇的速度,这里用定时器 实现节拍的: 由于 DX板功能众多,定时器需要复用,以下的定时器程序根据标志 time_service来判断当前的定时器是为谁服务的: //时钟复用部分.timer0 #include "reg51.h" #include "usb.h" #define uchar unsigned char #define uint unsigned int //time 为公共使用 //1.贪吃蛇, //2.音乐播放 //3.USB接口 uchar data time_service; //0.没用 //1.贪吃蛇 extern uchar xdata snake_flag; uchar xdata set_time; //时钟分频暂存器 #define TIME_RUN 7 //2.音乐播放 extern uchar tl0_f; //此装入值在music.c中定义 extern uchar th0_f; sbit MIO=P1^7; //IO口往返变化产生不同声调 //3.USB接口 extern xdata EPPFLAGS bEPPflags; extern xdata CONTROL_XFER ControlData; //定时器 0中断 void timer0(void) interrupt 1 { switch(time_service) { case 0:break; case 1: //贪吃蛇 TH0=0x0; TL0=0x0; set_time++; if(set_time==TIME_RUN) { set_time=0; snake_flag|=0x01;//置跑标志 } break; case 2: //音乐用 TL0=tl0_f;TH0=th0_f; //调入新的定时值 MIO=~MIO; //取反音乐输出 IO break; case 3: //usb用 TH0=0x55; TL0=0x55; bEPPflags.bits.timer = 1; //定时时间到,标志用于检查灯和按键 break; default:break; } }
/
本文档为【贪吃蛇】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索