1、单片机C语言贪吃蛇程序可下【单片机游戏设计及贪吃蛇程序】管理提醒: 本帖被 icneo 执行取消置顶操作(2009-01-02) 【单片机游戏设计及贪吃蛇程序】 单片机游戏设计 1。概念 对于大部分单片机+LCD的游戏设计,基本采用前后台方式,就是一个台中断,一个台循环(哪个前哪个后忘了),LCD部分基本是以固定点阵形式设计,什么叫固定点阵?首先先明确,我们设计的游戏不是什么魔兽争霸或CS,而是黑白形式的固定点阵游戏,例如常见著名游戏贪吃蛇或俄罗斯方块。他们的每个点都是预先就固定下来的,而且是比较大的点,这类专门的游戏机玻璃是经过厂家开模出来的,有固定的COM,SEG线,然后接到专门的单片机上
2、,例如常用的6502指令集合的单片机,呵呵,我以前就用6502设计过一个。 对于业余设计的游戏,我们一般用如128*64的LCD来显示,那么我们设计的时候首先应该把这个128*64的LCD分块,也就是分出固定点阵出来。LCD的基本点阵是128*64,就是¥#¥总之就是好多个点啦,但我们事实上不一定要运算这么多个点,除非你做的游戏很有看头。例如你只用左边64*64的地方来做贪吃蛇,那么你打算你的贪吃蛇的活动空间是多少呢?如果是8*8个点的话,算一下就是每个点64/8,64/8,也就是8*8个基本点阵,不过想好玩一点,当然就是要有16*16个点的活动空间啦,那么每个固定点阵就要占4*4的基本点阵了
3、。要注意,这些4*4的东西在64*64LCD上共16*16个,每个都要用来独立运算。 2。时钟 这个其实是游戏的速度,对于一般的弱智类游戏机,他也代表了难度,物体在每个时钟到达的时候就传动一次,例如俄罗斯方块没个时刻向下跑一层。赛车游戏每个时刻想前走一步。一般这类时钟的时间在0.X秒到1秒之间,物体有规律地匀速运动,让人看到感觉是连动。 3。运动 在这里,我先介绍两种比较普遍的弱智游戏机的物体运动规则:柔体传动,刚体传动。 刚体传动 代表作是俄罗斯方块,所谓刚体,就是硬硬的一个东东,运动的时候也不怎么旋转(注意,俄罗斯方块是会旋转,但其实他是没有经过算法的旋转,纯提取数组的方式,也就是把一个放
4、块做成4个模式的点阵结构,其实就是4个方向,呵呵)对于刚体的传动,在每个时钟到达的时候向一个方向(很可能是用户输入的)运动一个固定点阵。如果以坐标来表达,就是物体的所有基本点阵同时向一个方向(X或Y)移动一个单位。 柔体传动 代表作是贪吃蛇,贪吃蛇跑动的时候并不是整条蛇向一个方向动的(呵呵,蛇蛇身体僵硬了),而是在每个时钟的到来,物体由能量头带动(如蛇头),每个点的方向都向下一个点传播,然后自己向新的方向走动一步,走动后,下一个点由于得到了上一个点的方向并同样地运动一步,所以,他会马上填补上一个点的地方,如此类推。说的好象没说,看不懂没关系,因为实际的算法可以简化(傻瓜才会一个个点来走的),实
5、际上在设计贪吃蛇的时候,只需要把蛇尾巴的那个点阵去掉,然后在蛇头的新方向放一个点阵就是了。期间需要记录下每个蛇身的固定点阵的位置,并且在每个运动时刻过后刷新一次每个点的位置。 4。显示接口 我们用的一般是点阵式LCD,就是一大片点点,128*64,132*64,240*128等等等等啦,这些又叫条屏,就是一写就写一条8个点(有的也提供写一个点的功能,但贵,至少我没有),那么如果你只想写一个点怎么办?那就得先把这个点所在的条读出来,然后通过 与,或,的运算后,再放回到LCD上,这时候就要涉及到一个读LCD的问题了,有的LCD提供读的功能,你写过什么在上面他记的很清楚(就好象老丁实验板上的LCD)
6、,但有的便宜货就不行了,那么我们怎么办?没关系,你在内存中提取出一片空间,虚拟一个LCD出来,每次写在真实LCD上面的时候,也同时写到内存的哪个虚拟LCD上,那么你要读出LCD的值的时候实际就是读出虚拟LD上的数据,然后与或后,再重新写到LCD上,记得也要写到虚拟LCD上哦。你可以把这片缓冲叫做显存(COOL吧?) 5。流程 这是成功设计游戏的灵魂,你在设计游戏之前必须能正确构思到一个基本模型出来。这个基本是菜鸟和虾米的一个区别,有了构思,其他的其实都是时间问题了。以贪吃蛇为例,我们需要有这样的基本思路:(普通手机上的那种) 蛇运动处理,吃到食物的处理,放新食物的处理,死亡的处理。 以上是基本
7、的思路,至于那些记录分数,音乐效果,玩到一定分数会自动加速度等不是游戏的必须,可以在后期处理! 分析下来: 运动:根据用户输入按键进行柔体传动。 吃到食物:置没有食物标志了,蛇长大一个点阵。 放新食物:判断食物标志,如果没有食物,就要放食物,判断放的食物是否和蛇身重叠, 重叠了要重放。 死亡处理:判断是否撞中自己或撞墙。 这就是基本要做的东西,实际上就是程序要做的东西,那么把上面的东西连成一个流程是怎样的呢?我以文字表达: 蛇向一个固定方向进行柔体传动,没个运动时钟到达要做:1。判断食物标志,没有食物了就放一个,放的时候判断,不能和蛇身重叠 2。得到用户按键值,蛇走一步,并判断是否撞死了,没撞
8、死,再判断是否吃到东西了,没有吃到,就等下一个运动时钟,吃了?就增长一点。置一个没有食物的标志。然后等待下一个时刻的来临。 呵呵,其实程序就是这么简单,基本设计只有LCD部分和按键部分是和单片机有关的,其他都是程序思维和算法。对于菜鸟来说,难度在于思维,而不是单片机。说了屁话一堆,还得放上个能玩的,这里我介绍我的贪吃蛇程序,在实验板上跑的,很久以前就写的了,有人也玩过。放到现在才拿出来希望大家不要介意:) 程序注意:这是在丁丁的板子上跑的程序,有些函数部分采用了别人写的底层:例如键盘扫描,汉字显示,LCD显示等,为了保障老丁的利益,我没有完全给出所有的底层部分,其实他们和贪吃蛇本身没有太大关系
9、。贪婪者别以为拿来就用,我只希望大家用来交流学习。其实改改就能玩的了。 注释应该很详尽,有不懂自己想啦。 还有,我有点反感有些人公布程序了,但却把很多注释去掉,这个不知道是什么心态呢?希望大家能大方点,要给,就要给最好的!【单片机游戏设计及贪吃蛇程序】程序:/*snake_flag是游戏标志,第一位是跑动标志,在定时器中断上设置,下面程序没有定时器中断函数,因为定时器函数在丁板上是给很多个程序共用的,函数根据标志判断当前是为那个进程服务*/贪吃蛇游戏程序,屏左半部用于游戏活动,右半部为分数显示/游戏屏为16*16游戏点阵,可容纳蛇身块数256。每个游戏点阵又由4*4个LCD基本点阵组成/蛇行标
10、志在定时器上置位,这里为游戏的主体部分。#define uchar unsigned char#define uintunsigned int#define ulong unsigned long#define LCMD XBYTE0xAfff / 液晶数据口#define LCMC XBYTE0xAbff / 液晶命令口#define TIME_RUN 10 /定时器分品系数#include study.h#include reg51.h#include absacc.h#include intrins.h/游戏部分/x,y最大极限#define MAX_GAME_X 15#define M
11、AX_GAME_Y 15#define lcd_no_read 1 /编译选项,把这项屏蔽掉就采用LCD读出方式,否则采用显存形式uchar snake_flag, /蛇头标志765432 1 0 /上 下 左 右 xgameoverfoodrunsnake_len, /蛇身长度snake_food; /食物位置,高4位Y,低4位xuchar xdata snake_body256; /蛇身每个部分的数据/76543210/高4位Y方向低4位X方向 #ifdef lcd_no_readuchar xdata lcd_buf864;/lcd缓冲,用于记录LCD内部的点阵,可以理解为显存/当LCD
12、无读出功能时,就要采用显示缓冲。本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(xMAX_GAME_X)x-=(MAX_GAME_X+1); /这个是
13、写程序习惯的保护措施,预防输入范围过大while(yMAX_GAME_Y)y-=(MAX_GAME_Y+1);lcd_x=x4)|0x10;LCMC=0xb0+y/2; /设置Y位置LCMC=0xe0;if(y%2)/行的下半部for(i=0;i1(x1(x2)+i=tmp; /把新数据写回缓冲#elsetmp=LCMD;tmp=LCMD; /读LCD的方法,要求连读2次LCMD=tmp&0x0f;#endifelse /行的上半部,下同for(i=0;i1(x1(xMAX_GAME_X)x-=(MAX_GAME_X+1);while(yMAX_GAME_Y)y-=(MAX_GAME_Y+1)
14、;lcd_x=x4)|0x10;LCMC=0xb0+y/2; /设置Y位置LCMC=0xe0;if(y%2)/行的下半部for(i=0;i1(x1(x2)+i=tmp; #elsetmp=LCMD;tmp=LCMD;LCMD=tmp|0xf0;#endifelsefor(i=0;i1(x1(xMAX_GAME_X)x-=(MAX_GAME_X+1);while(yMAX_GAME_Y)y-=(MAX_GAME_Y+1);lcd_x=x4)|0x10;LCMC=0xb0+y/2; /设置Y位置LCMC=0xe0;if(y%2)for(i=0;i1(x1(x2)+i=tmp; #elsetmp=L
15、CMD;tmp=LCMD;LCMD=tmp|0x50;#endifelsefor(i=0;i1(x1(x2)+i=tmp;#elsetmp=LCMD;tmp=LCMD;LCMD=tmp|0x05;#endifLCMC=0xee;/* 游戏算法部分(8*8LCD)*/函数名 game_init()/功能:游戏开始的时候初始化画面的,这里只是简单地把132*64LCD用一条中间线划分开来/注意事项:暂时在中间画条线用来划分游戏空间/使用方式:内部调用,void game_init()uchar i;uchar xdata *da;for(da=0;da0x8000;da+)/清空xDATA,*da
16、=0x0;cls(9); /丁丁的清屏函数initlcd();for(i=0;i4)|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个身.向
17、右跑snake_body0=0x02; /装入射身数据snake_body1=0x01;snake_body2=0x00;/一开始游戏时的文字部分setcursor(8,0);lcdstring(分数为:rn);setcursor(8,2);lcddigit(snake_len-2);/函数名:show_mark /功能:显示当前分数,暂时以蛇身个数为分数 /参数说明:0,和非0, 0代表游戏中的显示,!0代表挂了的显示 /注意事项:调用到LCD.c显示函数,并需要汉字库的支持. / 返回值在GAMEOVER时候有效,返回0退出游戏,1从新游 戏 /使用情况:snake_run()在蛇吃到食物
18、的时候调用,在GAMEOVER后调用 uchar show_mark(uchar mode) uchar ch; setcursor(8,0); lcdstring(分数为:rn); 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( (
19、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
20、_x=snake_body0 &0x0f; tmp_head_y=(snake_body0 4); if(tmp_head_y=0) snake_flag|=0x04;/这个代表撞墙了,就置GAMEOVER标志,下同 else tmp_head_y-; break; case 0x40:/向下走 y+ tmp_head_x=snake_body0 &0x0f; tmp_head_y=(snake_body0 4); if(tmp_head_y=MAX_GAME_Y) snake_flag|=0x04; else tmp_head_y+; break; case 0x20:/向左走 x- tmp
21、_head_y=snake_body0 4; tmp_head_x=snake_body0 &0x0f; if(tmp_head_x=0) snake_flag|=0x04; else tmp_head_x-; break; case 0x10:/向右走 x+ tmp_head_y=snake_body0 4; tmp_head_x=snake_body0 &0x0f; if(tmp_head_x=MAX_GAME_X) snake_flag|=0x04; else tmp_head_x+; break; default:break; if(!(snake_flag&0x04) /如 果在之前
22、没有撞墙,就可以进行下一步判断 /以下是得到食物的判断。 if(snake_food!=( (tmp_head_y4);/灭蛇尾巴 for(i=snake_len;i0;i-) /柔体传动 snake_body=snake_bodyi-1; snake_body0=( tmp_head_y0;i-) snake_body=snake_bodyi-1; snake_body0=( tmp_head_y4 ) + tmp_head_x; /新蛇头 snake_len+;/长度增加1 snake_flag&=0x02;/清食物标志 show_mark(0);/显示分数 fill_game_dot(tmp_head_x,tmp_head_y);/显示新蛇 头 for(i=1;isnake_len+1;i+) /判断是否撞中自己 if(snake_body0=snake_body) snake_flag|=0x04; /撞中了就置GAMEOVER标志 break; /函数名:set_food /功能:放食物 /注意事项:这个函数在被调用前会先判断是否需要放食物, / 这里用自己编写的随机数来产生食物,随机数和蛇身位 置,定时器有关 / 每次放食物的时候必须先判断是否和蛇身重叠了,重叠 了要重新放 / 这里设定了
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1