【C++\Qt项目实战】俄罗斯方块

俄罗斯方块

  • 1 项目简介
  • 2 效果展示
  • 3 代码实现
    • 3.1 框架
    • 3.2 UI界面
    • 3.3 核心代码
      • 3.3.1 TetrisGameWindow.h
      • 3.3.2 TetrisGameWindow.cpp
      • 3.3.3 Subject.h
      • 3.3.4 Subject.cpp
      • 3.3.5 TetrisGame.h
      • 3.3.6 TetrisGame.cpp
  • 4 运行效果

1 项目简介

本项目灵感来自经典的俄罗斯方块游戏(Tetris),该游戏由Alexey Pajitnov于1984年开发。俄罗斯方块以其简单而富有挑战性的游戏机制广受欢迎,成为了许多平台上的经典游戏。随着现代开发工具的进步,使用Qt框架重新实现这一经典游戏不仅是对经典的致敬,也是对个人编程技能的一次提升。
《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。
a.按方向键的左右键可实现方块的左右移动;
b. 按方向键的下键可实现方块的加速下落;
c.按方向键的上键可实现方块的变形。

2 效果展示

样图示例

3 代码实现

3.1 框架

在这里插入图片描述

3.2 UI界面

在这里插入图片描述

3.3 核心代码

3.3.1 TetrisGameWindow.h

#ifndef TETRISGAMEWINDOW_H
#define TETRISGAMEWINDOW_H#include <QWidget>
#include <QPoint>
#include "core/Subject.h"namespace Ui {
class TetrisGameWindow;
}
class QTimer;namespace restonce {
class TetrisGame;
}class TetrisGameWindow: public QWidget, public restonce::Observer
{Q_OBJECTpublic:explicit TetrisGameWindow(QWidget *parent = 0);~TetrisGameWindow();
protected:void paintEvent(QPaintEvent *) override final;void keyPressEvent(QKeyEvent *) override final;virtual void onSubjectChanged() override final;
private slots:void on_pushButton_clicked();void slot_timeout();
private:Ui::TetrisGameWindow *ui;restonce::TetrisGame *m_game;int m_boxSize = 24;QPoint m_basePosition = QPoint(10, 10);QPoint m_baseNextPosition = QPoint(200, 240);QTimer *m_timer;
};#endif // TETRISGAMEWINDOW_H

3.3.2 TetrisGameWindow.cpp

#include "TetrisGameWindow.h"
#include "ui_TetrisGameWindow.h"
#include "core/TetrisGame.h"
#include "core/RandomBox.h"
#include <QPainter>
#include <QTimer>
#include <QKeyEvent>TetrisGameWindow::TetrisGameWindow(QWidget *parent) :QWidget(parent),ui(new Ui::TetrisGameWindow)
{ui->setupUi(this);m_game = new restonce::TetrisGame;m_timer = new QTimer(this);connect(m_timer, SIGNAL(timeout()),this, SLOT(slot_timeout()));this->setFixedSize(this->size());m_game->attachObserver(this);
}void TetrisGameWindow::slot_timeout()
{m_game->timeout();
}TetrisGameWindow::~TetrisGameWindow()
{delete ui;delete m_game;
}void TetrisGameWindow::on_pushButton_clicked()
{m_timer->start(1000);m_game->start();
}void TetrisGameWindow::paintEvent(QPaintEvent *)
{switch(m_game->getGameStatus()){case restonce::TetrisGame::GameStatus::runing:ui->label->setText("正在游戏");ui->pushButton->setEnabled(false);break;case restonce::TetrisGame::GameStatus::stop:ui->label->setText("游戏结束");ui->pushButton->setEnabled(true);break;case restonce::TetrisGame::GameStatus::undo:ui->label->clear();ui->pushButton->setEnabled(true);break;}QPainter painter(this);QPoint p2(m_basePosition.x()+ m_boxSize*restonce::TetrisGame::ROW+1,m_basePosition.y() -1);QPoint p3(m_basePosition.x()-1,m_basePosition.y() +m_boxSize*restonce::TetrisGame::LINE+1);QPoint p4(m_basePosition.x() + m_boxSize*restonce::TetrisGame::ROW+1,m_basePosition.y() + m_boxSize*restonce::TetrisGame::LINE+1);QPoint p1(m_basePosition.x()-1, m_basePosition.y()-1);painter.drawLine(p1, p2);painter.drawLine(p2, p4);painter.drawLine(p4, p3);painter.drawLine(p1, p3);for(int l=0; l<restonce::TetrisGame::LINE; ++l) {for(int r=0; r<restonce::TetrisGame::ROW; ++r) {QPoint p(m_basePosition.x() + r*m_boxSize,m_basePosition.y() + l*m_boxSize);int color = 0;if(m_game->exists(l, r)){color = m_game->color(l, r);}else if(m_game->getActiveBox() && m_game->getActiveBox()->inBody(l, r)){color = m_game->getActiveBox()->color();}if(color <= 0)continue;QString imgpath = QString::asprintf(":/boxes/images/box%d.jpg", color);painter.drawImage(p, QImage(imgpath));}}std::shared_ptr<restonce::RandomBox> nextBox = m_game->getNextBox();if(nextBox) {QString imgpath = QString::asprintf(":/boxes/images/box%d.jpg", nextBox->color());for(restonce::Point const& p : nextBox->getMyBoxes()) {painter.drawImage(QPoint(m_baseNextPosition.x() +m_boxSize*p.row(),m_baseNextPosition.y() + m_boxSize*p.line()),QImage(imgpath));}}
}void TetrisGameWindow::keyPressEvent(QKeyEvent *e)
{switch(e->key()){case Qt::Key_Down:m_game->down();break;case Qt::Key_Left:m_game->left();break;case Qt::Key_Right:m_game->right();break;case Qt::Key_Up:m_game->transform();break;}
}void TetrisGameWindow::onSubjectChanged()
{repaint();
}

3.3.3 Subject.h

#ifndef RESTONCE_SUBJECT_H
#define RESTONCE_SUBJECT_H#include <set>namespace restonce {class Observer
{
protected:Observer() = default;virtual ~Observer() = default;
public:virtual void onSubjectChanged() = 0;
};class Subject
{
public :void attachObserver(Observer *o);
protected:Subject() = default;virtual ~Subject() = default;void notifyObservers() ;
private:std::set<Observer *> m_observers;
};} // namespace restonce#endif // RESTONCE_SUBJECT_H

3.3.4 Subject.cpp

#include "core/Subject.h"namespace restonce {void Subject::attachObserver(Observer *o)
{m_observers.insert(o);
}void Subject::notifyObservers()
{for(Observer *o : m_observers){o->onSubjectChanged();}
}} // namespace restonce

3.3.5 TetrisGame.h

#ifndef RESTONCE_TETRISGAME_H
#define RESTONCE_TETRISGAME_H#include <memory>
#include <random>
#include "Subject.h"namespace restonce {class RandomBox;class TetrisGame: public Subject
{
public:enum class GameStatus {undo, runing, stop};enum class WinStatus {win, lose};enum {ROW = 10,LINE = 18};TetrisGame();// 用户通过gui可以对游戏进行的操作void start();void timeout();void transform();void down();void left();void right();void stop();// gui更新时会用以下函数读取游戏状态GameStatus getGameStatus() const;WinStatus getWinStatus() const;// 是否存在方块,如果越界会抛出异常bool exists(int line, int row) const;int color(int line, int row) const;std::shared_ptr<RandomBox> getActiveBox() const;// 下一个Boxstd::shared_ptr<RandomBox> getNextBox() const;// 某位置是否越界bool valid(int line, int row) const;// 填充某个位置void set(int line, int row, int color);
private:void init();private:GameStatus m_gameStatus;WinStatus m_winStatus;bool m_map[LINE][ROW] ;int  m_colorMap[LINE][ROW] ;std::shared_ptr<RandomBox> m_activebox, m_nextBox;std::mt19937 m_rd;
};} // namespace restonce#endif // RESTONCE_TETRISGAME_H

3.3.6 TetrisGame.cpp

#include "TetrisGame.h"
#include "RandomBox.h"
#include <time.h>namespace restonce {TetrisGame::TetrisGame()
{m_gameStatus = GameStatus::undo;m_winStatus = WinStatus::lose;m_rd.seed(time(NULL));for(int l=0; l<LINE; ++l) {for(int r=0; r<ROW; ++r) {m_map[l][r] = false;}}
}void TetrisGame::init()
{for(int l=0; l<LINE; ++l) {for(int r=0; r<ROW; ++r) {m_map[l][r] = false;m_colorMap[l][r] = 0;}}m_gameStatus = GameStatus::undo;m_winStatus = WinStatus::lose;m_activebox = std::make_shared<RandomBox>(*this, m_rd);m_nextBox = std::make_shared<RandomBox>(*this, m_rd);
}void TetrisGame::start()
{if(m_gameStatus == GameStatus::runing) {throw std::logic_error("Game is runing !");}init();m_gameStatus = GameStatus::runing;notifyObservers();
}void TetrisGame::timeout()
{if(m_gameStatus != GameStatus::runing) {return  ;}if(!m_activebox->down()) {// 此处准备消行for(int line=LINE-1; line>=0; --line) {bool isFull = true;for(int row=0; row<ROW; ++row) {if(!this->exists(line, row)) {isFull = false;break;}}if(isFull) {for(int l=line; l>=0; --l) {for(int r=0; r<ROW; ++r) {if(l ==0) {m_map[l][r] = false;} else {m_map[l][r] = m_map[l-1][r];}}}++ line;}}//m_activebox =m_nextBox;m_nextBox=std::make_shared<RandomBox>(*this, m_rd);if(!m_activebox->valid()) {// 新产生的方块不合法,说明你已经输了m_gameStatus = GameStatus::stop;m_winStatus = WinStatus::lose;}}notifyObservers();
}void TetrisGame::transform()
{if(m_activebox->transform()) {notifyObservers();}
}void TetrisGame::down()
{timeout();
}void TetrisGame::left()
{if(m_activebox->left()) {notifyObservers();}
}void TetrisGame::right()
{if(m_activebox->right()) {notifyObservers();}
}void TetrisGame::stop()
{if(m_gameStatus == GameStatus::runing) {m_gameStatus = GameStatus::stop;m_winStatus = WinStatus::lose;notifyObservers();}
}TetrisGame::GameStatus TetrisGame::getGameStatus() const
{return m_gameStatus;
}TetrisGame::WinStatus TetrisGame::getWinStatus() const
{if(m_gameStatus != GameStatus::stop) {throw std::logic_error("Game is not stop !");}return m_winStatus;
}bool TetrisGame::valid(int line, int row) const
{return line >=0 && line < LINE &&row >=0 && row < ROW;
}bool TetrisGame::exists(int line, int row) const
{if(!valid(line, row)) {throw std::out_of_range("Game position not exists !");}return m_map[line][row];
}int TetrisGame::color(int line, int row) const
{return m_colorMap[line][row];
}std::shared_ptr<RandomBox> TetrisGame::getActiveBox() const
{return m_activebox;
}void TetrisGame::set(int line, int row, int color)
{m_map[line][row] = true;m_colorMap[line][row] = color;
}std::shared_ptr<RandomBox> TetrisGame::getNextBox() const
{return m_nextBox;
}} // namespace restonce

4 运行效果

运行效果

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

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

相关文章

视频智能分析平台LntonAIServer安防监控平台花屏检测、马赛克检测功能介绍

视频监控系统在现代社会中扮演着至关重要的角色&#xff0c;无论是用于安全监控、交通管理还是其他用途&#xff0c;视频的质量直接关系到系统的可靠性和有效性。LntonAIServer通过新增的视频质量诊断功能&#xff0c;包括花屏检测和马赛克检测&#xff0c;进一步增强了视频监控…

Windows系统下安装JMeter

目录 一、官网下载JMeter 二、运行 JMeter 一、官网下载JMeter JMeter 官网安装地址 Apache JMeter - Apache JMeter™https://jmeter.apache.org/ 下载Windows版本 下载完成后 解压 二、运行 JMeter 打开bin目录 下面两个文件其中一个均可运行双击jmeter.bat 或者使用…

算法入门-深度优先搜索3

第六部分&#xff1a;深度优先搜索 112.路径总和&#xff08;简单&#xff09; 题目&#xff1a;给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果…

[项目][WebServer][项目介绍及知识铺垫][上]详细讲解

目录 1.何为WWW?2.HTTP分层1.整体2.细节3.DNS?4.协议之间是如何协同运作的&#xff1f; 3.Http相关概念1.特点2.URI && URL && URN3.HTTP URL格式 1.何为WWW? WWW是环球信息网的缩写&#xff0c;常简称为Web分为Web客户端和Web服务器程序&#xff0c;WWW可…

简单计算机网络概念

1.浏览器过程 输入url&#xff0c;解析url 1.协议http、https的区别&#xff1b;HTTPS就是在HTTP与TCP之间增加了SSL/TSL安全传输层 2.格式&#xff1a;协议//主机:端口/路径&#xff1b; 3.HTTP版本&#xff1a;1.0和1.1 4.HTTP/1.1&#xff1a;1. 持久连接&#xff1a;为了…

数据结构————单链表

引言 在计算机科学的领域里&#xff0c;数据结构的探索与应用是程序设计的灵魂。单链表&#xff0c;作为一种基础而灵活的数据结构&#xff0c;不仅在理论上有着丰富的内涵&#xff0c;其在实际编程中的应用亦是广泛而深远。本文旨在深入浅出地介绍单链表的实现过程&#xff0c…

探探我对测试开发的看法?

测试开发岗位主要负责确保软件的可用性和稳定性。 ● 可用性不仅包括功能的正常使用&#xff0c;还涵盖了软件在不同环境下的兼容性&#xff0c;如各种网络环境、不同 CPU 核心环境以及多样化的移动端设备等。 ● 稳定性方面我的理解是&#xff0c;测试人员不仅要从用户角度评判…

OpenAI gym: How to get complete list of ATARI environments

题意&#xff1a;OpenAI Gym&#xff1a;如何获取完整的 ATARI 环境列表 问题背景&#xff1a; I have installed OpenAI gym and the ATARI environments. I know that I can find all the ATARI games in the documentation but is there a way to do this in Python, witho…

UE5 半透明阴影 快速解决方案

Step 1&#xff1a; 打开该选项 Step 2&#xff1a; 将半透明材质给到模型后&#xff0c;设置光照的Shadow Resolution Scale&#xff0c;越大&#xff0c;阴影的效果越好 Step 3&#xff1a; 用这种方式去做&#xff0c;阴影会因为半透明的程度&#xff0c;降低阴影的浓度 要…

使用Azure+C#+visual studio开发图像目标检测系统

在这篇文章里面&#xff0c;我们讲解使用AzureC#visual studio在Azure上做图像的目标检测系统。 笔者是头一次接触C#。之前以Python Java和Scala为主。感觉C#.Net是一种挺好用的开发系统。C#和Java非常像。会一个学另一个很快。 首先&#xff0c;目标检测是个什么东西&#x…

【高校主办,EI稳定检索】2024年人机交互与虚拟现实国际会议(HCIVR 2024)

会议简介 2024年人机交互与虚拟现实国际会议&#xff08;HCIVR 2024&#xff09;定于2024年11月15-17日在中国杭州召开&#xff0c;会议由浙江工业大学主办。人机交互&#xff0c;虚拟现实技术的发展趋势主要体现在系统将越来越实际化&#xff0c;也越来越贴近人类的感知和需求…

python-新冠病毒

题目描述 假设我们掌握了特定时间段内特定城市的新冠病毒感染病例的信息。在排名 i 的当天有 i 个案例&#xff0c;即&#xff1a; 第一天有一例感染第二天有两例感染第三天有三例感染以此类推...... 请计算 n 天内的感染总数和每天平均感染数。 输入 整数 n 表示天数&…

将星 x17 安装ubuntu 20.04 双系统

准备工作&#xff0c;包含关闭快速启动&#xff0c;关闭Secret Boot 1.进入控制面板选择小图标&#xff0c;找到电源选项 2.点击更改当前不可用的设置&#xff0c;关闭快速启动 3.开机启动时快速按F2&#xff0c;进入BIOS 4.选择Setup Utiltity&#xff0c;选择Security&#…

LeetCode 热题 100 回顾5

干货分享&#xff0c;感谢您的阅读&#xff01;原文见&#xff1a;LeetCode 热题 100 回顾_力code热题100-CSDN博客 一、哈希部分 1.两数之和 &#xff08;简单&#xff09; 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标…

ArcGIS之建模处理栅格数据以表格显示分区统计(以夜间灯光数据为例)

当需要计算一个shp数据中多个面中的栅格数据值是&#xff0c;可以通过模型构建器进行批量处理&#xff0c;也就是统计多个面中的栅格数据值。但在处理过程中可能会遇见不同的错误&#xff0c;本文会介绍ERROR000883的解决办法。 数据准备&#xff1a;一个shp数据&#xff08;例…

Idea 创建 Maven项目的时候卡死

文章目录 一、Archetype 和 Catalog1.1 Archetype&#xff08;原型&#xff09;1.2 Catalog&#xff08;目录&#xff09; 二、可能遇到的问题2.1 问题描述2.2 原因分析2.3 解决方案 参考资料 一、Archetype 和 Catalog 1.1 Archetype&#xff08;原型&#xff09; Archetype…

私域电商 IP 化发展的探索与优势

摘要&#xff1a;本文聚焦于私域电商与社交电商的区别&#xff0c;重点探讨私域电商的 IP 属性。深入分析其在获取流量、转化用户以及挖掘用户价值方面的独特优势。同时引入链动 2 1 模式、AI 智能名片、S2B2C 商城小程序源码等元素&#xff0c;详细阐述这些元素在私域电商 IP…

C++——哈希

目录 1.undered系列容器 1.1 undered_map 1.1.1 undered_map特点介绍 1.1.2 undered_map接口介绍 1.2 undered_set 2.底层结构 2.1 哈希概念 2.2 哈希冲突 2.3 哈希函数 2.3.1 哈希函数设计原则&#xff1a; 2.3.2 常见哈希函数 1.直接定值法 2.除留余数法 3.平方…

数学建模笔记——层次分析法

数学建模笔记——层次分析法 数学建模笔记——层次分析法1. 层次分析法的基本原理和步骤2. 层次分析法的建模过程2.1 问题的提出2.2 模型原理2.3 为该问题建立层次结构模型2.4 构造判断矩阵1. 判断矩阵的含义2. 为该问题构造判断矩阵 2.5 一致性检验1. 一致性检验方法2. 对上述…

相机内存卡格式化了照片怎么恢复?格式化恢复详解

摄影爱好者们都知道&#xff0c;相机内存卡是记录我们美好瞬间的重要媒介。然而&#xff0c;在使用过程中&#xff0c;有时我们会因操作不当或设备故障&#xff0c;不小心格式化了内存卡&#xff0c;从而导致珍贵的照片丢失。面对这种情况&#xff0c;我们该如何恢复这些被格式…