一、方块类
//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();
}