知方号

知方号

C语言实现俄罗斯方块游戏程序设计附源码

C语言实现俄罗斯方块游戏程序设计附源码

 

目录

一、前言

二、需求分析

2.1 产品需求概述

2.1.1 功能简介

2.1.2 运行环境

2.2 功能需求

2.2.1 绘制地图

2.2.2 生成随机方块

2.2.3 按键响应

2.2.4 预览方块

2.2.5 分数累加

三、概要设计

3.1 系统体系结构图

3.2 模块描述

四、详细设计

4.1 系统主要函数说明   

4.1.1 函数DeawMap

4.1.2 函数Tetris*BlockRand

4.1.3 函数JudgeDirection

4.1.4 函数Form

4.1.5 函数ShowBlock

4.1.6 函数JudgeWall

4.1.7 函数MoveCursor

4.1.8 函数SetColour

4.1.9 函数JudgeGroud

4.1.10 函数JudgeEntire

4.1.11 函数Show

4.1.12 函数Location

五、系统测试

5.1 初始界面

5.2 控制方块

5.3 控制方块旋转

六、程序设计 

一、前言

 

俄罗斯方块游戏,是俄罗斯人阿莱克斯·帕伊特诺夫在八十年代末制作一款游戏,以其规则简单,容易上手,游戏过程变化无穷,已经成为一个家喻户晓老少皆宜的大众游戏。该程序是完成一个简易的俄罗斯方块的任务,其通过结构体、指针、绘图等方面的知识完成几个重要的功能:界面,方块下落,旋转,判断是否还能下落,左右移动,分数,速度设置,清除满的每行,下个方块的预览等。  

  二、需求分析 2.1 产品需求概述 2.1.1 功能简介

该程序是完成一个简易的俄罗斯方块的任务,规则简单,容易上手,游戏过程变化无穷,是一个家喻户晓、老少皆宜的大众游戏。本程序通过结构体、指针、绘图等方面的知识完成几个重要的功能:界面,方块下落,旋转,判断是否还能下落,左右移动,分数,速度设置,清除满的每行,下个方块的预览等。

2.1.2 运行环境

(1)硬件环境:

最低配置:CPU:Pentium3 800以上或其他的兼容规格,内存:256M以上,硬盘:100GB以上空间

推荐配置:CPU:Pentium4 1.6G,内存:512M以上,硬盘:100GB以上空间

(2)软件环境:

运用VC++6.0编程环境

  2.2 功能需求

本系统主要功能:绘制地图;生成随机方块;按键响应;预览方块;分数累加。

2.2.1 绘制地图

通过for循环嵌套的方式,绘制一个20*28的地图。

2.2.2 生成随机方块

随机生成下一个方块。

2.2.3 按键响应

通过按键控制程序,上键旋转方块,下键加速下落,左右键控制方块向左向右移动,Esc暂停游戏。

2.2.4 预览方块

在地图右上方显示下一个方块种类。

2.2.5 分数累加

1.在地图右上方显示目前已获得分数;

2.一次消除的行数越多,得分指数越高。

  三、概要设计

根据对题目的分析,该系统主要包括,生成方块、输出方块、按键响应、落地判断、刷新界面、预览方块六个功能,每一个功能都由相应的函数及其他函数辅助实现。主函数控制整个程序的运行及生成地图,可以使程序在一次的运行之中循环执行所有的功能,除此外还具有暂停功能,根据需要暂停程序,使程序更加的完美。

  3.1 系统体系结构图

图 1 俄罗斯方块_系统体系结构图

  3.2 模块描述

本系统为俄罗斯方块游戏系统,每个模块均为实现游戏功能,无子系统。

模块1:绘制地图

名  称

绘制地图

标识

DeawMap

系统名称

俄罗斯方块

接口说明

输入

输出

游戏地图

功能说明

绘制游戏地图

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

被调用模块

模块2:生成随机方块

名  称

生成随机方块

标识

Tetris*BlockRand

系统名称

俄罗斯方块

接口说明

输入

输出

方块

功能说明

确定下一个方块的类型

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

颜色设定

被调用模块

模块3:按键响应

名  称

按键响应

标识

JudgeDirection

系统名称

俄罗斯方块

接口说明

输入

键盘

输出

功能说明

控制方块

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

被调用模块

模块4:确定坐标

名  称

确定坐标

标识

Form

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

确定方块坐标

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

坐标更新

被调用模块

模块5:显示完整方块

名  称

显示完整方块

标识

ShowBlock

系统名称

俄罗斯方块

接口说明

输入

输出

 

功能说明

显示生成的方块

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

确定坐标、颜色设定、判断落地、判断是否落地、坐标更新、设置光标位置

被调用模块

模块6:判断左右界限

名  称

判断左右界限

标识

JudgeWall

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

判断左右界限

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

被调用模块

模块7:设置光标位置

名  称

设置光标位置

标识

MoveCursor

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

设置光标位置

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

被调用模块

模块8:颜色设定

名  称

颜色设定

标识

SetColour

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

确定下一个方块颜色

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

被调用模块

模块9:判断落地

名  称

判断落地

标识

JudgeGroud

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

判断是接触到方块或界面底边

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

被调用模块

模块10:判断整行是否填满

名  称

判断整行是否填满

标识

JudgeEntire

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

判读是否有整行被填满,若填满则清除行

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

被调用模块

模块11:显示信息

名  称

显示信息

标识

show

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

显示得分和下一个方块

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

设置光标位置

被调用模块

模块12:信息更新

名  称

信息更新

标识

Location

系统名称

俄罗斯方块

接口说明

输入

输出

功能说明

更新坐标和界面

运行环境

运行于VC++6.0编程坏境之下

调用关系

调用模块

设置光标位置

被调用模块

 

四、详细设计 4.1 系统主要函数说明   

函数名称

函数功能

DeawMap

绘制游戏地图

Tetris*BlockRand

确定下一个方块的类型

JudgeDirection

控制方块

Form

确定方块坐标

ShowBlock

显示生成的方块

JudgeWall

判断左右界限

MoveCursor

设置光标位置

SetColour

确定下一个方块颜色

JudgeGroud

判断是接触到方块或界面底边

JudgeEntire

判读是否有整行被填满

Show

显示得分和下一个方块

Location

更新坐标和界面

4.1.1 函数DeawMap

【功能】

绘制游戏地图

【参数】

【返回值】

【算法描述】

设置i,j两个整型变量,再通过for循环控制打印次数、内容及位置,绘制地图。

 

4.1.2 函数Tetris*BlockRand

【功能】

确定下一个方块的类型

【参数】

【返回值】

【算法描述】

用指针确定方块的类型及初始位置。

 

4.1.3 函数JudgeDirection

【功能】

控制方块

【参数】

【返回值】

【算法描述】

用if条件语句判断按键,实现不同按键对应的功能。

 

4.1.4 函数Form

【功能】

确定方块坐标

【参数】

【返回值】

【算法描述】

用Switch语句判断方块类型,在用if语句判断形态,并调用Location函数更新坐标。

 

4.1.5 函数ShowBlock

【功能】

显示生成的方块

【参数】

【返回值】

【算法描述】

调用JudgeDirection函数控制方块,用指针更新坐标、颜色信息,并调用MoveCursor函数更新光标位置,以及用if语句调用JudgeGroud函数判断是否到底面,最后调用JudgeEntire函数判断是否有整行被填满。

 

4.1.6 函数JudgeWall

【功能】

判断左右界限

【参数】

【返回值】

【算法描述】

用if条件语句判断当前方块坐标是否等于地图左右边界。

 

4.1.7 函数MoveCursor

【功能】

设置光标位置

【参数】

【返回值】

【算法描述】

用HANDLE output、GetStdHandle()获取标准输出的句柄,再用SetConsole CursorPosition()设置控制台光标位置。

 

4.1.8 函数SetColour

【功能】

确定方块颜色

【参数】

【返回值】

【算法描述】

用SetConsoleTextAttribute()控制方块颜色。

 

4.1.9 函数JudgeGroud

【功能】

判断是接触到方块或界面底边

【参数】

【返回值】

【算法描述】

用指针获取当前方块坐标,再用数个if语句判断是否到达底层,若到达底层则直接进行下一循环。

 

4.1.10 函数JudgeEntire

【功能】

判读是否有整行被填满

【参数】

【返回值】

【算法描述】

用if语句判断是否有整行被填满,若有整行被填满,则清除行,并调用Show更新得分。

 

4.1.11 函数Show

【功能】

在地图右上方显示得分和预览下一个方块

【参数】

【返回值】

【算法描述】

先用for循环二重嵌套调用MoveCursor清空显示区域,再用if语句判断新方块类型,将其打印在显示区域。

 

4.1.12 函数Location

【功能】

若填满则清除行

【参数】

【返回值】

【算法描述】

用指针更新坐标。

五、系统测试 5.1 初始界面

输出方块到顶部,显示下一个方块及得分

图 2 输出方块到顶部

5.2 控制方块

图 3 控制方块_初始位置

控制方块向左

图 4 控制方块_向左

控制方块向右

图 5 控制方块_向右

5.3 控制方块旋转

图 6 方块初始形态

图 7 方块旋转后形态

六、程序设计  #include#include#include#define HEIGHT 28 //设置地图高度#define WIDTH 20 //设置地图宽度#define ZERO 1#define HEIGHT_1 18#define Loca_y 6#define Loca_x 25#define PRINTF printf("■");#define LINE printf(" ");#define EMPTY printf(" ");typedef struct Tetris{ int x_1, y_1; //主x坐标,主y坐标,下面三个为附属,通过主坐标确定三个附属 int x_2, y_2; int x_3, y_3; int x_4, y_4; int code;//7种方块形态代号 Tetris * next;}Tetris;void DeawMap(); //绘制地图 Tetris * BlockRand(int code); //随机主方块生成 void JudgeDirection(Tetris ** Block); //按键响应 void Form(Tetris ** Block); //方块坐标全部确定void ShowBlock(Tetris ** Block); //显示完整方块 int JudgeWall(Tetris ** Block); //判断左右界限void MoveCursor(int x, int y); //移动光标 不闪屏是因为每次不会刷新全部地图,只会刷新某一特定区域void SetColour(int c); //颜色设定int JudgeGroud(Tetris * Phead, Tetris ** Block); //判断落地void JudgeEntire(Tetris * Head); //判断整行是否填满void NewEntire(Tetris * head, int y); //若上面函数成立,若清除该行,并刷新地图void Show(int n); //显示信息,下一个方块得分情况void Location(Tetris ** Block, int x, int y, int a, int b, int c, int d, int e, int f); //坐标更新1void Location_y(Tetris ** Block, int x, int y, int a, int b, int c, int d, int e, int f); //坐标更新2void Location_x(int x, int y, int a, int b, int c, int d, int e, int f); //信息更新Tetris *Phead = NULL; //链表头指针Tetris *Pend = NULL; //跟随指针 (尾插法需要)Tetris * Return = NULL; //节点地址返回int form = 0; //判断形态int UP = 0; //下降速度判断int code_y = 0;//随机形态int fengs = 220; //得分int main(){ DeawMap(); code_y = rand() % 7 + 1; while (1) { Return = BlockRand(code_y); code_y = rand() % 7 + 1; Show(code_y); ShowBlock(&Return); } system("pause>nul"); return 0;}void DeawMap() //绘制地图{ for (int i = 0; i < WIDTH; i++)PRINTF LINE //上边框 for (int i = 1; i < HEIGHT - 1; i++) //打印左右边框 { for (int j = 0; j < WIDTH; j++) { if (j == 0 || j == WIDTH - 1) { PRINTF if (j == WIDTH - 1)LINE } else EMPTY } } for (int i = 0; i < WIDTH; i++)PRINTF LINE //下边框 system("color 03");}void Location(Tetris ** Block, int x, int y, int a, int b, int c, int d, int e, int f)//坐标更新1{ (*Block)->x_1 = (*Block)->x_1 + x; (*Block)->y_1 = (*Block)->y_1 + y; (*Block)->x_2 = (*Block)->x_1 + a; (*Block)->y_2 = (*Block)->y_1 + b; (*Block)->x_3 = (*Block)->x_1 + c; (*Block)->y_3 = (*Block)->y_1 + d; (*Block)->x_4 = (*Block)->x_1 + e; (*Block)->y_4 = (*Block)->y_1 + f;}void Location_y(Tetris ** Block, int x, int y, int a, int b, int c, int d, int e, int f)//坐标更新2{ (*Block)->x_1 = (*Block)->x_1 + x; (*Block)->y_1 = (*Block)->y_1 + y; (*Block)->x_2 = (*Block)->x_2 + a; (*Block)->y_2 = (*Block)->y_2 + b; (*Block)->x_3 = (*Block)->x_3 + c; (*Block)->y_3 = (*Block)->y_3 + d; (*Block)->x_4 = (*Block)->x_4 + e; (*Block)->y_4 = (*Block)->y_4 + f;}Tetris * BlockRand(int code_y) //随机主方块生成{ srand((int)time(0)); Tetris * Block = (Tetris*)malloc(sizeof(Tetris)); Block->x_1 = 8; Block->y_1 = 4;//规定初始中心方块的坐标为(8,4) Block->code = code_y; if (Phead == NULL)Phead = Block; else Pend->next = Block; Block->next = NULL; Pend = Block; return Block;}void ShowBlock(Tetris ** Block) //显示完整方块 { while (1) { Form(&Return); if ((*Block)->code == 1)SetColour(13); if ((*Block)->code == 2)SetColour(15); if ((*Block)->code == 3)SetColour(12); if ((*Block)->code == 4)SetColour(10); if ((*Block)->code == 5)SetColour(6); if ((*Block)->code == 6)SetColour(4); if ((*Block)->code == 7)SetColour(8); MoveCursor((*Block)->x_1, (*Block)->y_1); PRINTF MoveCursor((*Block)->x_2, (*Block)->y_2); PRINTF MoveCursor((*Block)->x_3, (*Block)->y_3); PRINTF MoveCursor((*Block)->x_4, (*Block)->y_4); PRINTF if (JudgeGroud(Phead, &Return) == 0) { system("color 03"); break; } if (UP == 0) { for (int i = 0; i y_1); EMPTY MoveCursor((*Block)->x_2, (*Block)->y_2); EMPTY MoveCursor((*Block)->x_3, (*Block)->y_3); EMPTY MoveCursor((*Block)->x_4, (*Block)->y_4); EMPTY Location_y(&Return, 0, 1, 0, 1, 0, 1, 0, 1); JudgeDirection(&Return); JudgeEntire(Phead); }}void JudgeDirection(Tetris ** Block) //按键响应{ if (GetAsyncKeyState(VK_UP) && 0x8000) { form += 1; if (form == 4) { form = 0; } Form(&Return); } if (GetAsyncKeyState(VK_DOWN) && 0x8000) { //加速向下 时间加速 UP = 1; } if (GetAsyncKeyState(VK_LEFT) && 0x8000) { //向左移动 if (JudgeWall(&Return) != -1) Location_y(&Return, -1, 0, -1, 0, -1, 0, -1, 0); } if (GetAsyncKeyState(VK_RIGHT) && 0x8000) { //向右移动 if (JudgeWall(&Return) != -2) Location_y(&Return, 1, 0, 1, 0, 1, 0, 1, 0); } if (GetAsyncKeyState(VK_ESCAPE) && 0x0D) { MoveCursor(27, 15); printf("游戏暂停"); //判断Esc while (1) { if (GetAsyncKeyState(VK_ESCAPE) && 0x0D) { MoveCursor(27, 15); printf(" "); break; } } }}void Form(Tetris ** Block) //方块坐标全部确定{ //先确实哪一类,再细分 switch ((*Block)->code) { case 1: if (form == 0)Location(&Return, 0, 0, -1, 0, 0, -1, 1, 0); if (form == 1)Location(&Return, 0, 0, 0, 1, 0, -1, 1, 0); if (form == 2)Location(&Return, 0, 0, 0, 1, -1, 0, 1, 0); if (form == 3)Location(&Return, 0, 0, 0, 1, -1, 0, 0, -1); break; case 2: Location(&Return, 0, 0, 1, 0, 0, 1, 1, 1); break; case 3: if (form == 0 || form == 2)Location(&Return, 0, 0, 0, -1, 0, 1, 0, 2); if (form == 1 || form == 3)Location(&Return, 0, 0, -1, 0, 1, 0, 2, 0); break; case 4: if (form == 0)Location(&Return, 0, 0, -1, 0, 1, 0, 1, -1); if (form == 1)Location(&Return, 0, 0, 0, -1, 1, 0, 0, -2); if (form == 2)Location(&Return, 0, 0, 0, -1, 1, -1, 2, -1); if (form == 3)Location(&Return, 0, 0, 0, -1, 0, -2, -1, -2); break; case 5: if (form == 0)Location(&Return, 0, 0, 1, 0, 2, 0, 0, -1); if (form == 1)Location(&Return, 0, 0, 1, 0, 1, -1, 1, -2); if (form == 2)Location(&Return, 0, 0, 1, 0, 2, 0, 2, 1); if (form == 3)Location(&Return, 0, 0, 1, 0, 0, 1, 0, 2); break; case 6: if (form == 0 || form == 2)Location(&Return, 0, 0, 0, -1, 1, 0, 1, 1); if (form == 1 || form == 3)Location(&Return, 0, 0, 0, -1, 1, -1, -1, 0); break; case 7: if (form == 0 || form == 2)Location(&Return, 0, 0, 0, 1, 1, 0, 1, -1); if (form == 1 || form == 3)Location(&Return, 0, 0, 0, 1, -1, 0, 1, 1); }}void MoveCursor(int x, int y)//设置光标位置(就是输出显示的开始位置){ COORD pos = { x * 2,y }; HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//获得 标准输出的句柄 SetConsoleCursorPosition(output, pos); //设置控制台光标位置}void SetColour(int c) //颜色设定{ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);//API函数可以改变控制台颜色}int JudgeWall(Tetris ** Block) //判断左右界限{ if ((*Block)->x_1 == ZERO || (*Block)->x_2 == ZERO || (*Block)->x_3 == ZERO || (*Block)->x_4 == ZERO)return -1; if ((*Block)->x_1 == HEIGHT_1 || (*Block)->x_2 == HEIGHT_1 || (*Block)->x_3 == HEIGHT_1 || (*Block)->x_4 == HEIGHT_1)return -2; return 0;}int JudgeGroud(Tetris * Phead, Tetris ** Block) //判断落地{ Tetris * P = Phead; //如果到达最低层。直接经行下一循环 if ((*Block)->y_1 == 26 || (*Block)->y_2 == 26 || (*Block)->y_3 == 26 || (*Block)->y_4 == 26)return 0; while (P->next != NULL) { if (P->y_1 == (*Block)->y_1 + 1) { if (P->x_1 == (*Block)->x_1)return 0; } if (P->y_2 == (*Block)->y_1 + 1) { if (P->x_2 == (*Block)->x_1)return 0; } if (P->y_3 == (*Block)->y_1 + 1) { if (P->x_3 == (*Block)->x_1)return 0; } if (P->y_4 == (*Block)->y_1 + 1) { if (P->x_4 == (*Block)->x_1)return 0; } if (P->y_1 == (*Block)->y_2 + 1) { if (P->x_1 == (*Block)->x_2)return 0; } if (P->y_2 == (*Block)->y_2 + 1) { if (P->x_2 == (*Block)->x_2)return 0; } if (P->y_3 == (*Block)->y_2 + 1) { if (P->x_3 == (*Block)->x_2)return 0; } if (P->y_4 == (*Block)->y_2 + 1) { if (P->x_4 == (*Block)->x_2)return 0; } if (P->y_1 == (*Block)->y_3 + 1) { if (P->x_1 == (*Block)->x_3)return 0; } if (P->y_2 == (*Block)->y_3 + 1) { if (P->x_2 == (*Block)->x_3)return 0; } if (P->y_3 == (*Block)->y_3 + 1) { if (P->x_3 == (*Block)->x_3)return 0; } if (P->y_4 == (*Block)->y_3 + 1) { if (P->x_4 == (*Block)->x_3)return 0; } if (P->y_1 == (*Block)->y_4 + 1) { if (P->x_1 == (*Block)->x_4)return 0; } if (P->y_2 == (*Block)->y_4 + 1) { if (P->x_2 == (*Block)->x_4)return 0; } if (P->y_3 == (*Block)->y_4 + 1) { if (P->x_3 == (*Block)->x_4)return 0; } if (P->y_4 == (*Block)->y_4 + 1) { if (P->x_4 == (*Block)->x_4)return 0; } P = P->next; } return 1;}void JudgeEntire(Tetris * Head) //判断整行是否填满{ Tetris * PHead = Head; //从1到26 for (int y = 26; y >= 1; y--) { int sum = 0; while (PHead->next != NULL) { if (PHead->y_1 == y)sum++; if (PHead->y_2 == y)sum++; if (PHead->y_3 == y)sum++; if (PHead->y_4 == y)sum++; MoveCursor(20, 28); PHead = PHead->next; } PHead = Head; if (sum == 18) { //如果成行则,执行NewEntire() 清空该行,并将所有y下降一个单位。 NewEntire(Phead, y); fengs += 10; Show(code_y); } sum = 0; }}void NewEntire(Tetris * head,int y) //若上面函数成立,若清除该行,并刷新地图{ Tetris * PHead = head; while (PHead->next != NULL) { if (PHead->y_1 == y) { MoveCursor(PHead->x_1, PHead->y_1); EMPTY PHead->x_1 = 99; PHead->y_1 = 99; } if (PHead->y_2 == y) { MoveCursor(PHead->x_2, PHead->y_2); EMPTY PHead->x_2 = 99; PHead->y_2 = 99; } if (PHead->y_3 == y) { MoveCursor(PHead->x_3, PHead->y_3); EMPTY PHead->x_3 = 99; PHead->y_3 = 99; } if (PHead->y_4 == y) { MoveCursor(PHead->x_4, PHead->y_4); EMPTY PHead->x_4 = 99; PHead->y_4 = 99; } PHead = PHead->next; } PHead = head; while (PHead->next != NULL) { if (PHead->y_1 < y) { MoveCursor(PHead->x_1, PHead->y_1); EMPTY PHead->y_1 += 1; MoveCursor(PHead->x_1, PHead->y_1); PRINTF } if (PHead->y_2 < y) { MoveCursor(PHead->x_2, PHead->y_2); EMPTY PHead->y_2 += 1; MoveCursor(PHead->x_2, PHead->y_2); PRINTF } if (PHead->y_3 < y) { MoveCursor(PHead->x_3, PHead->y_3); EMPTY PHead->y_3 += 1; MoveCursor(PHead->x_3, PHead->y_3); PRINTF } if (PHead->y_4 < y) { MoveCursor(PHead->x_4, PHead->y_4); EMPTY PHead->y_4 += 1; MoveCursor(PHead->x_4, PHead->y_4); PRINTF } PHead = PHead->next; }}void Show(int n) //显示信息,下一个方块得分情况,如果想加入一些信息可以在该函数内修改{ //显示下一个方块 //先清空该区域for (int j = 4; j

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。