游戏逻辑的设计:
一.游戏逻辑概述:
二.判断俄罗斯方块是否可移动:
判断是否可以移动,就是要判断移动的位置是不是空位置,如果能放下此形状的俄罗斯方块,就说明这个方块可移动
要判断移动到的位置是不是空位置,
1)首先要看这个位置中心方块对应的位置是不是已经放了方块或者说是墙壁,如果是方块或者墙壁,则绝对不可移动到该位置,
2)如果不是方块或者墙壁,并不是说方块就可以放在这个地方了,需要进一步判断,看看特定类型的俄罗斯方块周围的另外三个方块能不能放置到周围的位置,即所需要的周围三个位置是不是都为空,只有三个都满足,这个俄罗斯方块才能在这个地方放下;
3)因为我们每走一步(即俄罗斯方块每下落一步我们都会判断一次,因此在俄罗斯方块下落路径上的所有位置我们都已经判断,也就不会存在“全包围“的那种结构!!!)
/**
* 判断是否可移动
*/
int ifMove(struct Tetris *tetris)
{
if(a[tetris->x][tetris->y]!=0)//当中心方块位置上有图案时,返回值为0,即不可移动
{
return 0;
}
else
{
if(
( tetris->flag==1 && ( a[tetris->x][tetris->y-1]==0 &&//当为田字方块且除中心方块位
置外,其他"■"字方块位置上无图案时,说明这个位置能够放下田字方块,可以移动到这个位置,返回值为1,即
可移动
a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
//或为直线方块且除中心方块位置外,其他"■"字方块位置上无图案时,返回值为1,即可移动
( tetris->flag==2 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y]==0 && a[tetris->x+4][tetris->y]==0 ) ) ||
( tetris->flag==3 && ( a[tetris->x][tetris->y-1]==0 && //直线方块(竖)
a[tetris->x][tetris->y-2]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||
( tetris->flag==4 && ( a[tetris->x-2][tetris->y]==0 && //T字方块
a[tetris->x+2][tetris->y]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||
( tetris->flag==5 && ( a[tetris->x][tetris->y-1]==0 && //T字方块(顺时针90°)
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y]==0 ) ) ||
( tetris->flag==6 && ( a[tetris->x][tetris->y-1]==0 && //T字方块(顺时针180°)
a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==7 && ( a[tetris->x][tetris->y-1]==0 && //T字方块(顺时针270°)
a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==8 && ( a[tetris->x][tetris->y+1]==0 && //Z字方块
a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||
( tetris->flag==9 && ( a[tetris->x][tetris->y-1]==0 && //Z字方块(顺时针180°)
a[tetris->x-2][tetris->y]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||
( tetris->flag==10 && ( a[tetris->x][tetris->y-1]==0 && //Z字方块(反转)
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==11 && ( a[tetris->x][tetris->y+1]==0 &&//Z字方块(反转+顺时针180°)
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x-2][tetris->y]==0 ) ) ||
( tetris->flag==12 && ( a[tetris->x][tetris->y-1]==0 && //7字方块
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y-1]==0 ) ) ||
( tetris->flag==15 && ( a[tetris->x-2][tetris->y]==0 && //7字方块(顺时针90°)
a[tetris->x-2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==14 && ( a[tetris->x][tetris->y-1]==0 && //7字方块(顺时针180°)
a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||
( tetris->flag==13 && ( a[tetris->x-2][tetris->y]==0 && //7字方块(顺时针270°)
a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==16 && ( a[tetris->x][tetris->y+1]==0 && //7字方块(反转)
a[tetris->x][tetris->y-1]==0 && a[tetris->x+2][tetris->y-1]==0 ) ) ||
( tetris->flag==19 && ( a[tetris->x-2][tetris->y]==0 &&//7字方块(反转+顺时针90°)
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==18 && ( a[tetris->x][tetris->y-1]==0 &&//7字方块(反转+顺时针180°)
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||
( tetris->flag==17 && ( a[tetris->x-2][tetris->y]==0 &&//7字方块(反转+顺时针270°)
a[tetris->x+2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) )
{
return 1;
}
}
return 0;
}
机智,我们没有非要判定来的是哪一种类型的俄罗斯方块,用或逻辑来联系所有可能出现的情况,这是一个算法,这个算法不可避免地会面临着处理速率的低下,但是算法的复杂度显然没有那么复杂,在对算法处理速度没有很高的要求的情况下,这是一个很可取的选择
注意,一共有19种俄罗斯方块,而我们用flag来标记出现的;
三.清除俄罗斯方块下降的痕迹:
/**
* 清除俄罗斯方块的痕迹
*/
void CleanTetris(struct Tetris *tetris)
{
for(i=0;i<4;i++) //数组b[4]中有4个元素,循环这4个元素,让每个元素的值都为0
{
b[i]=0; //数组b[4]的每个元素的值都为0
}
MakeTetris(tetris); //制作俄罗斯方块
for( i = tetris->x - 2;i <= tetris->x + 4; i+=2 ) //■X■■ X为中心方块
{
for(j = tetris->y-2;j <= tetris->y + 1;j++) /* ■
■
X
■ */
{
if( a[i][j] == 0 && j > FrameY ) //如果这个位置上没有图案,并且处于游戏界面当中
{
gotoxy(i,j);
printf(" "); //清除方块
}
}
}
}
四.判断方块是否满行
/**
* 判断是否满行并删除满行的俄罗斯方块
*/
void Del_Fullline(struct Tetris *tetris) //当某行有Frame_width-2个方块时,则满行消除
{
int k,del_rows=0; //分别用于记录某行方块的个数和删除方块的行数的变量
for(j=FrameY+Frame_height-1;j>=FrameY+1;j--)
{
k=0;
for(i=FrameX+2;i<FrameX+2*Frame_width-2;i+=2)
{
if(a[i][j]==1) //纵坐标依次从下往上,横坐标依次由左至右判断是否满行
{
k++; //记录此行方块的个数
if(k==Frame_width-2) //如果满行
{
for(k=FrameX+2;k<FrameX+2*Frame_width-2;k+=2) //删除满行的方块
{
a[k][j]=0;
gotoxy(k,j);
printf(" ");
}
//如果删除行以上的位置有方块,则先清除,再将方块下移一个位置
for(k=j-1;k>FrameY;k--)
{
for(i=FrameX+2;i<FrameX+2*Frame_width-2;i+=2)
{
if(a[i][k]==1)
{
a[i][k]=0;
gotoxy(i,k);
printf(" ");
a[i][k+1]=1;
gotoxy(i,k+1);
printf("■");
}
}
}
j++; //方块下移后,重新判断删除行是否满行
del_rows++; //记录删除方块的行数
}
}
}
}
tetris->score+=100*del_rows; //每删除一行,得100分
if( del_rows>0 && ( tetris->score%1000==0 || tetris->score/1000>tetris->level-1 ) )
{ //如果得1000分即累计删除10行,速度加快20ms并升一级
tetris->speed-=20;
tetris->level++;
}
}
因为游戏界面的宽度是Frame_width除去两个竖边框,满行时方块占有的宽度为Frame_width-2!!!
六.随机产生俄罗斯方块
1)在进行游戏时,每次下落的方块都是随机产生的;
2)需要使用随机数函数rand来获得随机的方块类型序号;
3)因为我们之前已经定义好了俄罗斯方块的类型flag,就是1~19,我们只需要随机产生在这19个数之中就好;
关于rand函数
1)rand函数没有输入参数,直接通过表达式rand来引用,生成一个在0~RANG_MAX之间的一个随机数,其中RAND_MAX的值与编译系统有关,一般为32767
2)虽然它是一个随机数函数,但严格而言,它返回的是一个伪随机数,
因为在没有执行其他操作的前提下,执行同一个程序时,调用rand函数所得的随机数序列是一致的!!!,这是一个很严重的问题
3)为了达到真正随机的效果,令rand的返回值更具随机性,通常要为随机数生成器提供一个新的随机种子,C语言提供了srand函数,可以为生成器播撒种子,只要种子不同,rand函数就会生成不同的随机数序列,
srand函数称为随机数生成器的初始化器!!!