童年时光,被遗忘的俄罗斯方块

来源:互联网 发布:前苏联挖到地狱知乎 编辑:程序博客网 时间:2024/05/21 17:39

        俄罗斯方块,简单易学,编这个程序也比贪吃蛇相对简单,多花心思,4天的开发周期也足够了,这个程序是近期写的,所以程序代码也比之前的贪吃蛇简练些……

 

总模块:

 

Main函数贴图:

游戏编程总少不了GUI,这里运用了LCD12864,顺便把LCD12864H文件的源程序也贴出来吧……

/**************************************************应用函数**************************************************///---------------------------------初始化函数---------------------------------//void init_12864(void);//初始化LCD12864//---------------------------------写入字符和数字函数---------------------------------//void write_string(unsigned char row,unsigned char col,unsigned char *str);//写入字符串函数,row代表行,col代表列,*str代表要写入的字符串的首地址void write_num(unsigned char row, unsigned char col, unsigned int num);//写入数字函数,row代表行,col代表列,num代表要写入的字符串的首地址//---------------------------------画图函数---------------------------------//void PhotoWrite(unsigned char code *picture);//写入图片void PhotoRegion(unsigned char X, unsigned char Y, unsigned char Color);//显示任意区域块(X:1~16、Y:1~8;Color代表填充色类型,1点亮,0擦除)/**************反白或其他操作显示***********************************/void PhotoArea(unsigned char X, unsigned char Y, unsigned char Width, unsigned char Color);/*显示任意区域(反白或其他操作),X取值为0~7,Y代表0~3行,Width代表反白格子数,Color为0 代表擦除(即清空),为1 代表点亮,为2 代表反白(即空白的点亮,点亮的擦除)*/void PhotoWhite(unsigned char Y);/*反白显示行,Y取值为1~4*/void PictureMoveRitght(unsigned char *Picture1, unsigned char *Picture2);/*图片右移*/

 

相应的LCD12864C文件如下:

/**************************************************应用函数**************************************************///初始化LCD12864void init_12864(void){DelayMs(20);write_com(0x30);//8位数据格式,基本指令显示  DelayMs(20);write_com(0x30);//8位数据格式,基本指令显示DelayMs(20);write_com(0x0C);//开显示,关闭光标DelayMs(20);write_com(0x01);//清屏指令DelayMs(20);write_com(0x06);//设置显示点:指针自加1RS=0;RW=0;}/*****************写入字符和数字函数*********************************/void write_string(unsigned char row,unsigned char col,unsigned char *str)//写入字符串函数,row代表行,col代表列,*str代表要写入的字符串的首地址{unsigned char *pstr;pstr=str;if(row==1)write_com(0x80+col-1);//设定DDRAM地址到地址计数器AC(DDRAM地址:0x80+col-1)else if(row==2)write_com(0x90+col-1);else if(row==3)write_com(0x88+col-1);else if(row==4)write_com(0x98+col-1);while(*pstr!='\0'){write_dat(*pstr);pstr++;}}void write_num(unsigned char row,unsigned char col,unsigned int num)//写入数字函数,row代表行,col代表列,num代表要写入的字符串的首地址{int BiteLength=0;int Wei[16];int NumTmp;int i;NumTmp=num;if(num!=0){for(i=0;i<10,NumTmp>0;i++){Wei[i]=NumTmp%10;NumTmp/=10;BiteLength++;}BiteLength--;if(row==1)write_com(0x80+col-1);//设定DDRAM地址到地址计数器AC(DDRAM地址:0x80+col-1)else if(row==2)write_com(0x90+col-1);else if(row==3)write_com(0x88+col-1);else if(row==4)write_com(0x98+col-1);for(i=BiteLength;i>=0;i--){write_dat(Wei[i]+'0');}}else if(num==0){if(row==1)write_com(0x80+col-1);//设定DDRAM地址到地址计数器AC(DDRAM地址:0x80+col-1)else if(row==2)write_com(0x90+col-1);else if(row==3)write_com(0x88+col-1);else if(row==4)write_com(0x98+col-1);write_dat('0');}}//---------------------------------画图函数---------------------------------////写入图片void PhotoWrite(unsigned char code *picture){unsigned char i, j, k;write_com(0x36);//打开扩充指令,同时开启绘图显示for(i = 0; i < 2; i++)//分上下两屏写{for(j = 0; j < 32; j++){write_com(0x80 + j);//写Y坐标if(i==0)//写X坐标write_com(0x80);elsewrite_com(0x88);for(k = 0; k < 16; k++)//写一整行数据write_dat(*picture++);}}write_com(0x30);//8位数据格式,基本指令显示}//显示任意区域块,X代表x轴起始点,Y代表y轴起始点;Color代表填充色类型,1代表点亮,0代表擦除void PhotoRegion(unsigned char X, unsigned char Y, unsigned char Color){unsigned char i, j;//i 用于写0~63行,j 用于写0~16列,每列一个字节(8位)unsigned char ColorTemp;if(Color==1)ColorTemp = 0xFF;else if(Color==0)ColorTemp = 0x00;write_com(0x36);//打开扩充指令,同时开启绘图显示for(i = (Y-1)*8; i < Y*8; i++){if(i > 31){write_com(0x80+i-32);//先写入水平坐标值write_com(0x88);//写入垂直坐标值}else {write_com(0x80+i);  //先写入水平坐标值write_com(0x80);//写入垂直坐标值}for(j = 0; j < X-1; j++)read_dat();for(; j < X; j++)//点亮各个点write_dat(ColorTemp);DelayMs(1);}write_com(0x30);//8位数据格式,基本指令显示}/**************反白显示***********************************//*显示任意区域(反白或其他操作),X取值为0~7,Y代表0~3行,Width代表反白格子数,Color为0 代表擦除(即清空),为1 代表点亮,为2 代表反白(即空白的点亮,点亮的擦除)*/void PhotoArea(unsigned char X, unsigned char Y, unsigned char Width, unsigned char Color){unsigned char i, j, flag=0x00;unsigned char read_h, read_l;//read_h代表横坐标, read_l代表纵坐标if(Y > 1){flag = 0x08;Y = Y-2;}write_com(0x34);//开启扩充指令,同时关闭绘图显示for(i = 0; i < 16; i++)//0~63共64个,64/4=16{for(j = 0; j < Width; j++){write_com(0x80 + (Y<<4) + i);//写入行坐标地址write_com(0x80 + flag + X + j);//写入纵坐标地址read_dat();//指令:读;每读/写一个字节(8位)指针自加1if(Color==0)//0 代表擦除(即清空){read_h = (0x00 & read_dat());read_l = (0x00 & read_dat());}else if(Color==1)//1 代表点亮{ read_h = (0xFF | read_dat());read_l = (0xFF | read_dat());}else if(Color==2)//2 代表反白(即空白的点亮,点亮的擦除){ read_h = (0xFF ^ read_dat());read_l = (0xFF ^ read_dat());}write_com(0x80 + (Y<<4) + i);//写入行坐标地址write_com(0x80 + flag + X + j);//写入纵坐标地址write_dat(read_h);//向横坐标写入数据write_dat(read_l);//向纵坐标写入数据}_nop_();_nop_();}write_com(0x36);//开启绘图write_com(0x30);//基本指令显示,8位数据格式}/*反白显示行,Y取值为1~4*/void PhotoWhite(unsigned char Y){PhotoArea(3, Y-1, 5, 2);}/*图片右移*/void PictureMoveRitght(unsigned char *Picture1, unsigned char *Picture2){unsigned char i, j;//i 用于写0~63行,j 用于写0~16列,每列一个字节(8位)unsigned char m;//用于记录已移动的格子数,总共16格unsigned char n;//n 用于移动整副图像,n=0-16则从图的头走到图的尾,刚好两幅图对调unsigned char *Picture10, *Picture20;//用于记录图1、图2的地址 m = 0;for(n = 0; n < 16; n++)//n 用于移动整副图像,n=0-16则从图的头走到图的尾,刚好两幅图对调{write_com(0x34);//打开扩充指令,同时关闭绘图显示Picture10 = Picture1;//记录图片地址Picture20 = Picture2 + (16-m);for(i = 0; i < 32; i++)//写上半屏{write_com(0x80 + i);//先写入水平坐标值(把LCD12864倒过来看,原来的高就是水平轴)write_com(0x80);//再写入垂直坐标值for(j = 0; j < m; j++)//写入图2 的数据write_dat(*Picture20++);Picture20 += (16-m);for(j = 0; j < 16-m; j++)//写入图1 的数据write_dat(*Picture10++);Picture10 += m;DelayMs(1);}for(i = 0; i < 32; i++)//写下半屏{write_com(0x80 + i);//先写入水平坐标值(把LCD12864倒过来看,原来的高就是水平轴)write_com(0x88);//再写入垂直坐标值for(j = 0; j < m; j++)//写入图2 的数据write_dat(*Picture20++);Picture20 += (16-m);for(j = 0; j < 16-m; j++)//写入图1 的数据write_dat(*Picture10++);Picture10 += m;DelayMs(1);}write_com(0x36);//开启绘图DelayMs(250);//延迟图片刷新速度,即间接反映图片移动速度m++;}}


好了,我想现在最主要的还是集中在俄罗斯方块游戏编程中最主要的核心:构思

这里我把sequare模块的H文件(头文件)贴出来吧,里面包含了俄罗斯方块游戏编程中主要的算法……

#ifndef __SQUARE_H__#define __SQUARE_H__#include<stdlib.h>#include"lcd12864.h"#include"keyboard.h"//方块体结构体typedef struct{unsigned char lable;//方块体标签unsigned char element[4][2];//方块体各方块行值列值unsigned char ShapeNumber;//方块体的形状数目(即方块体可变型种类)}square;extern unsigned char xdata photo[16][8];//整体画面赋予格子行值(0~15)、列值(0~7)extern unsigned char xdata GameLable;//游戏标签,1进行当中,0游戏终止extern unsigned int  xdata GamePoint;//游戏标签,记录游戏分数extern unsigned int  xdata GamePointRanking[3];//游戏标签,记录排名分数extern unsigned char xdata GameSpeed;//游戏标签,记录游戏速度,即难度extern unsigned char xdata MoveAble;//移动标签,1可移动,0禁止移动extern unsigned char xdata MoveRow;//移动标签,即当前方块垂直移动的格子数extern unsigned char xdata MoveCol;//移动标签,即当前方块水平移动的格子数extern unsigned char xdata NewLable;//生成标签,即当前方块是否更新,1代表需要更新,0代表不需要//方块体各方块初始行值、列值//-------------------------------------------------------------//extern unsigned char xdata A_1[4][2];extern unsigned char xdata A_2[4][2];extern unsigned char xdata A_3[4][2];extern unsigned char xdata A_4[4][2];//-------------------------------------------------------------//extern unsigned char xdata B_1[4][2];extern unsigned char xdata B_2[4][2];extern unsigned char xdata B_3[4][2];extern unsigned char xdata B_4[4][2];//-------------------------------------------------------------//extern unsigned char xdata C_1[4][2];extern unsigned char xdata C_2[4][2];//-------------------------------------------------------------//extern unsigned char xdata D_1[4][2];extern unsigned char xdata D_2[4][2];//-------------------------------------------------------------//extern unsigned char xdata E_1[4][2];/*-------------------------------函数-------------------------------*/void InitGame(void);//游戏初始化void RefreshSquare(square *S, unsigned char Row, unsigned char Col);//刷新方块函数(方块S 各个元素的行值加上Row个单位,列值加上Col个单位后,整个行值、列值重新赋给方块S ,即刷新方块S 的元素值)void InitSquare(square *S);//初始化方块体void ConversionSquare(square *S, unsigned char direction);//方块体转换(需写入方向direction)void MoveSquare(square *S, unsigned char direction);//方块体左移右移(需写入方向direction)void MoveDownSquare(square *S);//方块体下移void ClearSquare(void);//检测方块体是否整行该消失void RankingSquare(unsigned int point);//记录游戏排名分数/*-------------------------------方块体自动移动-------------------------------*/void Init_Timer0(void);//定时器0#endif

 

这个文件的sequare模块的C文件如下:

#include"square.h"unsigned char xdata photo[16][8];//整体画面赋予格子行值(0~15)、列值(0~7)unsigned char xdata GameLable;//游戏标签,0进行当中,1游戏终止unsigned int  xdata GamePoint;//游戏标签,记录游戏分数unsigned int  xdata GamePointRanking[3]={0,0,0};//游戏标签,记录排名分数unsigned char xdata GameSpeed=3;//游戏标签,记录游戏速度,即难度unsigned char xdata MoveAble;//移动标签,1可移动,0禁止移动unsigned char xdata MoveRow;//移动标签,即当前方块垂直移动的格子数unsigned char xdata MoveCol;//移动标签,即当前方块水平移动的格子数unsigned char xdata NewLable;//生成标签,即当前方块是否更新,1代表需要更新,0代表不需要//方块体各方块初始行值、列值//-------------------------------------------------------------//unsigned char xdata A_1[4][2] = {{1, 4}, {0, 4}, {1, 5}, {1, 3}};unsigned char xdata A_2[4][2] = {{1, 4}, {0, 4}, {1, 3}, {2, 4}};unsigned char xdata A_3[4][2] = {{1, 4}, {1, 5}, {1, 3}, {2, 4}};unsigned char xdata A_4[4][2] = {{1, 4}, {0, 4}, {1, 5}, {2, 4}};//-------------------------------------------------------------//unsigned char xdata B_1[4][2] = {{1, 4}, {0, 4}, {2, 4}, {2, 3}};unsigned char xdata B_2[4][2] = {{1, 4}, {1, 5}, {1, 3}, {2, 5}};unsigned char xdata B_3[4][2] = {{1, 4}, {0, 5}, {0, 4}, {2, 4}};unsigned char xdata B_4[4][2] = {{1, 4}, {0, 3}, {1, 5}, {1, 3}};//-------------------------------------------------------------//unsigned char xdata C_1[4][2] = {{1, 4}, {0, 4}, {0, 3}, {1, 5}};unsigned char xdata C_2[4][2] = {{1, 4}, {0, 5}, {1, 5}, {2, 4}};//-------------------------------------------------------------//unsigned char xdata D_1[4][2] = {{1, 4}, {0, 4}, {2, 4}, {3, 4}};unsigned char xdata D_2[4][2] = {{1, 4}, {1, 6}, {1, 5}, {1, 3}};//-------------------------------------------------------------//unsigned char xdata E_1[4][2] = {{1, 4}, {0, 5}, {0, 4}, {1, 5}};/*-------------------------------函数-------------------------------*///游戏初始化void InitGame(void){unsigned char i, j;for(i = 0; i < 16; i++)//整体画面赋予格子行值(0~15)、列值(0~7)for(j = 0; j < 8; j++) photo[i][j] = 0;GameLable=0;//游戏标签,0进行当中,1游戏终止GamePoint=0;//游戏标签,记录游戏分数MoveAble=0;//移动标签,1可移动,0禁止移动MoveRow=0;//移动标签,即当前方块垂直移动的格子数MoveCol=4;//移动标签,即当前方块水平移动的格子数NewLable=1;//生成标签,即当前方块是否更新,1代表需要更新,0代表不需要}//刷新方块函数(方块S 各个元素的行值加上Row个单位,列值加上Col个单位后,整个行值、列值重新赋给方块S ,即刷新方块S 的元素值)void RefreshSquare(square *S, unsigned char Row, unsigned char Col){unsigned char i;unsigned char *p;//指向数组的指针//匹配方块体类型if(S->lable==11)p = &A_1[0][0];else if(S->lable==12)p = &A_2[0][0];else if(S->lable==13)p = &A_3[0][0];else if(S->lable==14)p = &A_4[0][0];else if(S->lable==21)p = &B_1[0][0];else if(S->lable==22)p = &B_2[0][0];else if(S->lable==23)p = &B_3[0][0];else if(S->lable==24)p = &B_4[0][0];else if(S->lable==31)p = &C_1[0][0];else if(S->lable==32)p = &C_2[0][0];else if(S->lable==41)p = &D_1[0][0];else if(S->lable==42)p = &D_2[0][0];else if(S->lable==51)p = &E_1[0][0];//赋予元素行值、列值for(i = 0; i < 4; i++){S->element[i][0] = *(p+i*2) + Row;S->element[i][1] = *(p+i*2+1) + Col - 4; }}//初始化方块体(需写入方块体标签lable)void InitSquare(square *S){unsigned char i;unsigned char lable;//赋予lable随机数lable = rand()%5+1;//赋予ShampNumber值if(lable < 3)S->ShapeNumber = 4;else if(lable > 2 && lable < 5) S->ShapeNumber = 2;else if(lable==5)S->ShapeNumber = 1;//赋予lable值S->lable = lable*10 + rand()%S->ShapeNumber + 1;//匹配方块体类型并赋予方块体各元素行值、列值RefreshSquare(S, 0, 4);//初始化方块体绘图for(i = 0; i < 4; i++)PhotoRegion(S->element[i][0], S->element[i][1], 1);//判断初始方块体是否与已有方块体重合,重合则代表游戏失败,结束游戏for(i = 0; i < 4; i++)if(photo[ S->element[i][0]-1 ][ S->element[i][1]-1 ]==1)GameLable = 1;//Game Over!}//方块体转换(需写入方向direction)void ConversionSquare(square *S, unsigned char direction){unsigned char i;square STemp;//临时转换方块体if(direction==POSITIVE)//正反向(顺时针){if(S->lable%10==S->ShapeNumber)//方块体为本类型的最后一种形状STemp.lable = S->lable - (S->ShapeNumber-1); elseSTemp.lable = S->lable + 1;}else if(direction==OPPOSITE)//反方向(逆时针){if(S->lable%10==1)//方块体为本类型的第一种形状STemp.lable = S->lable+S->ShapeNumber-1; elseSTemp.lable = S->lable-1;}RefreshSquare(&STemp, MoveRow, MoveCol);//赋予临时方块体各元素的行值、列值for(i = 0; i < 4; i++)//判断临时转换的方块体是否与已有方块体重合或超出方框,若是则无法进行转换,直接终止函数进行if(photo[ STemp.element[i][0]-1 ][ STemp.element[i][1]-1 ]==1 || STemp.element[i][1] < 1 || STemp.element[i][1] > 8 || STemp.element[i][0] > 16)return;//直接终止函数进行for(i = 0; i < 4; i++)//清除原有方块体PhotoRegion(S->element[i][0], S->element[i][1], 0);S->lable = STemp.lable;//赋予转换的方块体的标签RefreshSquare(S, MoveRow, MoveCol);for(i = 0; i < 4; i++)//写入转换的方块体PhotoRegion(S->element[i][0], S->element[i][1], 1);}//方块体左移右移(需写入方向direction)void MoveSquare(square *S, unsigned char direction){unsigned char i, MoveColTemp;square STemp;//临时移动方块体if(direction==LEFT)//左方向MoveColTemp = MoveCol+1;else if(direction==RIGHT)//右方向MoveColTemp = MoveCol-1;RefreshSquare(&STemp, MoveRow, MoveColTemp);//赋予临时移动方块体各元素的行值、列值for(i = 0; i < 4; i++)//判断临时移动的方块体是否与已有方块体重合或超出方框,若是则无法移动,直接终止函数进行if(photo[ STemp.element[i][0]-1 ][ STemp.element[i][1]-1 ]==1 || STemp.element[i][1] < 1 || STemp.element[i][1] > 8 || STemp.element[i][0] > 16)return;//直接终止函数进行for(i = 0; i < 4; i++)//清除原有方块体PhotoRegion(S->element[i][0], S->element[i][1], 0);MoveCol = MoveColTemp;RefreshSquare(S, MoveRow, MoveCol);for(i = 0; i < 4; i++)//写入移动的方块体PhotoRegion(S->element[i][0], S->element[i][1], 1);}//方块体下移void MoveDownSquare(square *S){unsigned char i, MoveRowTemp;square STemp;//临时移动方块体MoveRowTemp = MoveRow+1;RefreshSquare(&STemp, MoveRowTemp, MoveCol);//赋予临时移动方块体各元素的行值、列值for(i = 0; i < 4; i++)//判断临时移动的方块体是否与已有方块体重合或超出方框,若是则游戏结束,直接终止函数进行if(photo[ STemp.element[i][0]-1 ][ STemp.element[i][1]-1 ]==1 || STemp.element[i][0] > 16){for(i = 0; i < 4; i++)//方块停止运动,赋予方块所处位置的点的值为1,锁定该区域,并记录分数photo[ S->element[i][0]-1 ][ S->element[i][1]-1 ]=1;NewLable = 1;//需要更新方块return;//直接终止函数进行}MoveRow = MoveRowTemp;for(i = 0; i < 4; i++)//清除原有方块体PhotoRegion(S->element[i][0], S->element[i][1], 0);RefreshSquare(S, MoveRow, MoveCol);for(i = 0; i < 4; i++)//写入移动的方块体PhotoRegion(S->element[i][0], S->element[i][1], 1);}//检测方块体是否整行该消失void ClearSquare(void){unsigned char i, j, k;unsigned char flag;for(i = 0; i < 16; i++){flag = i;for(j = 0; j < 8; j++)if(photo[i][j]==0)flag = 0;if(flag!=0){for(k = i; k > 0; k--)for(j = 0; j < 8; j++)photo[k][j] = photo[k-1][j];for(k = 0; k < 8; k++)photo[0][k] = 0;for(k = 0; k <= i; k++)for(j = 0; j < 8; j++)PhotoRegion(k+1, j+1, photo[k][j]);GamePoint+=8;write_num(1, 1, GamePoint);}}}//记录游戏排名分数void RankingSquare(unsigned int point){unsigned char i, j;for(i = 0; i < 3; i++)if(point > GamePointRanking[i]){for(j = 1; j > i; j--) GamePointRanking[j] = GamePointRanking[j-1];GamePointRanking[i] = point;break;}}/*-------------------------------方块体自动移动-------------------------------*/void Init_Timer0(void)//定时器0{TMOD |= 0x01;TH0 = -50000/256;//差不多0.05sTL0 = -50000%256;EA  = 1;ET0 = 1;TR0 = 1;}void Timer0_isr() interrupt 1 using 1{static unsigned char l=0;//调速度TH0 = -50000/256;//差不多0.05sTL0 = -50000%256;l++;if(l==4*GameSpeed){l = 0;MoveAble = 1;//可移动}}

        好了,现在大概就是这样,这里我想说的就是通过这次的编程,我认识到模块化的编程思想真的是很重要,所以无论怎么样,保持清晰的思路少不了模块化思维……

        附上这两贴《童年时光,小小贪吃蛇》和《童年时光,俄罗斯方块》,接下来我就定期发帖有关数据结构学习的一些心得,包括源代码。希望以后能一直坚持下去,当然分享资源是一种生活方式,我也希望能在帮助别人的同时能得到他人的一些见解和评价,让人受益匪浅的也许就是那么只言片语,所以,分享吧……


 

 

 

原创粉丝点击