美文网首页
案例:俄罗斯方块

案例:俄罗斯方块

作者: jdzhangxin | 来源:发表于2018-09-11 13:53 被阅读59次

需求

基于wxWidgets实现一个俄罗斯方块游戏

功能

  1. 游戏中会随机生成7种不同图形。
  2. 每种图形都是由四个方形的色块组成的。
  3. 玩家可以控制每种图形旋转、左右移动。
  4. 图形自动下落,当下落到底部或者碰到其他方块则不能继续下落。
  5. 每行方格满了,自动消除,并计一分。
  6. 当正中图形无法下落时,游戏结束。

参考代码

下面为了阅读方便,代码放在一个文件中,实际项目中要以类为单位分开存放。

#ifdef Win32
#define WXUSINGDLL
#define __WXMSW__
#define _UNICODE
#pragma comment(lib, "wxmsw31u_core.lib")
#pragma comment(lib, "wxbase31u.lib")
#endif

#include <wx/wx.h>
#include <algorithm>

using namespace std;

/**
* @breif 方块类型
*/
enum Tetrominoes {
    NoShape,        ///< 无图形
    ZShape,         ///< Z图形
    SShape,         ///< S图形
    LineShape,      ///< 线图形
    TShape,                 ///< T图形
    SquareShape,    ///< 方形
    LShape,                 ///< L形
    MirroredLShape  ///< 反L形
};

/**
* @breif 图形类
*
* @details
* 1. 管理类型
* 2. 获取图像边界
* 3. 旋转方块
*/
class Shape {
public:
     /**
    * @breif 构造函数
     */
    Shape() { SetShape(NoShape); }

     /**
     * @breif 设置图形类型
     *
     * @param shape 图形类型
     */
    void SetShape(Tetrominoes shape){
        static const int coordsTable[8][4][2] = {
            { { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 } },        // 无图形
            { { 0, -1 },  { 0, 0 },   { -1, 0 },  { -1, 1 } },        // Z图形
            { { 0, -1 },  { 0, 0 },   { 1, 0 },   { 1, 1 } },         // S图形
            { { 0, -1 },  { 0, 0 },   { 0, 1 },   { 0, 2 } },         // 线图形
            { { -1, 0 },  { 0, 0 },   { 1, 0 },   { 0, 1 } },         // T图形
            { { 0, 0 },   { 1, 0 },   { 0, 1 },   { 1, 1 } },         // 方形
            { { -1, -1 }, { 0, -1 },  { 0, 0 },   { 0, 1 } },        // L形
            { { 1, -1 },  { 0, -1 },  { 0, 0 },   { 0, 1 } }         // 反L形
        };

        // 设置当前的图形
        for (int i = 0; i < 4 ; i++) {
            for (int j = 0; j < 2; ++j)
                coords[i][j] = coordsTable[shape][i][j];
        }
        // 保存当前类型
        pieceShape = shape;
    }
     /**
     * @breif 设置随机图形
     */
    void SetRandomShape(){
        // 设置随机图形
        int x = rand() % 7 + 1;
        SetShape(Tetrominoes(x));
    }

     /**
     * @breif 获取图形类型
     *
     * @return 图形类型
     */
    Tetrominoes GetShape() const { return pieceShape; }

    int x(int index) const { return coords[index][0]; }
    int y(int index) const { return coords[index][1]; }

    int MinX() const {
      int m = coords[0][0];
      for (int i=0; i<4; i++) {
        m = min(m, coords[i][0]);
      }
      return m;
    }
    int MaxX() const {
      int m = coords[0][0];
      for (int i = 0; i < 4; i++) {
        m = max(m, coords[i][0]);
      }
      return m;
    }
    int MinY() const {
      int m = coords[0][1];
      for (int i = 0; i < 4; i++) {
        m = min(m, coords[i][1]);
      }
      return m;
    }
    int MaxY() const {
      int m = coords[0][1];
      for (int i = 0; i < 4; i++) {
        m = max(m, coords[i][1]);
      }
      return m;
    }

    /**
     * @breif 获取左旋图形
     *
     * @return 图形
     */
    Shape RotateLeft() const {
      // 方块不旋转
      if (pieceShape == SquareShape)
        return *this;

      Shape result;
      result.pieceShape = pieceShape;
      for (int i = 0; i < 4; ++i) {
        result.SetX(i, y(i));
        result.SetY(i, -x(i));
      }
      return result;
    }

    /**
     * @breif 获取右旋图形
     *
     * @return 图形
     */
    Shape RotateRight() const {
      // 方块不旋转
      if (pieceShape == SquareShape)
        return *this;

      Shape result;
      result.pieceShape = pieceShape;
      for (int i = 0; i < 4; ++i) {
        result.SetX(i, -y(i));
        result.SetY(i, x(i));
      }
      return result;
    }

  private:
    void SetX(int index, int x) { coords[index][0] = x; }
    void SetY(int index, int y) { coords[index][1] = y; }
    Tetrominoes pieceShape;///< 方块类型
    int coords[4][2];      ///< 方块坐标
};

class Board : public wxPanel {

public:
    Board(wxFrame *parent): wxPanel(parent, wxID_ANY, wxDefaultPosition,wxDefaultSize, wxBORDER_NONE){
        timer = new wxTimer(this, 1);

        m_stsbar = parent->GetStatusBar();
        isFallingFinished = false;
        isStarted = false;
        isPaused = false;
        numLinesRemoved = 0;
        curX = 0;
        curY = 0;

        ClearBoard();  

        Connect(wxEVT_PAINT, wxPaintEventHandler(Board::OnPaint));
        Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(Board::OnKeyDown));
        Connect(wxEVT_TIMER, wxCommandEventHandler(Board::OnTimer));
    }
    void Start(){
        if (isPaused)
            return;

        isStarted = true;
        isFallingFinished = false;
        numLinesRemoved = 0;

        // 清空面板
        ClearBoard();

        NewPiece();
        timer->Start(300);
    }
    void Pause(){
        if (!isStarted)
            return;

        isPaused = !isPaused;
        if (isPaused) {
            timer->Stop();
            m_stsbar->SetStatusText(wxT("paused"));
        } else {
            timer->Start(300);
            wxString str;
            str.Printf(wxT("%d"), numLinesRemoved);
            m_stsbar->SetStatusText(str);
        }
        Refresh();
    }
    void linesRemovedChanged(int numLines);

protected:
    void OnPaint(wxPaintEvent& event){
        wxPaintDC dc(this);
        wxSize size = GetClientSize();

        int boardTop = size.GetHeight() - BoardHeight * SquareHeight();
        for (int i = 0; i < BoardHeight; ++i) {
            for (int j = 0; j < BoardWidth; ++j) {
                Tetrominoes shape = ShapeAt(j, BoardHeight - i - 1);
                if (shape != NoShape)
                    DrawSquare(dc, 0 + j * SquareWidth(),
                            boardTop + i * SquareHeight(), shape);
            }
        }

        if (curPiece.GetShape() != NoShape) {
            for (int i = 0; i < 4; ++i) {
                int x = curX + curPiece.x(i);
                int y = curY - curPiece.y(i);
                DrawSquare(dc, 0 + x * SquareWidth(),
                        boardTop + (BoardHeight - y - 1) * SquareHeight(),
                        curPiece.GetShape());
            }
        }
    }

    void OnKeyDown(wxKeyEvent& event){
        if (!isStarted || curPiece.GetShape() == NoShape) {  
            event.Skip();
            return;
        }

        int keycode = event.GetKeyCode();

        if (keycode == 'p' || keycode == 'P') {
            Pause();
            return;
        }

        if (isPaused)
            return;

        switch (keycode) {
        case WXK_LEFT:
            TryMove(curPiece, curX - 1, curY);
            break;
        case WXK_RIGHT:
            TryMove(curPiece, curX + 1, curY);
            break;
        case WXK_DOWN:
            TryMove(curPiece.RotateRight(), curX, curY);
            break;
        case WXK_UP:
            TryMove(curPiece.RotateLeft(), curX, curY);
            break;
        case WXK_SPACE:
            DropDown();
            break;
        case 'd':
        case 'D':
            OneLineDown();
            break;
        default:
            event.Skip();
        }

    }
    void OnTimer(wxCommandEvent& event){
        if (isFallingFinished) {
            isFallingFinished = false;
            NewPiece();
        } else {
            OneLineDown();
        }
    }
private:
    enum { BoardWidth = 10, BoardHeight = 22 };

    Tetrominoes & ShapeAt(int x, int y) { return board[(y * BoardWidth) + x]; }

    int SquareWidth() { return GetClientSize().GetWidth() / BoardWidth; }
    int SquareHeight() { return GetClientSize().GetHeight() / BoardHeight; }

    void ClearBoard(){
        for (int i = 0; i < BoardHeight * BoardWidth; ++i)
            board[i] = NoShape;
    }

    void DropDown(){
        int newY = curY;
        while (newY > 0) {
            if (!TryMove(curPiece, curX, newY - 1))
                break;
            --newY;
        }
        PieceDropped();
    }

    void OneLineDown(){
        if (!TryMove(curPiece, curX, curY - 1))
            PieceDropped();
    }

    void PieceDropped(){
        for (int i = 0; i < 4; ++i) {
            int x = curX + curPiece.x(i);
            int y = curY - curPiece.y(i);
            ShapeAt(x, y) = curPiece.GetShape();
        }

        RemoveFullLines();

        if (!isFallingFinished)
            NewPiece();
    }

    void RemoveFullLines(){
        int numFullLines = 0;

        for (int i = BoardHeight - 1; i >= 0; --i) {
            bool lineIsFull = true;

            for (int j = 0; j < BoardWidth; ++j) {
                if (ShapeAt(j, i) == NoShape) {
                    lineIsFull = false;
                    break;
                }
            }

            if (lineIsFull) {
                ++numFullLines;
                for (int k = i; k < BoardHeight - 1; ++k) {
                    for (int j = 0; j < BoardWidth; ++j)
                        ShapeAt(j, k) = ShapeAt(j, k + 1);
                }
            }
        }

        if (numFullLines > 0) {
            numLinesRemoved += numFullLines;
            wxString str;
        str.Printf(wxT("%d"), numLinesRemoved);
            m_stsbar->SetStatusText(str);

            isFallingFinished = true;
            curPiece.SetShape(NoShape);
            Refresh();
        }
    }
    void NewPiece(){
        curPiece.SetRandomShape();
        curX = BoardWidth / 2 + 1;
        curY = BoardHeight - 1 + curPiece.MinY();

        if (!TryMove(curPiece, curX, curY)) {
            curPiece.SetShape(NoShape);
            timer->Stop();
            isStarted = false;
            m_stsbar->SetStatusText(wxT("game over"));
        }
    }

    bool TryMove(const Shape& newPiece, int newX, int newY){
        for (int i = 0; i < 4; ++i) {
            int x = newX + newPiece.x(i);
            int y = newY - newPiece.y(i);
            if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight)
                return false;
            if (ShapeAt(x, y) != NoShape)
                return false;
        }

        curPiece = newPiece;
        curX = newX;
        curY = newY;
        Refresh();
        return true;
    }

    void DrawSquare(wxPaintDC &dc, int x, int y, Tetrominoes shape){
        static wxColour colors[] = { wxColour(0, 0, 0), wxColour(204, 102, 102), 
                wxColour(102, 204, 102), wxColour(102, 102, 204), 
                wxColour(204, 204, 102), wxColour(204, 102, 204), 
                wxColour(102, 204, 204), wxColour(218, 170, 0) };

        static wxColour light[] = { wxColour(0, 0, 0), wxColour(248, 159, 171),
                wxColour(121, 252, 121), wxColour(121, 121, 252), 
                wxColour(252, 252, 121), wxColour(252, 121, 252), 
                wxColour(121, 252, 252), wxColour(252, 198, 0) };

        static wxColour dark[] = { wxColour(0, 0, 0), wxColour(128, 59, 59), 
                wxColour(59, 128, 59), wxColour(59, 59, 128), 
                wxColour(128, 128, 59), wxColour(128, 59, 128), 
                wxColour(59, 128, 128), wxColour(128, 98, 0) };


        wxPen pen(light[int(shape)]);
        pen.SetCap(wxCAP_PROJECTING);
        dc.SetPen(pen);

        dc.DrawLine(x, y + SquareHeight() - 1, x, y);
        dc.DrawLine(x, y, x + SquareWidth() - 1, y);

        wxPen darkpen(dark[int(shape)]);
        darkpen.SetCap(wxCAP_PROJECTING);
        dc.SetPen(darkpen);

        dc.DrawLine(x + 1, y + SquareHeight() - 1,
            x + SquareWidth() - 1, y + SquareHeight() - 1);
        dc.DrawLine(x + SquareWidth() - 1, 
            y + SquareHeight() - 1, x + SquareWidth() - 1, y + 1);

        dc.SetPen(*wxTRANSPARENT_PEN);
        dc.SetBrush(wxBrush(colors[int(shape)])); 
        dc.DrawRectangle(x + 1, y + 1, SquareWidth() - 2, 
            SquareHeight() - 2);
    }

    wxTimer *timer;                 ///< 计时器
    bool isStarted;                 ///< 开始状态
    bool isPaused;                  ///< 暂停状态
    bool isFallingFinished;             ///< 下落完成

    Shape curPiece;                 ///< 当前方块
    int curX;                       ///< 当前方块X坐标
    int curY;                       ///< 当前方块Y坐标

    int numLinesRemoved;                    ///< 移除行数
    Tetrominoes board[BoardWidth * BoardHeight];    ///< 方块面板
    wxStatusBar *m_stsbar;              ///< 分值状态栏
};

class Tetris : public wxFrame{
public:
    Tetris(const wxString& title): wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(180, 380)){
        wxStatusBar *sb = CreateStatusBar();
        sb->SetStatusText(wxT("0"));
        Board *board = new Board(this);
        board->SetFocus();
        board->Start();
    }
};

class MyApp : public wxApp {
  public:
    virtual bool OnInit(){
        srand(time(NULL));
        Tetris *tetris = new Tetris(wxT("Tetris"));
        tetris->Centre();
        tetris->Show(true);
        return true;
    }
};

IMPLEMENT_APP(MyApp) // 应用程序实例化

相关文章

  • 案例:俄罗斯方块

    需求 基于wxWidgets实现一个俄罗斯方块游戏 功能 游戏中会随机生成7种不同图形。 每种图形都是由四个方形的...

  • 轻松一下,500 行代码写一个俄罗斯方块游戏玩玩

    导读:本文我们要制作一个俄罗斯方块游戏。01 俄罗斯方块 Tetris俄罗斯方块游戏是世界上最流行的游戏之一。是由...

  • 2019届网易游戏校招-测试开发工程师编程题-C++

    2019届网易互联网校招笔试-编程题 1. 古老的俄罗斯方块游戏机。 题目:自定义俄罗斯方块列数,每次俄罗斯方块下...

  • 2019-04-25

    想写个俄罗斯方块玩。

  • 俄罗斯方块-swift

    先上个效果图 俄罗斯方块简介 《俄罗斯方块》(Tetris, 俄文:Тетрис)是一款由俄罗斯人阿列克谢·帕基特...

  • 2022-04-09

    俄罗斯方块告诉我们,你合群了,你就消失了。 俄罗斯方块同样告诉我们,如果你太不合群,你就出局了

  • 2019-06-30

    俄罗斯方块告诉我们:合群就会消失。

  • Tetris俄罗斯方块 for Mac(经典休闲游戏) 1.0

    Tetris俄罗斯方块 for Mac是动手又动脑的简单经典休闲游戏,很多人都喜欢玩,《俄罗斯方块》的基本规则是移...

  • .生活好像俄罗斯方块

    生活好像俄罗斯方块。 “人无远虑,必有近忧”,因为如果不早早想好在空中的俄罗斯方块该怎么放好,等都到了眼前再去琢磨...

  • 用300行Python代码实现俄罗斯方块

    俄罗斯方块是儿时最经典的游戏之一,刚开始接触 pygame 的时候就想写一个俄罗斯方块。但是想到旋转,停靠,消除等...

网友评论

      本文标题:案例:俄罗斯方块

      本文链接:https://www.haomeiwen.com/subject/ubopdftx.html