基于C++实现的(控制台)双人俄罗斯方块小游戏

基于win32控制台应用程序的双人俄罗斯方块小游戏

1. 课题概述

1.1 课题目标和主要内容

使用visual studio 2015在win32控制台应用程序下用多线程实现双人同时进行俄罗斯方块的桌面游戏。最终将要完成的效果如图1.1所示,左右共两片工作区,也是游戏的主题;工作区旁边是记录当前游戏的级别和分数以及下一个掉落方块类别,根据工作区的不同各自通过键盘上的’w’, ’a’, ’ s’, ’d’和’↑’, ’←’, ’↓’, ’→’来控制方块的旋转、下移、左移、右移。

1.2 系统的主要功能

  • 能够同时两个人进行俄罗斯方块的对战
  • 每个俄罗斯方块具备基本的旋转、左移、右移和下落操作
  • 当其中一个俄罗斯方块消掉一行时,另一个俄罗斯方块将增加一行
  • 能够调节方块的下落速度
  • 能够保存玩家的历史最高分
  • 双重模式供玩家选择
  • 能够根据模式的不同播放音乐

2. 系统设计

2.1 系统总体框架

俄罗斯方块游戏的设计思路遵循MVC设计模式,该模式是一种使用Model-View-Controller(模型-视图-控制器)设计创建应用程序的模式。MVC框架图如图2.1所示。

  • Model(模型):表示应用程序的数据的存取
  • View(视图):显示界面,根据Model提供的不同数据,显示不同的界面
  • Controller(控制器):处理用户界面的交互,以及向模型发送更新的数据

MVC分层有助于管理复杂的应用程序,这样可以在一段时间内专门关注一个方面。同时也让应用程序的测试更加容易,并且简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。

对于俄罗斯方块游戏来说,需要的设计步骤如下:

  • 设计Model。即工作区和方块的存储结构用来方便地存储数据
  • 设计View。即如何根据数据显示不同的界面
  • 设计Controller。即如何用键盘控制数据的变化

2.2 系统详细设计

2.2.1 模块划分图及描述

  • 初始化模块:完成初始界面的显示,以及数据的初始化。该函数封装了GetStdHandle()控制台API
  • 基本的显示函数:在制定位置显示指定颜色的字符串
BOOL textout(HANDLE hOutput,       //控制台句柄
int x,int y,         //显示坐标
WORD wColors[],      //显示颜色
int nColors,         //颜色数量
LPTSTR lpszString);  //显示的字符内容
  • 方块的显示和擦除:完成在指定位置显示/擦除指定颜色、形状的方块。
    • 方块显示:在x,y坐标位置显示长和宽为w和h,颜色为wColors的方块,方块形状由数组a表示,整型数p、q确定工作区位置。
void DrawBlocks(int a[], int w, int h, int x, int y, WORD wColors[], int nColors, int p, int q);
- 方块擦除:擦除x,y,y坐标位置的长和宽为w和h,颜色为wColors的方块,方块形状由数组a表示,整型数p、q确定工作区位置。
void ClearSquare(int a[], int w, int h, int x, int y, int p, int q);
  • 方块旋转:完成每种方块4个方向的旋转。
void Turn(int a[][4], int w, int h, int *x, int y, int MAP[][MAPW]);
  • 判断方块是否能够下落。
bool IsAvailable(int a[], int x, int y, int w, int h, int MAP[][MAPW]);
  • 方块满一行后消行:
  • 参数说明:在工作区row位置的一行,然后将该行之上的方块下移。其中数组m表示工作区,整型数p、q确定工作区位置。
void DeleteLine(int m[][MAPW], int row, int p, int q);
  • 给另一工作区加行:
    参数说明:在另一工作区,将工作区方块整体上移,然后在最底下一行位置增加一行。其中数组m表示工作区,整型数p、q确定工作区位置。
void AddLine(int m[][MAPW], int row, int p, int q);
  • 获取历史最高分:在程序所在路径下生成一个MaxScore.txt文件用来记录最高分。
int GetMaxScore();
  • 整体清屏
    参数说明:默认为0,整体清空界面,参数值为1时清空左工作区内方块,参数值为2时清空右工作区内的方块。
void ClearBlank(int i=0);
  • 判断重新开始还是结束游戏
void Change();
  • 游戏说明
void GameRule();
  • 难度选择
void LevelChoose();
  • 正经模式:按照要求实现双人多线程俄罗斯方块,能够完成相关操作。
void DoubleMode();
  • 地狱模式:改变背景音乐风格,随机确定初始下落位置。其他同正经模式。
void HeheMode();
  • 模式选择:选择正经模式、地狱模式、游戏说明还是退出游戏。
void ModeAlter();
  • 游戏结束:退出游戏时界面,打印GAME OVER。
void GameOver();
  • 线程处理函数:左右工作区分别对应线程0和线程1
DWORD WINAPI ThreadFunc0(LPVOID lpParam);
DWORD WINAPI ThreadFunc(LPVOID lpParam);

2.2.2 程序流程图及描述

按照面向过程的程序设计思想,自顶向下逐步求精的方法,首先需要根据基本功能进行模块划分,设计程序流程。俄罗斯方块游戏的基本的运行模式就是根据用户的按键,显示、翻转、移动不同的方块,因此其流程结构详见附件系统主流程1,系统主流程2,键盘处理流程。

2.2.3 存储结构、内存分配

  • 工作区数据存储:游戏中可划分出两个工作区,在两个区域内方块根据操作进行移动、翻转,用两个两位数组来分别保存两个工作区中每个位置信息点的信息。程序中选用工作区的大小是12*20,由常量MAPW和MAPH表示。
# define MAPW    12     //地图的宽度
# define MAPH    20     //地图的高度
  • 方块数据存储:俄罗斯方块有各种不同的形状,基本形状有7种,每一种形状采用4*4的数组来存储,分别使用三维数组来存储。
int b[7][4][4] = { { { 1 },{ 1,1,1 } },{ { 0,2 },{ 2,2,2 } },{ { 3,3 },{ 0,3,3 } },{ { 0,0,4 },{ 4,4,4 } },{ { 0,5,5 },{ 5,5 } },{ { 6,6,6,6 } },{ { 7,7 },{ 7,7 } }};

3. 程序运行结果分析

3.1 输出显示方式

用Drawblock函数更新方块,配合Textout函数更新游戏参数。

欢迎界面

模式选择界面

正经双人模式界面

3.2 操作流程

程序开始后,进入模式选择界面,可选择游戏说明,或直接开始游戏,模式选择完毕后则选择游戏难度,之后正式开始进行游戏。游戏中中可任意按空格键暂停游戏,或任意按回车键返回模式选择界面,以及按ESC键直接退出游戏。

3.3 运行效果

整体来说程序相对流畅,基本符合要求,各种界面之间切换自然,衔接不会有太多延迟感,音乐循环播放也没有感觉到切换的卡顿。

4. 总结

4.1 课题的难点和关键点

4.1.1 线程同步

本程序中使用双线程,难点在于两个线程彼此同步调配,二者均同从键盘按键中获取键值进行相应操作,为避免二者相互冲突,故引进临界区变量,当某一线程读取键值时锁定使另一线程暂时无法读取,由于临界区锁定时间短,人难以察觉出细微变化,故不影响整体速度。

4.1.2 增加一行

当某一玩家成功消行时,对另一玩家实行加行操作。难点在于重绘上移后的工作区。仿照消行函数Deleteline()的逻辑,从上向下逐行扫描更新。一方面,由于不同于消行操作,加行时的绘制起始点行列,须认真计算,否则容易出现绘制异常;另一方面,由于绘制方块函数Drawblock()逻辑原因,对于工作区内块值为零的区域,不执行任何操作,故当该工作区内方块整体上移时,对于某些排列空隙有可能上移后被填满,所以对于块值为零区域,单独调用Textout()函数在其区域打印char[3]的空格进行擦除,但同时造成下落方块的暂时消失,可以在循环按键处理中更新下落方块,使由于擦除造成的方块闪烁感消失。

本程序中设定由于对方消行所增加的新行不在具有能够消除的属性,故要增加两个整型数分别记录增加的行,方便在调用消行函数时消去的行不在惩罚增加行之中。

4.1.3 线程结束

当游戏一方到达顶部时,游戏结束后重新开始需要将原有两个线程结束掉,该问题属于程序设计逻辑问题。解决思想如下:

当一方到达顶部时,设置该对应线程结束完成标志ok1(如线程0)和设置结束另一线程命令标志flag2,之后return结束该线程;当另一线程接收结束命令flag2后,设置该对应线程结束完成标志ok2,之后return结束该线程;

当满足两个线程结束标志ok1和ok2时将退出当前游戏模式,跳转到模式选择模式。

4.2 本课题的评价

从最初的一个子线程配合一个主线程,到现在的两个相似的子线程交叉调度;

从最初的单人模式,到现在正经双人和地狱双人模式;从只能游戏一次,到现在任意返回、任意退出、无限循环开始。可以说本课题完成费了不少时间精力,完成相对满意。但程序仍有一些不够完善的地方,例如长条形方块下落时再不足旋转空间处旋转造成旋转出工作区外、两个线程对键盘读取键值的处理不够理想,容易引起漏键的现象。希望随着编程能力的提高将来能够更好地将这些问题优化好,做成一个成熟应用程序。

4.3 心得体会

通过此次程序设计实践,一方面,对win32控制台应用程序有了新的认识,不在仅局限使用标准输入输出流cin/cout进行界面交互,通过使用API 函数可以使编程编得更加方便,增加对句柄的熟悉度;另一方面,学习了多线程编程的一些相关概念及函数调用,在debug时明白了线程之间切换的大概过程,让我debug能力得到了很好地提高。对于此类程序编写时,要注意其可扩展性,方便后期修改,模块要区分的清除很重要的一点就是,程序设计时,一定要整理后各个模块以及各个模块间衔接的逻辑关系,尽可能地避免逻辑错误。

5. 参考文献

[1] 张宏,黄小诚. VC++语言多线程编程及其实现[J]. 科技信息(科学教研). 2008(07)

[2] 韩玉坤,介龙梅. 浅谈多线程编程技术及方法[J]. 电脑学习. 2010(02)

[3] 胡吉全,李明星. 基于VC多线程技术的优化设计方法[J]. 交通与计算机. 2005(02)

[4] 王日宏. 基于VC的Win32多线程同步问题[J]. 计算机系统应用. 2004(07)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/62073.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python subprocess.run 使用注意事项,避免出现list index out of range

在执行iOS UI 自动化专项测试的时候,在运行第一遍的时候遇到了这样的错误: 2024-12-04 20:22:27 ERROR conftest pytest_runtest_makereport 106 Test test_open_stream.py::TestOpenStream::test_xxx_open_stream[iPhoneX-xxx-1-250] failed with err…

力扣1401. 圆和矩形是否有重叠

用矢量计算&#xff1a; class Solution { public:bool checkOverlap(int radius, int xCenter, int yCenter, int x1, int y1, int x2, int y2) {//矩形中心float Tx(float)(x1x2)/2;float Ty(float)(y1y2)/2;//强行进行对称操作&#xff0c;只考虑第一象限if(xCenter<Tx)…

信号和槽思维脑图+相关练习

将登录框中的取消按钮使用信号和槽的机制&#xff0c;关闭界面。 将登录按钮使用信号和槽连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是否为"123456",如果账号密码匹配成功&#xff0c;当前界面关…

第一部分:基础知识 2. SQL基础 --[MySQL轻松入门教程]

第一部分:基础知识 2. SQL基础 --[MySQL轻松入门教程] SQL(Structured Query Language)是一种用于管理和处理关系型数据库的标准语言。它被广泛应用于各种数据库系统,如MySQL, PostgreSQL, Oracle, SQL Server等。下面是一些SQL的基础知识和常用操作示例。 1.SQL简介 SQ…

《Clustering Propagation for Universal Medical Image Segmentation》CVPR2024

摘要 这篇论文介绍了S2VNet&#xff0c;这是一个用于医学图像分割的通用框架&#xff0c;它通过切片到体积的传播&#xff08;Slice-to-Volume propagation&#xff09;来统一自动&#xff08;AMIS&#xff09;和交互式&#xff08;IMIS&#xff09;医学图像分割任务。S2VNet利…

源码可运行-PHP注册登录源码,PHP实现登陆后才能访问页面

最近有一个项目需要实现会员注册和页面登陆后才能访问&#xff0c;所以简单的HTML是无法实现的&#xff0c;就必须通过PHP、html和Mysql来实现&#xff0c;先给大家看一下登录和注册页的效果图。&#xff08;注册完成后会自动跳转到登录窗口&#xff0c;即使A用户登陆后分享了网…

性能测试常见面试问题和答案

一、有没有做过性能测试&#xff0c;具体怎么做的 性能测试是有做过的&#xff0c;不过我们那个项目的性能做得不多&#xff0c;公司要求也不严格。一般SE 给我们相关的性能需求&#xff0c;首先我们需要对性能需求进行场景分析与设计&#xff0c;这里&#xff0c;其实主要就是…

二百七十八、ClickHouse——将本月第一天所在的那一周视为第一周,无论它是从周几开始的,查询某个日期是本月第几周

一、目的 ClickHouse指标表中有个字段week_of_month&#xff0c;含义是这条数据属于本月第几周。 而且将本月第一天所在的那一周视为第一周&#xff0c;无论它是从周几开始的。比如2024-12-01是周日&#xff0c;即12月第一周。而2024-12-02是周一&#xff0c;即12月第二周 二…

【OCR】——端到端文字识别GOT-OCR2.0不香嘛?

代码&#xff1a;https://github.com/Ucas-HaoranWei/GOT-OCR2.0?tabreadme-ov-file 在线demo&#xff1a;https://huggingface.co/spaces/stepfun-ai/GOT_official_online_demo 0.前言 最早做ocr的时候&#xff0c;就在想如何能做一个端到端的模型&#xff0c;就不用先检测再…

AndroidStudio-常见界面控件

一、Button package com.example.review01import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.TextViewclass Review01Activity : AppCompatActivity() {override fun onCreate(savedInstanceStat…

网络安全中的 SOC 是什么?

当今世界&#xff0c;网络威胁日益增多&#xff0c;确保网络安全已成为各种规模企业的首要任务。网络安全讨论中经常出现的一个术语是 SOC&#xff0c;即安全运营中心的缩写。但网络安全中的 SOC 是什么呢&#xff1f; SOC在防御网络威胁、管理安全事件和全天候监控系统方面发…

智选球员:运用动态规划提升棒球队的签约效益

目录 一、签约棒球自由球员 二、分析和理解 &#xff08;一&#xff09;问题背景回顾 &#xff08;二&#xff09;目标确定 &#xff08;三&#xff09;约束条件分析 &#xff08;四&#xff09;明确输出要求 三、动态规划&#xff08;Dynamic Programming&#xff09;解…

Lua面向对象实现

Lua中的面向对象是通过表&#xff08;table&#xff09;来模拟类实现的&#xff0c;通过setmetatable(table,metatable)方法&#xff0c;将一个表设置为当前表的元表&#xff0c;之后在调用当前表没有的方法或者键时&#xff0c;会再查询元表中的方法和键&#xff0c;以此来实现…

航空美食新升级,光明肉业携手东航食品打造经典辣肉面新篇章

在航空餐饮日益注重品质与创新的大环境下&#xff0c;各大航空公司纷纷在美食领域下功夫&#xff0c;力求为乘客提供更加多元化、高品质的餐饮体验。近日&#xff0c;东航那碗面再次成为行业焦点&#xff0c;其经典辣肉面在光明肉业的助力下实现了“创新”升级&#xff0c;为乘…

在.NET 6中使用Serilog收集日志

此示例的完整详细信息&#xff1a;https://download.csdn.net/download/hefeng_aspnet/89998498 Serilog 是一个日志库&#xff0c;它提供对文件、控制台和其他几个地方的记录。它易于配置&#xff0c;并且具有干净且易于使用的界面。 Serilog具有无与伦比的输出目的地选择&…

STM32 HAL库开发学习5. 系统滴答定时器

STM32 HAL库开发学习5. 系统滴答定时器 一、滴答定时器概述1. 概述2. 时钟源3. 寄存器&#xff08;1&#xff09;控制与状态寄存器 STK_CTRL&#xff08;2&#xff09;重载寄存器 STK_LOAD&#xff08;3&#xff09;当前值寄存器 STK_VAL 二、HAL库滴答定时器初始化三、SysTick…

智慧零售时代传统供应链的痛点与数字化变革:定制开发 AI 智能名片 S2B2C 商城系统的应用与影响

摘要&#xff1a;本文深入探讨了智慧零售时代传统供应链所面临的痛点&#xff0c;分析了在该时代背景下打通线上线下渠道以及整合资源的关键需求。阐述了传统零售供应链若线上线下渠道独立、数据不流通将导致的库存管理与物流效率问题&#xff0c;并强调其必须进行数字化变革以…

国家信息中心单志广:智慧城市转型中的数据要素价值释放

今日&#xff0c;由中国电信集团主办的2024数字科技生态大会数据要素合作论坛在广州市举办。国家发改委国家信息中心信息化和产业发展部主任单志广在论坛发展主旨演讲&#xff1a;智慧城市转型中的数据要素价值释放&#xff0c;主要包括发展新形势、数据新要素、数据新产权、数…

从零开始使用GOT-OCR2.0——多模态OCR项目:微调数据集构建 + 训练(解决训练报错,成功实验微调训练)

在上一篇文章记录了GOT-OCR项目的环境配置和基于官方模型参数的基础使用。环境安装的博文快速链接&#xff1a; 从零开始使用GOT-OCR2.0——多模态通用型OCR&#xff08;非常具有潜力的开源OCR项目&#xff09;&#xff1a;项目环境安装配置 测试使用-CSDN博客 本章在环境配置…

【在Linux世界中追寻伟大的One Piece】读者写者问题与读写锁

目录 1 -> 读者写者问题 1.1 -> 什么是读者写者问题 1.2 -> 读者写者与生产消费者的区别 1.3 -> 如何理解读者写者问题 2 -> 读写锁 2.1 -> 读写锁接口 3 -> 读者优先(Reader-Preference) 4 -> 写者优先(Writer-Preference) 1 -> 读者写者…