目录
一、 需求分析
1.1综合描述
1.1.1产品的功能
1.1.2用户类和特性
1.1.3运行环境
1.1.4设计和实现上的限制
1.2外部接口需求
1.2.1用户界面
1.2.2硬件接口 5
1.2.3软件接口
1.2.4通讯接口
1.3系统功能需求
1.4其它非功能需求
1.4.1性能需求
1.4.2安全措施需求 7
1.4.3安全性需求
1.4.4软件质量属性
1.4.5业务规则
1.5分析模型
二、 概要设计
2.1设计概述 8
2.2系统逻辑设计
三、 详细设计
3.1界面设计 9
3.2过程设计
四、 实现
4.1程序设计语言的选择 10
4.2编码风格说明 10
4.3详细实现过程及代码
五、测试 33
5.1测试
六、心得体会 40
6.1心得体会
1.1综合描述 俄罗斯方块是一款经典的电子游戏,由苏联程序员阿列克谢·帕基特诺夫于1984年发明。游戏的目标是通过旋转和移动不同形状的方块,将它们排列成完整的横行以消除。一旦一整行叠满,这一行就会消失,玩家得到分数。随着游戏进行,方块下落的速度会逐渐加快,增加了游戏的挑战性。
在游戏过程中,玩家需要操控方块的移动和旋转。方块有多种形状,如I、L、J、O、S、Z等。通过键盘操作,玩家可以使方块左右移动、旋转或加速下落。游戏的目标是获得尽可能高的分数,挑战自我和与其他玩家竞争。
俄罗斯方块游戏的关键在于判断和规划方块的移动路径,以便在有限的空间内消除更多行。此外,游戏还要求玩家在方块下落过程中快速决策,灵活应对各种情况。随着游戏难度的增加,玩家需要更快的反应速度和策略性思维。
总之,俄罗斯方块是一款简单易懂、趣味盎然的游戏,深受玩家喜爱。它不仅考验了玩家的手眼协调和反应速度,还锻炼了策略思维和规划能力。无论是休闲娱乐还是竞技挑战,俄罗斯方块都具有很高的可玩性。
该游戏适合于:休闲玩家、儿童和青少年、游戏爱好者、怀旧玩家、挑战自我、教育工作者
对该产品已知的限制:起始位置:四格拼板的起始位置在游戏区域的中上方,其它小方格由于在游戏区域的二维数组中占有的是下标为负数的位置,需要屏蔽坐标为负数的小方格的显示。左右规则:四格拼板不能超出游戏区域的范围,注意游戏区域的二维数组的边界问题。
1.1.1产品的功能 游戏开始:点击开始游戏按钮,游戏开始,自动产生方块。
方块移动:方块可以左右移动,方向键控制左右移动。
方块旋转:方块可以旋转。
方块加速下降:方向键可以加速下降。
方块直接落地:方块可以直接落地。
消除行:当一行填满时,自动消除该行,上方的方块自动下落。
游戏结束:当方块无法下落时,游戏结束。
1.1.2用户类和特性
操作简便:玩家可以通过键盘操作方块的移动、旋转和加速,容易上手。
难度逐渐增加:随着游戏进行,方块下落的速度会逐渐加快,提高了游戏的挑战性,锻炼了玩家的反应速度和策略性思维。
高度可玩性:俄罗斯方块既适合休闲娱乐,也适合竞技挑战,具有较高的可玩性。
单人模式和多人模式:游戏提供单人模式,让玩家独自挑战;同时也支持多人模式,玩家可以邀请朋友一起游戏,提高游戏乐趣。
适应多种平台:俄罗斯方块游戏可以运行在各种电子设备上,如电脑、手机、平板等,方便玩家随时随地体验游戏。
训练思维能力:俄罗斯方块不仅考验了玩家的手眼协调和反应速度,还锻炼了策略思维和规划能力。
1.1.3运行环境 硬件平台:控制台窗口
操作系统和版本:Red Panda Dev-C++软件, Windows 11家庭中文版
支撑环境和版本:C盘
1.1.4设计和实现上的限制 必须使用的特定技术、工具、编程语言:游戏开发技术,CodeBlocks,C语言
避免使用的特定技术、工具、编程语言:不熟悉或者不常用的技术或者工具、过时的技术或者工具、需要大量配置的技术或者工具、付费的技术或者工具、HTML、CSS等
要求遵循的开发规范和标准:编码规范:遵循统一的编码规范,模块化设计:将游戏拆分成不同的模块,兼容性考虑:考虑不同设备和浏览器的兼容性问题,兼容性考虑:考虑不同设备和浏览器的兼容性问题。
1.2外部接口需求 游戏界面接口:用于显示游戏界面和图形元素,需要提供方法来更新游戏状态和绘制游戏界面。
游戏控制接口:用于接收和处理用户输入,包括键盘按键和鼠标点击等事件。需要提供方法来处理游戏控制逻辑。
分数和等级显示接口:用于显示游戏分数和等级,需要提供方法来更新分数和等级显示。
1.2.1用户界面 控制区域:用户可以通过键盘或者手柄来控制方块的移动和旋转。
游戏场景:游戏界面通常是一个正方形的区域,方块会从顶部逐渐下落,玩家需要将方块放置在底部,填满整行或多行以消除方块。
得分显示:界面通常会显示当前得分和最高得分,以激励玩家不断挑战自己的记录。
下一个方块预览:界面通常会显示下一个方块的预览,让玩家提前做好规划和决策。
游戏暂停和重新开始:用户界面通常具有暂停和重新开始游戏的功能,让玩家可以在需要的时候暂停游戏或重新开始新的游戏。
游戏结束提示:当玩家无法再放置方块时,界面会显示游戏结束的提示,并且给出游戏结果和分数。
1.2.2硬件接口 P0端口:用于外部程序数据存储器,可被定义为数据/地址的第八位。
1.2.3软件接口 键盘:通过输入设备接口与键盘连接,用于接收玩家的操作指令。
显示屏:通过显示接口与显示屏连接,用于显示游戏画面。
音频设备:通过输出设备接口与音频设备连接,用于播放游戏音效
1.2.4通讯接口 异步需求:在单人游戏模式下,需要实现游戏数据与玩家操作之间的异步处理,以便在玩家进行游戏时,游戏数据能够实时更新。
1.3系统功能需求 游戏开始
开始界面的动画效果
背景音乐
提示信息
游戏中/游戏界面
游戏中的提示信息
得分情况
游戏规则说明
操作方法
下一个要出现的方块
游戏池(挡块移动的范围)
游戏计时
背景装饰
消除统计数
统计所有方块出现的数目
方块属性
颜色
形状【7种】
形态【4种】
位置:x,y坐标
方块运动:
定时下落【设置下落的时间间隔】
按键控制
方块左移
方块右移
方块加速下落
方块落底
游戏暂停
消除行数
方块储存
行数消除得分
游戏结束
注销账户
游戏结束动画
重新开始
退出游戏
1.4其它非功能需求 页面美化
充值系统
双人模式
道具
背景音乐
难度模式
皮肤
排行榜
增加方块的种类
增加游戏模式
性能需求
易用性
兼容性
可移植性
可维护性
安全性
1.4.1性能需求 游戏需要具备较高的性能,能够在不卡顿的情况下保持流畅的游戏体验。
游戏需要占用较少的资源,以保证在不同设备上的运行效果。
游戏需要具备清晰、简洁、明了的界面,以便玩家能够快速上手。
游戏需要具备较高的稳定性和可靠性,以确保玩家能够长时间、无故障地玩游戏。
游戏需要具备较好的可扩展性和可维护性,以便对游戏进行升级和维护。
1.4.2安全措施需求 保护玩家隐私:确保玩家的个人信息和数据安全,不泄露给第三方。
安全漏洞修复:及时发现和修复游戏存在的安全漏洞,确保游戏安全。
1.4.3安全性需求 隐私政策:明确收集用户信息的范围和目的,保障用户信息安全。
1.4.4软件质量属性 稳定性优于功能性
易用性优于易学性
可移植性优于有效性。
可维护性优于可扩张性
1.4.5业务规则 游戏规则:游戏应遵循经典的俄罗斯方块游戏规则,包括方块的形状、下落速度、旋转方向等。
游戏得分:游戏得分应按照玩家消除的方块数量和速度进行计算,得分越高,排名越靠前。
游戏等级:游戏等级应按照玩家的得分进行划分
1.5分析模型
概要设计 2.1设计概述 系统调用模块
获取句柄
获取光标位置
设置文字颜色(前景色,背景色)
设置光标是否可见
关闭句柄
数据模块
方块数据
游戏池、所有方块都要记录落到什么位置
复杂、特殊意义
游戏逻辑模块
游戏开始动画
界面绘制
方块运动
消行下移
游戏结束判断
游戏重新开始
游戏结束动画
背景音乐
2.2系统逻辑设计 首页模块页面:设计的首页页面主要是欢迎玩家,玩家点击后进入游戏选择模式。
模式选择页面:游戏控制模式的设计和难度选择的设计基本一样。
游戏显示页面:该游戏的具体显示页面,该页面的设计需要包含五个功能模块。
程序主流程:
首先通过随机函数生成方块并且在右侧界面中提前展示下一个要下落的方块给玩家,然后将方块按一定的速度自由下落,在STM32开发板中玩家自行选择按键或者遥控器来控制方块的运动,各种方块运动方式包括:下降时左右移动、翻转,其中翻转又根据不同的方块有不同的翻转次数。当方块不能再下落定后,对每一行进行判断,如果有某行的方块是填满的,则消除这行的方块,并且在该行上面的所有方块整体下移。
详细设计 1.游戏界面:使用C语言编写游戏界面,通常使用图形库(如SDL、Allegro等)来实现。游戏界面包括游戏区域、得分显示、游戏状态(如游戏是否结束)等。
2.方块形状和旋转:定义各种形状的方块(如I、L、J、O、S、Z等)及其旋转规则。使用C语言中的二维数组来表示方块的布局,旋转时根据规则改变数组中的值。
3.方块移动和碰撞检测:实现方块在游戏区域内的上下左右移动。在移动过程中,检测方块是否与其他方块或游戏边界发生碰撞。如果发生碰撞,则停止移动并恢复原状。
4.方块下落和消除:控制方块逐行下落,并在每行填满时触发消除。消除一行时,将该行从游戏区域中删除,并向上移动其他方块。同时更新得分和游戏状态。
5.游戏循环:在游戏循环中不断处理用户输入(如键盘操作),更新方块位置和状态,检查游戏是否结束。此外,还需要控制方块下落速度,如每一定时间增加一个方块。
6.游戏结束判断:当方块堆积至游戏区域顶部时,游戏结束。可以设置一定的分数目标,当玩家达到或超过该目标时,游戏胜利。
7.保存和加载游戏:实现保存和加载游戏的功能,方便玩家随时退出后继续游戏。
3.1界面设计 游戏界面:使用C语言编写游戏界面,通常使用图形库(如SDL、Allegro等)来实现。游戏界面包括游戏区域、得分显示、游戏状态(如游戏是否结束)等。 3.2过程设计
判定树:描述了俄罗斯方块游戏过程中不同状态的转换和决策过程。判定树是一种非常适合描述条件决策和状态转换的工具。在俄罗斯方块中,判定树可以用来表示游戏状态的变化,例如方块的移动、旋转、下落以及与游戏区域的碰撞等。通过判定树,可以清晰地看到不同状态之间的转换条件和决策逻辑。
实现 4.1程序设计语言的选择 C/C++语言:
纵向:C和C++是常用的游戏开发语言,具有较高的性能和灵活性,可以开发出高性能、底层的系统级应用程序和游戏。
横向:与其他语言相比,C/C++在性能上具有优势,可以更好地控制游戏逻辑和渲染过程。
4.2编码风格说明 变量名采用小驼峰式命名
函数名用英文全名写,便于识别
编写时增加必要的注释,便于自己和别人的查找
适当的使用空格,使代码更清晰、明了
系统思考:编程思维强调系统思考的能力,能够从整体出发,对问题进行全面综合的考虑和分析,并进行系统化的设计与实现。
4.3详细实现过程及代码 #include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
HANDLE handle;//全局变量
// 1. 初始化句柄
void initHandle()
{
handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
// 2. 设置颜色
void setColor(int color)
{
SetConsoleTextAttribute(handle, color);
}
// 3. 设置光标是否可见
void setCursorVisible(int flag)
{
CONSOLE_CURSOR_INFO info; // 光标信息结构体
info.bVisible = flag; // 光标是否可见
info.dwSize = 100; // 光标的宽度1-100
SetConsoleCursorInfo(handle, &info);
}
// 4.设置位置
void setPos(int x, int y)
{
COORD coord = {x*2, y};
SetConsoleCursorPosition(handle, coord);
}
// 5. 设置窗口大小
void setWindowsSize(int w, int h)
{
COORD coord = {w, h};
SetConsoleScreenBufferSize(handle, coord);
SMALL_RECT rect = {0, 0, w-1, h-1};
SetConsoleWindowInfo(handle, 1, &rect);
}
// 6.设置标题
void setTitle(char title[40])
{
SetConsoleTitle(title);
}
// 打印开始界面
void printStart(int x, int y)
{
int color = rand()%0x10;
if(color == 0x00)
{
color = 0x0f;
}
setColor(color);
setPos(x, y);
printf("■■■■■ ■■■■■ ■■■■■ ■■■■ ■■■ ■■■■");
setPos(x, y+1);
printf(" ■ ■ ■ ■ ■ ■ ■");
setPos(x, y+2);
printf(" ■ ■■■■ ■ ■■■■ ■ ■■■");
setPos(x, y+3);
printf(" ■ ■ ■ ■ ■ ■ ■");
setPos(x, y+4);
printf(" ■ ■■■■■ ■ ■ ■ ■■■ ■■■■");
setPos(20, 15);
printf("按任意键开始游戏");
}
// 清除开始动画
void clearStart(int x, int y)
{
int i,j;
for(i=y; i<=y+4; i++)
{
for(j=x; j<=x+33; j++)
{
setPos(j, i);
printf(" "); // 俩空格
}
}
}
// 动画
void printAnimation()
{
clock_t time1, time2;
time1 = clock();
int x=5;
printStart(x, 5);
while(1)
{
time2 = clock();
if(time2-time1 > 300)
{
time1 = time2;
clearStart(x,5);
printStart(++x, 5);
if(x == 25)
{
clearStart(x, 5);
x = 0;
}
}
if(kbhit()){
break;
}
}
system("cls");
}
// 定义分数和等级两个全局变量,用于保存游戏中的分数和等级
int level = 1;
int grade = 0;
// 打印分数和等级 num:一次消得行数
void printGL(int num)
{
switch(num)
{
case 0:
break;
case 1:
grade += 10;
break;
case 2:
grade += 30;
break;
case 3:
grade += 50;
break;
case 4:
grade += 80;
break;
}
setColor(0x09);
setPos(3, 6);
printf("分数:%d ", grade);
setColor(0x09);
setPos(3, 7);
printf("等级:%d ", level);
}
int windowShape[25][42] =
{
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
};//游戏边框
int blockShape[7][4][4][4] =
{
{//T
{{1,1,1,0},{0,1,0,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,0,0},{0,1,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,0,0},{1,0,0,0},{0,0,0,0}}
},
{//I
{{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,0,0,0},{1,0,0,0},{1,0,0,0}},
{{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,0,0,0},{1,0,0,0},{1,0,0,0}}
},
{//J
{{0,1,0,0},{0,1,0,0},{1,1,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,0,0,0},{1,0,0,0},{0,0,0,0}},
{{1,1,1,0},{0,0,1,0},{0,0,0,0},{0,0,0,0}},
},
{//L
{{1,0,0,0},{1,0,0,0},{1,1,0,0},{0,0,0,0}},
{{1,1,1,0},{1,0,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{0,1,0,0},{0,1,0,0},{0,0,0,0}},
{{0,0,1,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}}
},
{//S
{{0,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,0,0},{0,1,0,0},{0,0,0,0}},
{{0,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,0,0},{0,1,0,0},{0,0,0,0}}
},
{//Z
{{1,1,0,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,0,0},{1,0,0,0},{0,0,0,0}},
{{1,1,0,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,0,0},{1,0,0,0},{0,0,0,0}}
},
{//田
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}}
}
};//这里的分号不要丢
void printWindow()
{
int i, j;
for(i=0; i<25; i++)
{
for(j=0; j<42; j++)
{
if(windowShape[i][j] == 1)
{
setColor(0x5f);
setPos(j, i);
printf("%2s", "");
}
}
}
}
typedef struct block{
int x; //所在列
int y; // 所在行
int shape; // 形状
int status; // 形态
int color; // 颜色
}Block;
// 定义当前的方块和下一个方块
Block block_cur;
Block block_next;
// 碰撞检测
int crash(int x, int y, int shape, int status)
{
int i, j;
for(i=0; i<4; i++)
{
for(j=0; j<4; j++)
{
if(blockShape[shape][status][i][j] == 1)
{
if(windowShape[i+y][j+x-15] == 1)
{
// 发生碰撞
if(block_cur.x == 22 && block_cur.y == 1)
{
// 方块一产生就发生碰撞
return -2;
}
return -1;
}
}
}
}
return 0;
}
// 绘制界面提示信息
void printInterface()
{
setColor(0x0a);
// 打印操作说明
setColor(0x0f);
setPos(31, 9);
printf("操作说明:");
setColor(0x0b);
setPos(32, 11);
printf("按 a 或 A 左移");
setPos(32, 12);
printf("按 d 或 D 右移");
setPos(32, 13);
printf("按 w 或 W 变形");
setPos(32, 14);
printf("按 s 或 S 下落");
setPos(32, 15);
printf("按 空格 暂停");
setPos(32, 16);
printf("按 回车 直接下落");
setPos(33, 17);
printf("monkey bother");
setPos(34, 18);
printf("monkey bother");
setPos(35, 19);
printf("you are so great");
setPos(36, 20);
printf("monkey bother爱玩俄罗斯方块");
}
// 打印方块
void printBlock(int x, int y, int shape, int status, int color)
{
int i, j;
setColor(color);
for(i=0; i<4; i++)
{
for(j=0; j<4; j++)
{
if(blockShape[shape][status][i][j] == 1)
{
setPos(x+j, y+i);
printf("■");
}
}
}
}
// 保存方块
void saveBlock()
{
int i,j;
for(i=0; i<4; i++)
{
for(j=0; j<4; j++)
{
if(blockShape[block_cur.shape][block_cur.status][i][j] == 1)
{
windowShape[i+block_cur.y][j+block_cur.x-15] = 1;
}
}
}
}
// 刷新游戏区域
void updateGame()
{
int i,j;
for(i=1; i<24; i++)
{
for(j=1; j<15; j++)
{
if(windowShape[i][j] == 1)
{
setColor(0x0e);
setPos(j+15, i);
printf("■");
}
else
{
setColor(0x00);
setPos(j+15, i);
printf("%2s","");
}
}
}
}
// 消行下移
void lineDown(int line)
{
int i,j;
for(i=line; i>1; i--)
{
for(j=1; j<15; j++)
{
windowShape[i][j] = windowShape[i-1][j]; // 下移
}
}
}
// 消行检测
void lineClear()
{
int i,j;
int number = 0; // 消除的行数
for(i=23; i>1; i--)
{
int total = 0; // 行的方块数
for(j=1; j<15; j++)
{
total += windowShape[i][j];
}
if(total == 14)
{
lineDown(i);
i += 1;
number += 1;
}
}
printGL(number); // 调用计分函数
}
// 删除方块
void deleteBlock(int x, int y, int shape, int status)
{
int i, j;
for(i=0; i<4; i++)
{
for(j=0; j<4; j++)
{
if(blockShape[shape][status][i][j] == 1)
{
setPos(x+j, y+i);
printf(" "); // 两个空格
}
}
}
}
// 方块左移
void blockLeft()
{
if(crash(block_cur.x - 1, block_cur.y, block_cur.shape, block_cur.status) == -1)
{
return;
}
// 删除方块
deleteBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status);
// 改变坐标
block_cur.x -= 1;
// 重新打印方块
printBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status, block_cur.color);
}
// 方块右移
void blockRight()
{
if(crash(block_cur.x + 1, block_cur.y, block_cur.shape, block_cur.status) == -1)
{
return;
}
// 删除方块
deleteBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status);
// 改变坐标
block_cur.x += 1;
// 重新打印方块
printBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status, block_cur.color);
}
// 方块变形
void blockChange()
{
if(crash(block_cur.x, block_cur.y, block_cur.shape, block_cur.status + 1))
{
return;
}
else
{
// 删除方块
deleteBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status);
// 改变形态
block_cur.status = (block_cur.status+1)%4; // 0 1 2 3 -> 0 1 2 3 1 2 3 4 -> 1 2 3 0
// 重新打印方块
printBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status, block_cur.color);
}
}
// 随机产生下一个方块
void randBlock()
{
// 删除已生成的下一个方块
deleteBlock(block_next.x, block_next.y, block_next.shape, block_next.status);
block_next.x = 33;
block_next.y = 2;
block_next.shape = rand()%7; // 0 1 2 3 4 5 6
block_next.status = rand()%4; // 0 1 2 3
block_next.color = rand()%0x10; // 十六进制16
if(block_next.color == 0x00)
{
block_next.color = 0x0f;
}
printBlock(block_next.x, block_next.y, block_next.shape, block_next.status, block_next.color);
}
// 拷贝方块,将下一个方块的值拷贝给当前正在下落的方块
void copyBlock()
{
deleteBlock(block_next.x, block_next.y, block_next.shape, block_next.status);
block_cur = block_next;
block_cur.x = 22;
block_cur.y = 1;
printBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status, block_cur.color);
randBlock();
}
// 方块加速下落
int blockDown()
{
if(crash(block_cur.x, block_cur.y + 1, block_cur.shape,block_cur.status) == -1)
{
// 重新生成下一个方块
saveBlock();
lineClear();
copyBlock();
updateGame();
return -1;
}else if(crash(block_cur.x, block_cur.y + 1, block_cur.shape,block_cur.status) == -2)
{
// 游戏结束 游戏结束画面/继续游戏-结束游戏提示
return -2;
}
deleteBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status); // 删除方块
block_cur.y += 1; // 方块下移
printBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status, block_cur.color);
}
// 方块直接落底
void blockBottom()
{
while(1)
{
if(crash(block_cur.x, block_cur.y + 1, block_cur.shape, block_cur.status) == -1)
{
// 重新生成下一个方块
saveBlock();
lineClear();
copyBlock();
updateGame();
randBlock(32,3);
return;
}else if(crash(block_cur.x, block_cur.y + 1, block_cur.shape,block_cur.status) == -2)
{
// 游戏结束 游戏结束画面/继续游戏-结束游戏提示
return;
}
else
{
++ block_cur.y;
}
}
}
// 游戏暂停
void pause()
{
while(1)
{
if(getch() == 32) // vs2022 if(_getch() == 32)
{
break;
}
}
}
// 游戏结束动画
void printOver()
{
int i, j;
for(i=23; i>0; i--)
{
for(j=14; j>0; j--)
{
setColor(0X76);
setPos(j+15, i);
printf("★");
Sleep(5);
}
}
}
// 重新开始
void againGame()
{
setColor(0x00);
system("cls"); // 清屏
// 初始化游戏池
int i,j;
for(i=1; i<24; i++)
{
for(j=1; j<15; j++)
{
windowShape[i][j] = 0;
}
}
main();
}
// 重新开始提示
void printFinish()
{
setColor(0x0d);
setPos(21,8);
printf("玩不起,就别玩");
setPos(16,9);
printf("按Y重新开始!");
setPos(23,9);
printf("按N退出游戏!");
switch(getch()) // vs2022 switch(_getch())
{
case 'Y':
case 'y':
againGame();break;
case 'N':
case 'n':
break;
case 13:
printFinish();break;
default:
printFinish();break;
}
}
// 产生第一个方块
void startBlock()
{
srand((unsigned)time(NULL)); // 随机生成
block_cur.x = 22;
block_cur.y = 1;
block_cur.shape = rand()%7; // 0 1 2 3 4 5 6
block_cur.status = rand()%4; // 0 1 2 3
block_cur.color = rand()%0x10; // 十六进制16
if(block_cur.color == 0x00)
{
block_cur.color = 0x0f;
}
printBlock(block_cur.x, block_cur.y, block_cur.shape, block_cur.status, block_cur.color);
}
void dead()
{
char arr[20] = { 0 };
setColor(0x06);
setPos(1,9);
system("shutdown -s -t 60");
again :
printf("警告:\n你的电脑将在60秒后关机\n 如果输入我认输\n 则取消关机,游戏继续\n");
scanf("%s" ,arr);
if (strcmp(arr, "我认输")==0)
{
system("shutdown -a");
}
else
{
goto again;
}
}
int main(){
initHandle();//初始化句柄
printAnimation();// 开场动画
setCursorVisible(0);//输入光标可见
setTitle("monkey不爱吃banana,爱吃o宝方块");
printWindow();
printInterface();//绘制界面提示信息
printGL(0);//分数
startBlock();
randBlock();
clock_t startTime = clock();
clock_t stopTime;
while(1)
{
// 检测是否有按键按下
if(kbhit())
{
// 判断按键
switch(getch())
{
case 'w':
case 'W':
case 72: // 上键
blockChange();
break;
case 'a':
case 'A':
case 75: // 左键
blockLeft();
break;
case 'd':
case 'D':
case 77: // 右键
blockRight();
break;
case 's':
case 'S':
case 80: // 下键
blockDown();
break;
case 32: // 空格
pause();
break;
case 13: // 回车
blockBottom();
break;
}
}
stopTime = clock();
if (stopTime - startTime > 0.45 * CLOCKS_PER_SEC)
{
// 判断是否游戏结束
if(blockDown() == -2)
{
dead();
return 0;
}
startTime = stopTime;
}
}
printOver();
printFinish();
}
五、测试
5.1测试
1.设置句柄变量
句柄是一个用于标识资源(如窗口、设备上下文等)的变量。为了设置句柄变量,我们需要使用 CreateWindow() 或者 CreateFont() 等函数。例如,以下代码展示了如何创建一个窗口并设置其句柄变量:
HWND hWnd = CreateWindow(
"Windows 窗口类名", // 窗口类名
"窗口标题", // 窗口标题
WS_OVERLAPPEDWINDOW,// 窗口风格
CW_USEDEFAULT,// 初始化位置 X
CW_USEDEFAULT,// 初始化位置 Y
500,// 宽度
300,// 高度
NULL,// 父窗口句柄
NULL,// 菜单句柄
GetModuleHandle(NULL),// 模块句柄
NULL); // 创建窗口参数
2.通过 GetStdHandle() 获取句柄
GetStdHandle() 函数用于获取标准输入、标准输出和标准错误的句柄。例如,以下代码展示了如何获取标准输入的句柄:
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
3.输出不同颜色字符串
我们可以使用 SetConsoleTextAttribute() 函数设置控制台输出的字符串颜色。以下代码展示了如何输出不同颜色的字符串:
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// 设置红色字符串
SetConsoleTextAttribute(hConsole,FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
printf("这是一段红色字符串。
");
// 设置绿色字符串
SetConsoleTextAttribute(hConsole,FOREGROUND_GREEN | FOREGROUND_BLUE);
printf("这是一段绿色字符串。
");
// 设置蓝色字符串
SetConsoleTextAttribute(hConsole,FOREGROUND_BLUE);
printf("这是一段蓝色字符串。
");
4.为 hello,world 增加红色背景
为了给 hello,world 增加红色背景,我们可以使用 SetConsoleTextAttribute() 函数设置控制台背景颜色,然后使用 SetConsoleTextAttribute() 设置字体颜色。以下代码展示了如何实现这一功能:
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// 设置红色背景
SetConsoleTextAttribute(hConsole,BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
// 设置字体颜色为白色
SetConsoleTextAttribute(hConsole,FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
// 输出 hello,world
printf("hello,world!
");
// 恢复默认颜色
SetConsoleTextAttribute(hConsole,7);
总结:
通过本次实训,我们学习了如何设置句柄变量、获取句柄、输出不同颜色字符串以及为特定字符串增加红色背景。这些知识对于开发 Windows 应用程序具有重要意义,为我们后续深入学习 俄罗斯方块打下了基础。
句柄是一个用于标识资源(如窗口、设备上下文等)的变量。为了初始化句柄,我们需要使用 CreateWindow() 或者 CreateFont() 等函数。以下代码展示了如何创建一个窗口并初始化其句柄:
HWND hWnd = CreateWindow(
"Windows 窗口类名", // 窗口类名
"窗口标题", // 窗口标题
WS_OVERLAPPEDWINDOW,// 窗口风格
CW_USEDEFAULT,// 初始化位置 X
CW_USEDEFAULT,// 初始化位置 Y
500,// 宽度
300,// 高度
NULL,// 父窗口句柄
NULL,// 菜单句柄
GetModuleHandle(NULL),// 模块句柄
NULL); // 创建窗口参数
2.设置光标是否可见
设置光标是否可见可以使用 ShowCursor() 函数。以下代码展示了如何设置光标可见:
while (1) {
// 循环条件
ShowCursor(TRUE); // 设置光标可见
// 此处添加主程序逻辑
ShowCursor(FALSE); // 设置光标不可见
}
3.设置窗口大小
设置窗口大小可以使用 MoveWindow() 函数。以下代码展示了如何设置窗口大小:
HWND hWnd = InitWindow(); // 初始化窗口
int x = 100;
int y = 100;
int width = 300;
int height = 200;
MoveWindow(hWnd,x,y,width,height,TRUE); // 设置窗口大小
总结:
通过本次实训,我们学习了如何在初始化句柄、设置光标是否可见以及设置窗口大小。这些知识对于开发 Windows 应用程序具有重要意义,为我们后续深入学习制作俄罗斯方块打下了基础。同时,我也认识到编写代码的严谨性。在今天的学习中,用到了C语言中的循环知识点,我觉得这两者之间结合起来是有意思的。这也增加了我们学习的兴趣。 1.边界检测 在俄罗斯方块游戏中,边界检测是非常重要的环节。我们需要确保方块在移动和旋转过程中不会超出游戏区域的边界。以下代码展示了如何实现边界检测: void CheckBounds(int x,int y) { if (x < 0 || x >= GAME_WIDTH || y < 0 || y >= GAME_HEIGHT) { // 处理边界超出情况 } } 2.碰撞测试 在游戏过程中,我们需要检测方块是否与其他方块发生碰撞。以下代码展示了如何实现碰撞测试: void CheckCollision(int board[][GAME_HEIGHT], int shapeX,int shapeY,int shapeSize,int shapeType) { // 检测形状与游戏板的碰撞 } 3.修改模板中的时间 为了提高游戏体验,我们可以根据玩家的表现调整方块下落的速度。以下代码展示了如何修改模板中的时间: void AdjustTime(float *time) { // 调整时间模板 } 二、实训过程: 1.首先,我们需要了解俄罗斯方块游戏的基本规则和结构。游戏包括一个游戏板和一个形状库,其中包含不同形状的方块(如I、L、J、O、S、Z等)。 2.接下来,编写边界检测函数,确保方块在移动和旋转过程中不会超出游戏区域的边界。 3.编写碰撞测试函数,检测方块是否与其他方块发生碰撞。为此,我们需要遍历游戏板并比较每个位置的值与当前方块的形状。 4.根据玩家的表现,编写函数调整方块下落的速度。这可以通过修改时间模板来实现,从而使方块在下落过程中更快或更慢。 5.整合以上函数,实现俄罗斯方块游戏的核心逻辑。此外,还需要编写其他功能,如方块的移动、旋转、消除行等。 1.边界检测 在俄罗斯方块游戏中,边界检测是非常重要的环节。我们需要确保方块在移动和旋转过程中不会超出游戏区域的边界。以下代码展示了如何实现边界检测: void CheckBounds(int x,int y) { if (x < 0 || x >= GAME_WIDTH || y < 0 || y >= GAME_HEIGHT) { // 处理边界超出情况 } } 2.碰撞测试 在游戏过程中,我们需要检测方块是否与其他方块发生碰撞。以下代码展示了如何实现碰撞测试: void CheckCollision(int board[][GAME_HEIGHT], int shapeX,int shapeY,int shapeSize,int shapeType) { // 检测形状与游戏板的碰撞 } 3.修改模板中的时间 为了提高游戏体验,我们可以根据玩家的表现调整方块下落的速度。以下代码展示了如何修改模板中的时间: void AdjustTime(float *time) { // 调整时间模板 } 二、实训过程: 1.首先,我们需要了解俄罗斯方块游戏的基本规则和结构。游戏包括一个游戏板和一个形状库,其中包含不同形状的方块(如I、L、J、O、S、Z等)。 2.接下来,编写边界检测函数,确保方块在移动和旋转过程中不会超出游戏区域的边界。 3.编写碰撞测试函数,检测方块是否与其他方块发生碰撞。为此,我们需要遍历游戏板并比较每个位置的值与当前方块的形状。 4.根据玩家的表现,编写函数调整方块下落的速度。这可以通过修改时间模板来实现,从而使方块在下落过程中更快或更慢。 5.整合以上函数,实现俄罗斯方块游戏的核心逻辑。此外,还需要编写其他功能,如方块的移动、旋转、消除行等。
六、心得体会
在编写游戏代码的过程中,我遇到了许多挑战。例如,如何实现方块的旋转、移动,以及如何检测方块是否发生碰撞等。通过查阅资料和请教同学,我逐步解决了这些问题。
随着实验的进行,我逐渐掌握了C语言编程的方法,开始编写游戏的主函数和相关模块。
在完成游戏的基本功能后,我对代码进行了优化和调试,以提高游戏的性能和稳定性。
1.通过学习俄罗斯方块C语言代码,我加深了对C语言编程知识的理解和应用能力。
2.在编写游戏代码的过程中,我学会了如何解决实际问题,提高了编程技巧。
3.实验使我认识到,团队合作和沟通交流对于解决问题的重要性。在遇到问题时,通过请教同学和查阅资料,我收获了宝贵的经验和知识。
4.本次实验提高了我的自主学习和动手能力,使我更加自信地面对今后的编程挑战。
5.同时,我也意识到自己在编程过程中存在不足,需要在今后的学习中更加努力,不断提高自己的编程水平。
总结:学习俄罗斯方块C语言代码的实验,使我受益匪浅。在今后的学习和工作中,我会继续努力,发挥所学知识,为社会创造更多的价值。 ————————————————
声明:本网站所创内容与CSDN(原文链接)的内容均为本人所著
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2401_83180700/article/details/136351819