
功能:
1.游戲中會(huì)隨機(jī)產(chǎn)生7種不同的圖形;
2.每種圖形都是由四個(gè)方形的色塊組成的;
3.玩家可以控制每種圖形旋轉(zhuǎn)、左右移動(dòng);
4.圖形自動(dòng)下落,當(dāng)下落到底部或者碰到其他方塊則不能繼續(xù)下落;
5.每行方格滿了,自動(dòng)消除,并計(jì)一分;
6.當(dāng)正中圖形無(wú)法下落時(shí),游戲結(jié)束
具體流程
- 圖形類
記錄7種圖形的信息
1.管理類型
2.獲取圖形邊界
3.轉(zhuǎn)轉(zhuǎn)方塊
圖形類型
// 圖形類型 0-7
enum Tetrominoes{
NoShape, // 無(wú)圖形 0
ZShape, // Z 1
SShape, // S 2
LineShape, // | 3
TShape, // T 4
SquareShape, // 方塊 5
Lshape, // L 6
MirroredLshape // 反L 7
};
8×4×2 : 8頁(yè) 每頁(yè)是一個(gè)4行2列的二維數(shù)組
- 說(shuō)明:
/**
* @brief 圖形類
*
* @details
* 1.圖形的基本信息
* 2.管理圖形
* 3.獲取圖形邊界
* 4.旋轉(zhuǎn)圖形
*/
class Shape{
public:
/**
* @brief 構(gòu)造函數(shù)
*
* @details
* 以無(wú)圖形創(chuàng)建實(shí)例
*/
Shape();
/**
* @brief 設(shè)置圖形
*
* @details
* 1.規(guī)定圖形位置
* 2.根據(jù)圖形類型設(shè)置圖形坐標(biāo),并保存當(dāng)前圖形類型
*
* @param shape 圖形類型(enum)
*/
SetShape(Tetrominoes shape);
/**
* @brief 設(shè)置圖形
*
* @details
* 1.規(guī)定圖形位置
* 2.根據(jù)圖形類型設(shè)置圖形坐標(biāo),并保存當(dāng)前圖形類型
*
* @param shape 圖形類型(enum)
*/
SetShape(Tetrominoes shape);
/**
* @brief 隨機(jī)設(shè)置圖形
*
* @details
* 1.能夠隨機(jī)產(chǎn)生1-7之間的隨機(jī)數(shù)
* 2.根據(jù)隨機(jī)數(shù)設(shè)置圖形
*/
SetRandomShape();
/**
* @brief 獲取圖形類型
*
* @return 圖形類型
*
*/
Tetrominoes GetShape()const{return pieceShape;}
/**
* @brief 獲取圖形中一個(gè)方塊的橫/縱坐標(biāo)
*
* @param 1.index 第index個(gè)方塊 2.x/y 方塊的橫/縱坐標(biāo)
* 以無(wú)圖形創(chuàng)建實(shí)例
*/
int x(int index) const { return coords[index][0]; }
int y(int index) const { return coords[index][1]; }
/**
* @brief 獲取圖形邊界
*
* @return 最大/最小邊界值
*/
int MinX() const;
int MaxX() const;
int MinY() const;
int MaxY() const;
/**
* @brief 獲取圖形邊界
*
* @return 最大/最小邊界值
*/
int MinX() const;
int MaxX() const;
int MinY() const;
int MaxY() const;
/**
* @brief 獲取左旋圖形
*
* @details
* 以(0,0)為中心
* x -> y
* y -> -x
*@return 圖形(類)
*/
// o
// o o -> o o
// o o o
Shape RotateLeft() const;
/**
* @brief 獲取右旋圖形
*
* @details
* 以(0,0)為中心
* x -> -y
* y -> x
* @return 圖形(類)
*/
// o o o
// o o -> o o
// o
Shape RotateRight() const;
private:
Tetrominoes pieceShape; // 圖形類別
int coords[4][2]; // 圖形坐標(biāo)
/**
* @brief 設(shè)置圖形的橫、縱坐標(biāo)
*
* @param 1.index:行數(shù) 2.x/y:橫縱坐標(biāo)
*/
void SetX(int index, int x) { coords[index][0] = x; }
void SetY(int index, int y) { coords[index][1] = y; }
};
- 代碼
// 圖形類
// 1.管理類型
// 2.獲取圖形邊界
// 3.旋轉(zhuǎn)方塊
class Shape{
public:
Shape(){SetShape(NoShape);} // 構(gòu)造函數(shù)
void SetShape(Tetrominoes shape){ // 設(shè)置圖形類型
static const int coordsTable[8][4][2] = { // 圖形位置
{{0,0},{0,0},{0,0},{0,0}}, // 無(wú)圖形
{{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++){ // 設(shè)置的當(dāng)前圖形
for(int j=0;j<2;j++){
coords[i][j] = coordsTable[shape][i][j];
}
pieceShape = shape; // 保存當(dāng)前類型
}
}
void SetRandomShape(){ // 隨機(jī)設(shè)置圖形類型
int type = rand()%7+1;
// rand()%7 0-6 -> 1-7
SetShape(Tetrominoes(type));
}
Tetrominoes GetShape()const{return pieceShape;} // 獲取圖形類型
int x(int index)const{return coords[index][0];}
int y(int index)const{return coords[index][1];}
int MaxX()const{
int m = coords[0][0];
for(int i=0;i<4;i++){
m = max(m,coords[i][0]);
}
return m;
}
int MinX()const{
int m = coords[0][0];
for(int i=0;i<4;i++){
m = min(m,coords[i][0]);
}
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;
}
int MinY()const{
int m = coords[0][1];
for(int i=0;i<4;i++){
m = min(m,coords[i][1]);
}
return m;
}
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 RotateRight()const{ // 獲取右旋后的圖形
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]; // 圖形坐標(biāo)
};
- 面板類
繼承wxPanel
控件(繪制、鍵盤(pán)、計(jì)時(shí)器、游戲過(guò)程等) - 說(shuō)明
/**
* @brief 面板類
*
* @details
* 繼承wxPanel
*/
class Board:public wxPanel{
public:
/**
* @brief 構(gòu)造函數(shù)
*
* @details
* 初始化父類的一些參數(shù)
* 初始化部分成員變量
* 綁定事件
*
* @param parent 框架類
*/
Board(wxFrame *parent);
/**
* @brief 開(kāi)始
*
* @details
* 修改部分成員變量
* 創(chuàng)建一個(gè)新的圖形
* 開(kāi)始計(jì)時(shí)
*/
void Start();
/**
* @brief 暫停
*
* @details
* 根據(jù) 暫停標(biāo)志 進(jìn)行操作
* 暫停 停止計(jì)時(shí) 分?jǐn)?shù)欄顯示 paused
* 不暫停 開(kāi)始計(jì)時(shí) 分?jǐn)?shù)欄顯示分?jǐn)?shù)
*/
void Pause();
protected:
/**
* @brief 繪制事件
*
* @param event 事件
*
* @details
* 獲取畫(huà)筆
* 獲取面板區(qū)域大小 // 在框架類中初始化成員列表中定義過(guò)
* 獲取剩余面板的高度(偏移量)
* 繪制正在移動(dòng)的圖形
* 繪制底層堆積的圖形
*/
void OnPaint(wxPaintEvent& event);
/**
* @brief 鍵盤(pán)事件
*
* @param event 事件
*
* @details
* 左移 右移
* 左旋 右旋
* <space> 下落到底
* <d/D> 下降一行
* <p/P> 暫停
*/
void OnKeyDown(wxKeyEvent& event);
/**
* @brief 計(jì)時(shí)器事件
*
* @param event 事件
*
* @details
* 判斷下降標(biāo)志
* 不在下落過(guò)程:更改標(biāo)志、創(chuàng)建新的圖形
* 在下落過(guò)程:下降一行
*/
void OnTimer(wxCommandEvent& event);
private:
/**
* @brief 方塊個(gè)數(shù)
*
* @details
* 10*22個(gè)方塊
*/
enum {BoardWidth = 10,BoardHeight = 22};
/**
* @brief 獲取圖形類
*
* @details
* 獲取(i,j)坐標(biāo)的圖形類型 10*22、
*
* @return Tetrominoes 圖形類型
*/
Tetrominoes& ShapeAt(int x,int y){return board[(y*BoardWidth)+x];}
/**
* @brief 獲取一個(gè)方塊的尺寸
*
* @details
* 方塊的寬/高 = 面板的寬/高 / 方塊個(gè)數(shù)
*/
int SquareWidth(){return GetClientSize().GetWidth()/BoardWidth;}
int SquareHeight(){return GetClientSize().GetHeight()/BoardHeight;}
/**
* @brief 清空面板
*
* @details
* 將方塊面板全部置為NoShape
*/
void ClearBoard();
/**
* @brief 下落到最底層 <space>
*
* @details
* 對(duì)y進(jìn)行--
*/
void DropDown();
/**
* @brief 下降一行
*
* @details
* y-1
*/
void OneLineDown();
/**
* @brief 更新下降圖形后的信息
*
* @details
* 將圖形基點(diǎn)根據(jù)圖形類的坐標(biāo)進(jìn)行擴(kuò)展
* 分別將拓展的坐標(biāo)設(shè)置為當(dāng)前圖形的類別對(duì)應(yīng)的顏色
* 有滿行消除滿的行
* 如果下降結(jié)束 創(chuàng)建新的圖形
*/
void PieceDropped();
/**
* @brief 消除已滿的行
*
* @details
* 從頂端開(kāi)始一行一行的遍歷
* 如果第i行滿了,滿的行數(shù)++,將當(dāng)前行的圖形信息由上一行代替
* 更新計(jì)分板數(shù)值
*/
void RemoveFullLines();
/**
* @brief 創(chuàng)建新圖形
*
* @details
* 設(shè)置當(dāng)前位置的坐標(biāo):當(dāng)前位置位于面板中間最上方
* 如果不能移動(dòng)則游戲結(jié)束
*/
void NewPiece();
/**
* @brief 嘗試移動(dòng) <->> <<->
*
* @param newPiece(const Shape&)圖形(類) newX newY:橫縱坐標(biāo)
*
* @details
* 將當(dāng)前的圖形移動(dòng)到(newX,newY)
* 移動(dòng)后不能超邊界、不能重合
*
*/
bool TryMove(const Shape& newPiece,int newX,int newY);
/**
* @brief 繪制方塊
*
* @param dc(wxPaintDC):畫(huà)筆 x/y:圖形類中坐標(biāo) shape(Tetrominoes):圖形類別(決定方塊顏色)
* @details
* 不同類型的圖形 顏色不同
* 繪制一個(gè)方塊
* x+1,y+1 線寬
*/
void DrawSquare(wxPaintDC &dc,int x,int y,Tetrominoes shape);
wxTimer *timer; // 計(jì)時(shí)器
bool isStarted; // 標(biāo)志:游戲開(kāi)始
bool isPaused; // 標(biāo)志:游戲暫停
bool isFallingFinished; // 標(biāo)志:方塊下落過(guò)程
Shape curPiece; // 類:當(dāng)前的圖形
int curX; // 當(dāng)前方塊的x坐標(biāo)
int curY; // 當(dāng)前方塊的y坐標(biāo) // 當(dāng)前位置為基點(diǎn) 圖形類中的坐標(biāo)作為基點(diǎn)的擴(kuò)展
int numLinesRemoved; // 移除的行數(shù)->得分
Tetrominoes board[BoardWidth*BoardHeight]; // 方塊面板 10*22 用來(lái)顯示去全部面板的方塊信息 存放的圖形類別 根據(jù)圖形類別顯示
wxStatusBar *m_stsbar; // 分值狀態(tài)欄
};
- 代碼
// 面板類:繼承wxPanel
class Board:public wxPanel{
public:
Board(wxFrame *parent)
:wxPanel(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxBORDER_NONE){ // 構(gòu)造函數(shù)
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(){ // 啟動(dòng)
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 lineRemovedChanged(int numLines){ // 消除
}
protected:
void OnPaint(wxPaintEvent& event){ // 繪制事件
wxPaintDC dc(this); // 獲取畫(huà)筆
wxSize size = GetClientSize(); // 獲取面板區(qū)域大小
int boardTop = size.GetHeight() - BoardHeight*SquareHeight();
// 畫(huà)最底層堆積的圖形
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);
}
}
// 畫(huà)目前移動(dòng)中的圖形
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){ // 鍵盤(pán)事件
if(!isStarted || curPiece.GetShape() == NoShape){
event.Skip();
return;
}
int keycode = event.GetKeyCode(); // 獲取鍵盤(pán)事件
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){ // 計(jì)時(shí)器事件
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(){ // 創(chuàng)建新方塊
curPiece.SetRandomShape(); // 隨機(jī)創(chuàng)建圖形
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){ // 嘗試移動(dòng)
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 clours[] = {
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.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(clours[int(shape)]));
dc.DrawRectangle(x+1,y+1,SquareWidth()-2,SquareHeight()-2);
}
wxTimer *timer; // 計(jì)時(shí)器
bool isStarted; // 開(kāi)始狀態(tài)
bool isPaused; // 暫停狀態(tài)
bool isFallingFinished; // 下落完成
Shape curPiece; // 當(dāng)前方塊
int curX; // 當(dāng)前方塊x坐標(biāo)
int curY; // 當(dāng)前方塊y坐標(biāo)
int numLinesRemoved; // 移除的行數(shù)
Tetrominoes board[BoardWidth*BoardHeight]; // 方塊面板
wxStatusBar *m_stsbar; // 分值狀態(tài)欄
};
- 框架類
繼承wxFrame
創(chuàng)建面板
其他的一些設(shè)置(計(jì)分板、初始分?jǐn)?shù)、焦點(diǎn)等) - 說(shuō)明
/**
* @brief 構(gòu)造函數(shù)
*
* @details
* 以無(wú)圖形創(chuàng)建實(shí)例
*/
class Teris:public wxFrame{
public:
/**
* @brief 構(gòu)造函數(shù)
*
* @details
* 以初始化成員列表方式,初始化繼承父類的一些參數(shù)
* 創(chuàng)建面板
* 設(shè)置信息(計(jì)分版等)
*
* @param title 標(biāo)題
*/
Teris(const wxString& title);
}
- 代碼
// 框架類 繼承wxFrame
class Teris:public wxFrame{
public:
Teris(const wxString& title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(180,380)){
wxStatusBar *sb = CreateStatusBar(); // 創(chuàng)建計(jì)分狀態(tài)欄
sb->SetStatusText(wxT("0")); // 設(shè)置初始分值為0
Board *board = new Board(this); // 創(chuàng)建面板
board->SetFocus(); // 設(shè)置焦點(diǎn)
board->Start(); // 啟動(dòng)
}
};
- 應(yīng)用程序類
繼承wxAPP
創(chuàng)建框架
重載OnInit() - 說(shuō)明
/**
* @brief 應(yīng)用程序類
*
* @details
* 初始化框架
*/
class MyApp:public wxAPP{
public:
/**
* @brief 初始化
*
* @details
* 重寫(xiě)wxAPP類中的OnInit
+ 創(chuàng)建框架
* 對(duì)框架的一些設(shè)置
* 顯示框架
*/
bool OnInit(){}
};
- 代碼:
// 應(yīng)用程序類 繼承wxAPP
// 創(chuàng)建框架
// 重載 OnInit()
class MyApp:public wxApp{
public:
bool OnInit()override{
srand(time(NULL)); // 設(shè)置隨機(jī)種子
Teris* teris = new Teris(wxT("Teris")); // 創(chuàng)建框架
teris->Centre(); // 框架居中
teris->Show(true); // 框架顯示
return true;
}
};
- 應(yīng)用程序?qū)嵗?br> 調(diào)用宏 程序?qū)嵗?br> 參數(shù):應(yīng)用程序類類名
// 調(diào)用宏 程序?qū)嵗?// 參數(shù):應(yīng)用程序類類名
wxIMPLEMENT_APP(MyApp);