<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # wxWidgets 中的俄羅斯方塊游戲 > 原文: [http://zetcode.com/gui/wxwidgets/thetetrisgame/](http://zetcode.com/gui/wxwidgets/thetetrisgame/) 俄羅斯方塊游戲是有史以來最受歡迎的計算機游戲之一。 原始游戲是由俄羅斯程序員 Alexey Pajitnov 于 1985 年設計和編程的。此后,幾乎所有版本的幾乎所有計算機平臺上都可以使用俄羅斯方塊。 俄羅斯方塊被稱為下降塊益智游戲。 在這個游戲中,我們有七個不同的形狀,稱為 tetrominoes。 S 形,Z 形,T 形,L 形,線形,鏡像 L 形和正方形。 這些形狀中的每一個都形成有四個正方形。 形狀從板上掉下來。 俄羅斯方塊游戲的目的是移動和旋轉形狀,以便它們盡可能地適合。 如果我們設法形成一行,則該行將被破壞并得分。 我們玩俄羅斯方塊游戲,直到達到頂峰。 ![Tetrominoes](https://img.kancloud.cn/2b/7a/2b7a874cd2ec9a34c259d3dd686809e9_328x132.jpg) 圖:Tetrominoes wxWidgets 是一個用于創建應用的工具包。 還有其他一些旨在創建計算機游戲的庫。 不過,可以使用 wxWidgets 和其他應用工具包來創建簡單的游戲。 ## 開發 我們的俄羅斯方塊游戲沒有圖像,我們使用 wxWidgets 編程工具包中提供的繪圖 API 繪制四面體。 每個計算機游戲的背后都有一個數學模型。 俄羅斯方塊也是如此。 游戲背后的一些想法。 * 我們使用`wxTimer`創建游戲周期。 * 繪制四方塊。 * 形狀以正方形為單位移動(而不是逐個像素移動)。 * 從數學上講,棋盤是一個簡單的數字列表。 `Shape.h` ```cpp #ifndef SHAPE_H #define SHAPE_H enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape }; class Shape { public: Shape() { SetShape(NoShape); } void SetShape(Tetrominoes shape); void SetRandomShape(); 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 MaxX() const; int MinY() const; int MaxY() const; Shape RotateLeft() const; Shape RotateRight() const; 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]; }; #endif ``` `Shape.cpp` ```cpp #include <stdlib.h> #include <algorithm> #include "Shape.h" using namespace std; void Shape::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 } }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { 1, 1 } }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { 0, 2 } }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 } }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } }; for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) coords[i][j] = coordsTable[shape][i][j]; } pieceShape = shape; } void Shape::SetRandomShape() { int x = rand() % 7 + 1; SetShape(Tetrominoes(x)); } int Shape::MinX() const { int m = coords[0][0]; for (int i=0; i<4; i++) { m = min(m, coords[i][0]); } return m; } int Shape::MaxX() const { int m = coords[0][0]; for (int i=0; i<4; i++) { m = max(m, coords[i][0]); } return m; } int Shape::MinY() const { int m = coords[0][1]; for (int i=0; i<4; i++) { m = min(m, coords[i][1]); } return m; } int Shape::MaxY() const { int m = coords[0][1]; for (int i=0; i<4; i++) { m = max(m, coords[i][1]); } return m; } Shape 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; } Shape 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; } ``` `Board.h` ```cpp #ifndef BOARD_H #define BOARD_H #include "Shape.h" #include <wx/wx.h> class Board : public wxPanel { public: Board(wxFrame *parent); void Start(); void Pause(); void linesRemovedChanged(int numLines); protected: void OnPaint(wxPaintEvent& event); void OnKeyDown(wxKeyEvent& event); void OnTimer(wxCommandEvent& event); 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(); void DropDown(); void OneLineDown(); void PieceDropped(); void RemoveFullLines(); void NewPiece(); bool TryMove(const Shape& newPiece, int newX, int newY); void DrawSquare(wxPaintDC &dc, int x, int y, Tetrominoes shape); wxTimer *timer; bool isStarted; bool isPaused; bool isFallingFinished; Shape curPiece; int curX; int curY; int numLinesRemoved; Tetrominoes board[BoardWidth * BoardHeight]; wxStatusBar *m_stsbar; }; #endif ``` `Board.cpp` ```cpp #include "Board.h" Board::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 Board::Start() { if (isPaused) return; isStarted = true; isFallingFinished = false; numLinesRemoved = 0; ClearBoard(); NewPiece(); timer->Start(300); } void Board::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 Board::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 Board::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': OneLineDown(); break; case 'D': OneLineDown(); break; default: event.Skip(); } } void Board::OnTimer(wxCommandEvent& event) { if (isFallingFinished) { isFallingFinished = false; NewPiece(); } else { OneLineDown(); } } void Board::ClearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = NoShape; } void Board::DropDown() { int newY = curY; while (newY > 0) { if (!TryMove(curPiece, curX, newY - 1)) break; --newY; } PieceDropped(); } void Board::OneLineDown() { if (!TryMove(curPiece, curX, curY - 1)) PieceDropped(); } void Board::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 Board::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 Board::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 Board::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 Board::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); } ``` `Tetris.h` ```cpp #include <wx/wx.h> class Tetris : public wxFrame { public: Tetris(const wxString& title); }; ``` `Tetris.cpp` ```cpp #include "Tetris.h" #include "Board.h" Tetris::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(); } ``` `main.h` ```cpp #include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); }; ``` `main.cpp` ```cpp #include "main.h" #include "Tetris.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { srand(time(NULL)); Tetris *tetris = new Tetris(wxT("Tetris")); tetris->Centre(); tetris->Show(true); return true; } ``` 我對游戲做了一些簡化,以便于理解。 游戲啟動后立即開始。 我們可以通過按 `p` 鍵暫停游戲。 `空格鍵`將把俄羅斯方塊放在底部。 `d` 鍵會將棋子下降一行。 (它可以用來加快下降速度。)游戲以恒定速度運行,沒有實現加速。 分數是我們已刪除的行數。 ```cpp ... isFallingFinished = false; isStarted = false; isPaused = false; numLinesRemoved = 0; curX = 0; curY = 0; ... ``` 在開始游戲之前,我們先初始化一些重要的變量。 `isFallingFinished`變量確定俄羅斯方塊形狀是否已完成下降,然后我們需要創建一個新形狀。 `numLinesRemoved`計算行數,到目前為止我們已經刪除了行數。 `curX`和`curY`變量確定下降的俄羅斯方塊形狀的實際位置。 ```cpp 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); } } ``` 游戲的繪圖分為兩個步驟。 在第一步中,我們繪制所有形狀或已放置到板底部的形狀的其余部分。 所有正方形都記在`board`數組中。 我們使用`ShapeAt()`方法訪問它。 ```cpp 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()); } } ``` 下一步是繪制掉落的實際零件。 ```cpp ... switch (keycode) { case WXK_LEFT: TryMove(curPiece, curX - 1, curY); break; ... ``` 在`Board::OnKeyDown()`方法中,我們檢查按鍵是否按下。 如果按向左箭頭鍵,我們將嘗試將棋子向左移動。 我們說嘗試,因為這片可能無法移動。 ```cpp void Board::OnTimer(wxCommandEvent& event) { if (isFallingFinished) { isFallingFinished = false; NewPiece(); } else { OneLineDown(); } } ``` 在`Board::OnTimer()`方法中,我們可以創建一個新的片段,將前一個片段放到底部,或者將下降的片段向下移動一行。 ```cpp void Board::DropDown() { int newY = curY; while (newY > 0) { if (!TryMove(curPiece, curX, newY - 1)) break; --newY; } PieceDropped(); } ``` `Board::DropDown()`方法將下落的形狀立即下降到板的底部。 當我們按下空格鍵時會發生這種情況。 ```cpp void Board::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(); } ``` 在`Board::PieceDropped()`方法中,我們將當前形狀設置為其最終位置。 我們調用`RemoveFullLines()`方法來檢查是否至少有一個完整的行。 如果尚未在`Board::PieceDropped()`方法中創建新的俄羅斯方塊形狀,則可以創建一個新的俄羅斯方塊形狀。 ```cpp 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); } } ``` 此代碼將刪除所有行。 找到整條線后,我們增加計數器。 我們將整行上方的所有行向下移動一行。 這樣我們就破壞了整個生產線。 注意,在俄羅斯方塊游戲中,我們使用了樸素引力。 這意味著正方形可能會漂浮在空白間隙上方。 ```cpp void Board::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")); } } ``` `Board::NewPiece()`方法隨機創建一個新的俄羅斯方塊。 如果棋子無法進入其初始位置,則游戲結束。 ```cpp bool Board::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; } ``` 在`Board::TryMove()`方法中,我們嘗試移動形狀。 如果形狀在棋盤的邊緣或與其他形狀相鄰,則返回`false`。 否則,我們將當前下降形狀放置到新位置并返回`true`。 `Shape`類保存有關俄羅斯方塊的信息。 ```cpp for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) coords[i][j] = coordsTable[shape][i][j]; } ``` `coords`數組保存俄羅斯方塊的坐標。 例如,數字`{0, -1}, {0, 0}, {1, 0}, {1, 1}`表示旋轉的 S 形。 下圖說明了形狀。 ![Coordinates](https://img.kancloud.cn/5c/0b/5c0bbb2ed04c22b46915ea1ef803ce4d_272x230.jpg) 圖:坐標 當繪制當前下降片時,將其繪制在`curX`和`curY`位置。 然后,我們查看坐標表并繪制所有四個正方形。 ![Tetris](https://img.kancloud.cn/f6/52/f652e6914260fb73fc5bfc599c0b13be_182x412.jpg) 圖:俄羅斯方塊 這是 wxWidgets 中的俄羅斯方塊游戲。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看