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

news2024/11/13 16:20:24

俄罗斯方块

  • 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_OBJECT

public:
    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;
    // 下一个Box
    std::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.coloradmin.cn/o/2120736.html

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

相关文章

1963Springboot个性化音乐推荐管理系统idea开发mysql数据库web结构java编程计算机网页源码maven项目

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…

【Chain-of-Thought Prompting】链式思考(CoT)提示、零样本 COT 提示、自动思维链(Auto-CoT)

链式思考&#xff08;CoT&#xff09;提示 图片来源&#xff1a;Wei等人&#xff08;2022&#xff09;(opens in a new tab) 在 Wei等人&#xff08;2022&#xff09;(opens in a new tab) 中引入的链式思考&#xff08;CoT&#xff09;提示通过中间推理步骤实现了复杂的推理…

QQ频道机器人零基础开发详解(基于QQ官方机器人文档)[第三期]

QQ频道机器人零基础开发详解(基于QQ官方机器人文档)[第三期] 第三期介绍&#xff1a;频道模块之频道成员 目录 QQ频道机器人零基础开发详解(基于QQ官方机器人文档)[第三期]第三期介绍&#xff1a;频道模块之频道成员获取子频道在线成员数获取频道成员列表获取频道身份组成员列…

计算机组成原理简介

计算机组成原理简介 计算机组成原理是一门研究计算机系统结构、工作原理及其性能分析的学科。它主要探讨计算机硬件的结构及其与软件的交互&#xff0c;旨在帮助我们理解计算机如何执行任务、处理数据和控制指令流。 本文将从以下几个方面介绍计算机组成原理的基本概念&#…

大和解!淘宝微信11年“屏蔽战”终落幕

​转载&#xff1a;新熵 原创 作者丨余寐 编辑丨蕨影 拆墙之后&#xff0c;一切才刚刚开始。 见证历史的时刻到了&#xff01;互联网两大巨头正式牵手&#xff0c;淘宝平台拟全面引入微信支付。 淘宝天猫方面回应称&#xff0c;淘宝天猫始终秉持开放的合作理念&#xff0c;积…

【FPGA数字信号处理】- FIR串行滤波器

理解和掌握 FIR 串行滤波器是踏入数字信号处理领域的重要一步。 那么&#xff0c;什么是 FIR 串行滤波器&#xff1f;它是如何工作的&#xff1f;又有着怎样的神奇之处呢&#xff1f;让我们一起揭开它的神秘面纱。 一、FIR 滤波器简介 FIR 滤波器&#xff0c;全称为有限脉冲…

GMT绘图笔记:绘制堆叠三维图

绘制三维图可以更好地表现物理场的空间分布和对比&#xff0c;也有很多的软件工具可以实现这一功能&#xff0c;本专栏主要强调使用GMT绘制。语法也非常简单&#xff0c;实例代码如下&#xff1a; gmt begin tibet_moho_3D png,pdf E600 # 绘制底图 gmt set FORMAT_GEO_MAP d…

【超快解决方法】教资点击打印准考证没反应!!!

首先点击右键【检查】 2.选中控制台 3.点击小红点 4.再点击第二个链接就可以下载下来啦

【Delphi】实现程序屏幕内容防止截图

前言&#xff1a; 有时候&#xff0c;我们做的程序界面内容不希望被截图&#xff0c;那么如何实现呢&#xff1f;其实实现起来非常简单&#xff0c;就是一句话的事。 在主窗口show的时候&#xff0c;调用如下参数即可&#xff01;需要注意的是Delphi中并没有定义WDA_EXCLUDEFRO…

微信加粉计数器/微信加人计数网页版

微信加粉计数器/微信加人计数网页版 计数器支持的vx版本微信计数器特色计数器的定制开发 计数器支持的vx版本 最新版本计数器支持任意版本的微信&#xff0c;在也不用使用指定的版本了 微信计数器特色 目前该计数器采用非hook,非注入&#xff0c;不修改覆盖任何源文件 计数…

Dagger:Android 和 Java 的快速依赖注入框架

在软件开发中&#xff0c;依赖注入&#xff08;DI&#xff09;是一种设计模式&#xff0c;用于实现控制反转&#xff0c;减少代码耦合&#xff0c;提高模块化。Dagger 是一个由 Google 开发的依赖注入库&#xff0c;专门用于 Android 和 Java 应用程序&#xff0c;以其快速和高…

为什么 GitHub 成为开发者的“唯一真爱”?吊打其他代码托管平台

GitHub,这个名字可能已经深深印在了每一个开发者的脑海里。但你有没有想过,为什么 GitHub 能够在激烈的竞争中脱颖而出,成为全球最大、最流行的代码托管平台?其实它的成功背后,有着很多不为人知的细节。今天我们就来聊聊 GitHub 是如何一步步赢得开发者的心,让其他竞争对…

何时何地,你需要提示工程、函数调用、RAG还是微调大模型?

介绍 在快速发展的生成式人工智能领域&#xff0c;某些流行术语已变得司空见惯&#xff1a;“提示工程”、“函数调用”、“RAG”和“微调”&#xff0c;你应该也经常遇到这些术语&#xff0c;但你是否能够理清这些概念之间的关系&#xff1f;这些其实都是一些大模型的应用策略…

OpenGL Texture C++ 预览Camera视频

OpenGL是一个图形API&#xff0c;并不是一个独立的平台。包含了一系列可以操作图形、图像的函数。基于Texture纹理强大的功能&#xff0c;本篇文章实现Android OpenGL Texture C 预览Camera视频流的功能。 项目github地址&#xff1a;https://github.com/wangyongyao1989/WyFFm…

Reflection Llama-3.1 70B:目前最强大的开源大语言模型

Reflection Llama-3.1 70B:目前最强大的开源大语言模型 模型特点性能表现使用建议未来展望 近日,一个名为Reflection Llama-3.1 70B的新型大语言模型(LLM)引起了业界广泛关注。该模型采用了名为"Reflection-Tuning"的创新训练技术,能够自主检测推理过程中的错误并及时…

入门必看!高薪+自由职业的3D建模师有多香?

‌3D建模‌是将现实世界中的物体或场景转化为三维数字模型的过程。这项技术连接着现实与虚拟两个世界&#xff0c;通过3D建模&#xff0c;我们可以将名胜古迹、雕塑艺术品等以三维的形式呈现于互联网&#xff0c;让用户体验到更加真实、立体的视觉感受。3D建模的应用领域广泛&a…

【基于 Spring Boot 的二手交易平台】

构建一个基于 Spring Boot 的二手交易平台是一个涉及多个组件和技术栈的复杂项目。以下是一个基本的框架概述&#xff0c;可以帮助你开始搭建这样一个平台&#xff1a; 技术栈选择 Spring Boot: 用于快速开发 RESTful Web 服务。数据库: MySQL, PostgreSQL, 或其他关系型数据…

华为 HCIP-Datacom H12-821 题库 (15)

有需要题库的可以加下方Q群 V群进行学习交流 1.以下关于 OSPF 路由聚合的描述&#xff0c;错误的是哪一项&#xff1f; A、OSPF 中任意一台路由器都可以进行路由聚合的操作 B、OSPF 有两种路由聚合方式&#xff1a;ABR 聚合和ASBR 聚合 C、路由聚合是指将相同前缀的路由信息聚合…

018.PL-SQL编程—包

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

【Prompt Engineering提示:Active-Prompt、方向性刺激提示、PAL(程序辅助语言模型)】

Active-Prompt 思维链&#xff08;CoT&#xff09;方法依赖于一组固定的人工注释范例。问题在于&#xff0c;这些范例可能不是不同任务的最有效示例。为了解决这个问题&#xff0c;Diao 等人&#xff08;2023&#xff09;(opens in a new tab)最近提出了一种新的提示方法&…