这是用链表结构制作俄罗斯方块的作业。这种结构并不是最适合来做这个游戏的,但是用链表来做,可以提高对链表的理解和对链表的运用能力,自己做的还是比较粗糙,希望大家来学习交流指出意见和建议,一共有三个部分。
第二部分
第三部分
这是进入游戏的欢迎界面,下方可以看到选择1,2,3,4个数字,来进入不同的界面,这些界面都是需要制作的。
1.头文件和需要用的全局变量
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
前三个头文件大家都很熟悉了, windows.h 文件是用来控制DOS界面,获取控制台上坐标位置,设置字体颜色需要用到的,稍后会详细给出代码来说明如何用到;conio.h
是用来接受键盘输入的,只要用在kbhit()和getch()函数上;time.h具体用来获取随机数,后面也会介绍。
#define FrameX 13//游戏窗口左上角的X坐标
#define FrameY 3//游戏窗口左上角的y坐标
#define Frame_height 20//游戏窗口的高度
#define Frame_width 18//游戏窗口的宽度
全局宏定义这几个常量,后面都要用到,需要记一下,游戏窗口实际如下图:
左上角坐标实际如下图:
int i,j,Temp1,Temp2;//几个需要用的int变量
int a[80][80]={0};//用来表示游戏界面的画或者不画情况的正方数组,都是0或1
int b[4];//一个俄罗斯方块4个块
int speed;//游戏速度
int number;//方块数量
int score;//分数
int level;//等级
typedef struct Tetris
{
int x;
int y;
int flag;
struct Tetris *pnext;
}asd;//用来做链表
struct Tetris *pa_tetris;//制作链表的工具指针
struct Tetris *pz_tetris;//尾指针
struct Tetris *pHead_tetris;//头指针
struct Tetris *pTemp_tetris;//这个指针如何用稍后会有代码
int contact;//0或1的变量有用
HANDLE hOut;//控制台句柄
需要说的是 HANDLE hOut ,这个句柄需要在此声明一下,之后的gotoxy函数必须在这个句柄说明之后,才能正常编译。
以下是整个游戏定义出要用的函数
void gotoxy(int x,int y);//移动光标
void DrawGameframe();//打印游戏方框
void CreateFlag();//稍后细说
void MakeTetris(struct Tetris*);//制作方块
void PrintTetris(struct Tetris*);//打印方块
void CleanTetris(struct Tetris*);//清楚上一时间方块痕迹
int ifMove(struct Tetris*);//判断是否可以移动
void Del_Fullline(struct Tetris*);//判断是否满行
void Gameplay();//稍后细说
void Regulation();//游戏规则
void explantion();//游戏解释
void Replay();//重玩
void welcome();//欢迎界面
void title();//标题
void flower();//一朵小花
void closer();//关闭游戏
int color();//颜色函数
int main();//主函数
2.函数解释
下面就按照上面给出的函数顺序,一个一个介绍函数(CreateFlag和Gameplay放在最后):
void gotoxy(int x,int y);
void gotoxy(int x,int y)
{
COORD pos;
pos.X=x;
pos.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
}
SetConsoleCursorPosition是API中定位光标位置的函数。
如果用户定义了 COORD pos,那么pos其实是一个结构体变量,其中X和Y是它的成员,之后每当调用gotoxy函数的时候,括号里面的 x和y就是你需要让光标跑到那个位置的坐标,很好用。
void DrawGameframe();
void DrawGameframe()
{
gotoxy(FrameX+Frame_width-5,FrameY-2);
color(11);
printf("俄罗斯方块");
gotoxy(FrameX+2*Frame_width+3,FrameY+7);
color(2);
printf("**********");
gotoxy(FrameX+2*Frame_width+13,FrameY+7);
color(3);
printf("出现下一个方块");
gotoxy(FrameX+2*Frame_width+3,FrameY+13);
color(2);
printf("**********");
gotoxy(FrameX+2*Frame_width+3,FrameY+17);
color(14);
printf("↑键:旋转");
gotoxy(FrameX+2*Frame_width+3,FrameY+19);
printf("空格:暂停游戏");
gotoxy(FrameX+2*Frame_width+3,FrameY+15);
printf("Esc:退出游戏");
gotoxy(FrameX,FrameY);
color(12);
printf("╔");
gotoxy(FrameX+2*Frame_width-2,FrameY);
printf("╗");
gotoxy(FrameX,FrameY+Frame_height);
printf("╚");
gotoxy(FrameX+2*Frame_width-2,FrameY+Frame_height);
printf("╝");
for(i=2;i<2*Frame_width-2;i+=2)
{
gotoxy(FrameX+i,FrameY);
printf("-");
}
for(i=2;i<2*Frame_width-2;i+=2)
{
gotoxy(FrameX+i,FrameY+Frame_height);
printf("-");
a[FrameX+i][FrameY+Frame_height]=2;
}
for(i=1;i<Frame_height;i++)
{
gotoxy(FrameX,FrameY+i);
printf("|");
a[FrameX][FrameY+i]=2;
}
for(i=1;i<Frame_height;i++)
{
gotoxy(FrameX+2*Frame_width-2,FrameY+i);
printf("|");
a[FrameX+2*Frame_width-2][FrameY+i]=2;
}
}
这是打印游戏方框的函数,如图
其中也可以看出为什么要定义以下几个变量了,如果不定义,那就会全是单纯的数字,看上去很乱
#define FrameX 13
#define FrameY 3
#define Frame_height 20
#define Frame_width 18
需要将光标移动到指定位置之前,需要先调用gotoxy函数,gotoxy(x,y),这样你下面要用的printf就会在指定位置进行打印了。
还看到有个color函数,color(数字)这个是之后会说的颜色函数,和gotoxy类似,每次你要将打印出的东西变个颜色的时候,也是提前调用color函数。
void PrintTetris(struct Tetris*tetris);
void PrintTetris(struct Tetris *tetris)
{
for(i=0;i<4;i++)
{
b[i]=1;//用循环将方块数组全部赋1
}
MakeTetris(tetris);//马上会说
for(i=tetris->x-2;i<=tetris->x+4;i+=2)//会有图示解释
{
for(j=tetris->y-2;j<=tetris->y+1;j++)//会有图示解释
{
if(a[i][j]==1&&j>FrameY)//FrameY就是最顶段的Y坐标,超过这个坐标就打印不了
{
gotoxy(i,j);//定位
printf("▇");//打印
}
}
}
gotoxy(FrameX+2*Frame_width+3,FrameY+1);//以下都是打印游戏界面右边的信息,每打印一个方块,信息都会更新或者不更新,所以每次都要运行一遍
color(4);
printf("level : ");
color(12);
printf("%d",level);
gotoxy(FrameX+2*Frame_width+3,FrameY+3);
color(4);
printf("score :");
color(12);
printf("%d",score);
gotoxy(FrameX+2*Frame_width+3,FrameY+5);
color(4);
printf("speed :");
color(12);
printf("%dms",speed);
}
解释一下这段代码
for(i=tetris->x-2;i<=tetris->x+4;i+=2)//会有图示解释
{
for(j=tetris->y-2;j<=tetris->y+1;j++)//会有图示解释
{
if(a[i][j]==1&&j>FrameY)//FrameY就是最顶段的Y坐标,超过这个坐标就打印不了
{
gotoxy(i,j);//定位
printf("▇");//打印
}
}
}
游戏所用的方块横向站2个单位,竖直占1个单位,而且俄罗斯方块所有的一共19种方块形态,无论怎么转换,都是在这个4X4的大方形里面变换的,所以双重循环把16个格子都读了一遍,那些需要打印,哪些不需要打印,都有1和0定义过,所以上面的代码将留很好理解了。
void MakeTetris(struct Tetris*tetris);
void MakeTetris(struct Tetris*tetris)
{
a[tetris->x][tetris->y]=b[0];//中心方块即上面4X4
switch(tetris->flag)
{
case 1://田字格
{
color(10);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x+2][tetris->y-1]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
case 2:
{
color(13);
a[tetris->x-2][tetris->y]=b[1];
a[tetris->x+2][tetris->y]=b[2];
a[tetris->x+4][tetris->y]=b[3];
break;
}
case 3:
{
color(13);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x][tetris->y-2]=b[2];
a[tetris->x][tetris->y+1]=b[3];
break;
}
case 4:
{
color(11);
a[tetris->x-2][tetris->y]=b[1];
a[tetris->x+2][tetris->y]=b[2];
a[tetris->x][tetris->y+1]=b[3];
break;
}
case 5:
{
color(11);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x][tetris->y+1]=b[2];
a[tetris->x-2][tetris->y]=b[3];
break;
}
case 6:
{
color(11);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x-2][tetris->y]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
case 7:
{
color(11);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x][tetris->y+1]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
case 8:
{
color(14);
a[tetris->x][tetris->y+1]=b[1];
a[tetris->x-2][tetris->y]=b[2];
a[tetris->x+2][tetris->y+1]=b[3];
break;
}
case 9:
{
color(14);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x-2][tetris->y]=b[2];
a[tetris->x-2][tetris->y+1]=b[3];
break;
}
case 10:
{
color(14);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x-2][tetris->y-1]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
case 11:
{
color(14);
a[tetris->x][tetris->y+1]=b[1];
a[tetris->x-2][tetris->y-1]=b[2];
a[tetris->x-2][tetris->y]=b[3];
break;
}
case 12:
{
color(12);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x][tetris->y+1]=b[2];
a[tetris->x-2][tetris->y-1]=b[3];
break;
}
case 13:
{
color(12);
a[tetris->x-2][tetris->y]=b[1];
a[tetris->x+2][tetris->y-1]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
case 14:
{
color(12);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x][tetris->y+1]=b[2];
a[tetris->x+2][tetris->y+1]=b[3];
break;
}
case 15:
{
color(12);
a[tetris->x-2][tetris->y]=b[1];
a[tetris->x-2][tetris->y+1]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
case 16:
{
color(9);
a[tetris->x][tetris->y+1]=b[1];
a[tetris->x][tetris->y-1]=b[2];
a[tetris->x+2][tetris->y-1]=b[3];
break;
}
case 17:
{
color(9);
a[tetris->x-2][tetris->y]=b[1];
a[tetris->x+2][tetris->y+1]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
case 18:
{
color(9);
a[tetris->x][tetris->y-1]=b[1];
a[tetris->x][tetris->y+1]=b[2];
a[tetris->x-2][tetris->y+1]=b[3];
break;
}
case 19:
{
color(9);
a[tetris->x-2][tetris->y]=b[1];
a[tetris->x-2][tetris->y-1]=b[2];
a[tetris->x+2][tetris->y]=b[3];
break;
}
}
}