48、wxWidgets游戏之俄罗斯方块

 一、方块类

//Shape是所有方块的代码

///方块样式
enum Tetrominoes{NoShape,ZShape,SShape,LineShape,TShape,SquareShape,LShape,MirroredShape};

class Shape
{
public:
    Shape(){SetShape(NoShape);}
    void SetShape(Tetrominoes shape);
    void SetRandomShape();

    Tetrominoes GetShape() const{return pieceShape;}
    int GetX(int index) const {return  coords[index][0];}
    int GetY(int index) const {return coords[index][1];}

    int MinX() const;
    int MaxX() const;
    ///获取4个点中,最小的顶点值
    int MinY() const;
    int MaxY() const;

    Shape RotateLeft() const;
    Shape RotateRight() const;

private:
    ///设置第index点的x值。index点的值范围0-3
    void SetX(int index,int x){coords[index][0] = x;}

    ///设置第index点的y值。index点的值范围0-3
    void SetY(int index,int y){coords[index][1] = y;}

    ///当前砖块样式
    Tetrominoes pieceShape;

    ///当前砖块坐标
    int coords[4][2];

    int curShapeIndex;//当前方块索引
};

#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 } },//noshape
        { { -1, 1 },  { -1, 0 },   { 0, 0 },  { 0, -1 } },//S
        { { 1, 1 },  { 1, 0 },   { 0, 0 },   { 0, -1 } },//Z型
        { { 0, 2 },  { 0, 1 },   { 0, 0 },   { 0, -1 } },//一字型
        { { 0, 1 },  { 1, 0 },   { 0, 0 },   { -1, 0 } },//T
        { { 1, 1 },   { 0, 1 },   { 1, 0 },   { 0,0 } },//O型;四方型
        { { 0, 1 }, { 0, 0 },  { 0, -1 },   { -1, -1 } },//J型

        { { 0, 1 },  { 0, 0 },  { 0, -1 },   { 1, -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;
}

///生成随机方块。即从7种方块中随机生成某一种方块
void Shape::SetRandomShape()
{
    int x = rand() % 7 + 1;
    SetShape(Tetrominoes(x));
    curShapeIndex = 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]);
    }
}
int Shape::MaxY() const
{
    int m = coords[0][1];
    for (int i=0; i<4 ; i++){
        m = max(m,coords[i][1]);
    }
    return m;
}

///以平面直角坐标系为参照。
///左旋相当于把x,y值互换,并把y方向上的值取负值
Shape Shape::RotateLeft() const
{
    if(pieceShape == SquareShape) return *this;//正方形方块无须调整

    Shape result;
    result.pieceShape = pieceShape;

    for(int i =0 ; i < 4 ; ++i){
        result.SetX(i,GetY(i));
        result.SetY(i,-GetX(i));
    }
    return result;
}


///以平面直角坐标系为参照。
///右旋相当于把x,y值互换,并把x方向的值取负值
Shape Shape::RotateRight() const
{
    if(pieceShape == SquareShape) return *this;//正方形方块无须调整


    Shape result;
    result.pieceShape = pieceShape;

    for(int i=0 ; i <4 ; ++i)
    {
        result.SetX(i,-GetY(i));
        result.SetY(i,GetX(i));
    }

    return result;
}

二、逻辑控制


using namespace std;

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);

    //方块面板界面是一个 10 * 22 的整形二维数组
//    enum{
//
//        ///横向方块数量
//        SquareWidthCount = 10,
//
//        ///竖向方块数量
//        SquareHeightCount = 22
//    };


    ///横向方块数量
    static const int     SquareWidthCount = 10;

    ///竖向方块数量
    static const int     SquareHeightCount = 25;

private:

    ///格子总数量
    Tetrominoes board[SquareWidthCount * SquareHeightCount];

    ///返回某个格子的
    Tetrominoes& ShapeAt(int x, int y){
        return board[(y * SquareWidthCount ) + x];
    }

    /// 单个方块宽度 = 客户区宽度 / 横向方块数量
    int SquareWidth(){return GetClientSize().GetWidth() / SquareWidthCount;}

    /// 单个方块高度 = 客户区高度 / 竖向方块数量
    int SquareHeight(){return GetClientSize().GetHeight() / SquareHeightCount;}

    void ClearBoard();
    void DropDown();
    void OneLineDown();
    void PieceDropped();

    ///删除
    void RemoveFullLines();

    ///新的方块
    void NewPiece();
    bool TryMove(const Shape& newPiece, int newX ,int newY);

    wxTimer* timer;
    bool isStarted;
    bool isPaused;
    bool isFallingFinished;

    ///当前方块
    Shape curPiece;
    int curX;
    int curY;


    int numLinesRemoved;

    wxStatusBar* m_stsbar;
};


Board::Board(wxFrame* parent)
    : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(380, 580), wxBORDER_NONE)
{
    srand((unsigned)time(NULL));
    timer = new wxTimer(this, wxID_ANY);

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

    ClearBoard();


    Bind(wxEVT_PAINT, wxPaintEventHandler(Board::OnPaint), this);
    Bind(wxEVT_KEY_DOWN, wxKeyEventHandler(Board::OnKeyDown), this);
    Bind(wxEVT_TIMER, wxCommandEventHandler(Board::OnTimer), this);

}

///开始游戏
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);

    ///方块落到底部后要保持住。从最后一行依次向上绘制方块
    for(int y = 0; y < SquareHeightCount ; ++y)
    {
        for(int x = 0; x < SquareWidthCount ; ++x)
        {
            dc.SetPen(wxColour("#f0f0f0"));
            dc.SetBrush(wxNullBrush);
            //画面板上的方格
            dc.DrawRectangle(x * SquareWidth(), y * SquareHeight(), SquareWidth(), SquareHeight());
            //在面板上方格里填上位置数学
            dc.DrawText(wxString::Format("%i", x  + y * SquareWidthCount),
                        x * SquareWidth(), y * SquareHeight());

            Tetrominoes shape = ShapeAt(x, y);
            if(shape != NoShape)
            {
                dc.SetPen(*wxLIGHT_GREY);
                dc.SetBrush(*wxGREEN);
                dc.DrawRectangle(x * SquareWidth(), y * SquareHeight(), SquareWidth(), SquareHeight());
                //在面板上方格里填上方块索引值
                dc.SetPen(*wxRED);
                dc.SetBrush(*wxRED);
                dc.DrawText(wxString::Format("%i", shape),
                                             x * SquareWidth(), y * SquareHeight());
            }
        }
    }


    if(curPiece.GetShape() != NoShape)
    {
        for(int i = 0; i < 4 ; ++i)
        {
            int x = curX + curPiece.GetX(i);
            int y = curY + curPiece.GetY(i);
            //绘制方块
            dc.SetPen(*wxLIGHT_GREY);
            dc.SetBrush(*wxGREEN);
            dc.DrawRectangle(x * SquareWidth(), y * SquareHeight(),
                             SquareWidth(), SquareHeight()
                            );
            //在面板上方格里填上方块图形索引值
            dc.SetPen(*wxRED);
            dc.SetBrush(*wxRED);
            dc.DrawText(wxString::Format("%i", curPiece.GetShape()),
                                         x * SquareWidth(), y * SquareHeight());
        }//end for
    }//end if


}

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();
    }
}

///清空面板。即把面板上SquareWidthCount * SquareHeightCount数量的格子全部设置为没有图形。
void Board::ClearBoard()
{
    for (int i = 0; i < SquareHeightCount * SquareWidthCount; ++i)
        board[i] = NoShape;//所有格子置空
}
void Board::DropDown()
{
    int newY = curY;
    while (newY > 0)
    {
        if (!TryMove(curPiece, curX, newY + 1))
            break;
        ++newY;
    }
    PieceDropped();
}


///方块下落控制
void Board::OnTimer(wxCommandEvent& event)
{
    if (isFallingFinished)//触底啦 ,重新生成方块
    {
        isFallingFinished = false;
        NewPiece();
    }
    else //没到底部,则一行一行地向下挪
    {
        OneLineDown();
    }
}

///方块逐行正常下落控制
///X值不变,Y值减1
void Board::OneLineDown()
{
    if(!TryMove(curPiece, curX, curY + 1) ) PieceDropped();
}

///方块落底控制.
///1.计算坐标
///2.将面板的对应位置,设为当前图形编号
void Board::PieceDropped()
{
//    cout<<"PieceDropped"<<endl;
    for (int i = 0; i < 4; ++i)
    {
        int x = curX + curPiece.GetX(i);
        int y = curY + curPiece.GetY(i);
//        cout<<"i="<<i<<" " <<x <<","<<y <<"="<<ShapeAt(x,y);
        ShapeAt(x, y) = curPiece.GetShape();//面板上的位置设为当前图形的编号
//        cout<<" " <<x <<","<<y <<"="<<ShapeAt(x,y)<<"\t";
    }
//    cout<<endl;

    RemoveFullLines();//判断是否有满行

    if (!isFallingFinished)   NewPiece();
}


///清除满行。若当前行填满,则清除之;其余行随之下降
void Board::RemoveFullLines()
{
    int numFullLines = 0;

    for (int y = SquareHeightCount - 1 ; y > 0; y--) //20
    {
        bool lineIsFull = true;

        for (int x = 0; x < SquareWidthCount; x++)//10
        {
            if (ShapeAt(x, y) == NoShape)
            {
                lineIsFull = false;
                break;
            }
        }

        if (lineIsFull)
        {
            ++numFullLines;
            for (int j = y; j > 0 ; j--)
            {
                for (int x = 0; x < SquareWidthCount; x++)
                    ShapeAt(x, j) = ShapeAt(x, j - 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 = SquareWidthCount / 2 + 1;//初始X值。面板的水平中间位置
    curY = 1; // SquareHeightCount - 1 + curPiece.MinY();//初始Y值。面板顶部
//wxLogMessage(wxString::Format("CurY=%i,MinY=%i" ,curY,curPiece.MinY()));

    if(TryMove(curPiece, curX, curY)) //移动方块到面板顶部
    {
        cout << "NewPiece True curX=" << curX << " curY=" << curY << endl;
    }
    else//失败则停止
    {
        cout << "NewPiece False" << endl;
        curPiece.SetShape(NoShape); //置空
        timer->Stop();              //计时器停止
        isStarted = false;          //
        m_stsbar->SetStatusText(wxT("game over"));
    }


}

///移动方块到某一位置
///例如执行OneLineDrop函数中的TryMove(curPiece,curX,curY-1)语句,意味着下降一行
///例如执行左移TryMove(curPiece, curX - 1, curY)语句,意味着向左移动一格
bool Board::TryMove(const Shape& newPiece, int newX, int newY)
{
//    cout<<"TryMove"<<endl;
    for(int i = 0; i < 4 ; ++i)
    {
//        cout<<"i="<<i << " GetX=" << newPiece.GetX(i) << " GetY=" << newPiece.GetY(i)<<endl ;
//        cout<<"     newX=" << newX << " newY="<<newY<<endl;
        int x = newX + newPiece.GetX(i);
        int y = newY + newPiece.GetY(i);
//        cout<<"     x=" << x << " y="<<y<<endl;
        if(x < 0 || x >= SquareWidthCount || y < 0 || y >= SquareHeightCount) return false;

        if(ShapeAt(x, y) != NoShape) return false;
//        cout<<endl;
    }//end for int i

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

三、主程序控制

#include <wx/wx.h>
class Tetris:public wxFrame
{
public:
    Tetris(const wxString& title);
};


Tetris::Tetris(const wxString& title)
    :wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(580,780))
{
//    wxPanel* bottomPanel = new wxPanel(this);
    wxTextCtrl* tc = new wxTextCtrl(this,wxID_ANY,"",wxPoint(-1,-1),wxSize(100,100),wxTE_MULTILINE);

    wxStatusBar* sb = CreateStatusBar();
    sb->SetStatusText(wxT("0"));
    Board* board = new Board(this);

    wxBoxSizer* sz= new wxBoxSizer(wxHORIZONTAL);
    sz->Add(board,0,wxEXPAND|wxALL,20);
    sz->Add(tc,1,wxEXPAND);
    SetSizer(sz);

    board->SetFocus();
    board->Start();
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值