功能:
1.游戏中会随机产生7种不同的图形;
2.每种图形都是由四个方形的色块组成的;
3.玩家可以控制每种图形旋转、左右移动;
4.图形自动下落,当下落到底部或者碰到其他方块则不能继续下落;
5.每行方格满了,自动消除,并计一分;
6.当正中图形无法下落时,游戏结束
具体流程
- 图形类
记录7种图形的信息
1.管理类型
2.获取图形边界
3.转转方块
图形类型
// 图形类型 0-7
enum Tetrominoes{
NoShape, // 无图形 0
ZShape, // Z 1
SShape, // S 2
LineShape, // | 3
TShape, // T 4
SquareShape, // 方块 5
Lshape, // L 6
MirroredLshape // 反L 7
};
8×4×2 : 8页 每页是一个4行2列的二维数组
- 说明:
/**
* @brief 图形类
*
* @details
* 1.图形的基本信息
* 2.管理图形
* 3.获取图形边界
* 4.旋转图形
*/
class Shape{
public:
/**
* @brief 构造函数
*
* @details
* 以无图形创建实例
*/
Shape();
/**
* @brief 设置图形
*
* @details
* 1.规定图形位置
* 2.根据图形类型设置图形坐标,并保存当前图形类型
*
* @param shape 图形类型(enum)
*/
SetShape(Tetrominoes shape);
/**
* @brief 设置图形
*
* @details
* 1.规定图形位置
* 2.根据图形类型设置图形坐标,并保存当前图形类型
*
* @param shape 图形类型(enum)
*/
SetShape(Tetrominoes shape);
/**
* @brief 随机设置图形
*
* @details
* 1.能够随机产生1-7之间的随机数
* 2.根据随机数设置图形
*/
SetRandomShape();
/**
* @brief 获取图形类型
*
* @return 图形类型
*
*/
Tetrominoes GetShape()const{return pieceShape;}
/**
* @brief 获取图形中一个方块的横/纵坐标
*
* @param 1.index 第index个方块 2.x/y 方块的横/纵坐标
* 以无图形创建实例
*/
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]; // 图形坐标
/**
* @brief 设置图形的横、纵坐标
*
* @param 1.index:行数 2.x/y:横纵坐标
*/
void SetX(int index, int x) { coords[index][0] = x; }
void SetY(int index, int y) { coords[index][1] = y; }
};
- 代码
// 图形类
// 1.管理类型
// 2.获取图形边界
// 3.旋转方块
class Shape{
public:
Shape(){SetShape(NoShape);} // 构造函数
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; // 保存当前类型
}
}
void SetRandomShape(){ // 随机设置图形类型
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]; // 图形坐标
};
- 面板类
继承wxPanel
控件(绘制、键盘、计时器、游戏过程等) - 说明
/**
* @brief 面板类
*
* @details
* 继承wxPanel
*/
class Board:public wxPanel{
public:
/**
* @brief 构造函数
*
* @details
* 初始化父类的一些参数
* 初始化部分成员变量
* 绑定事件
*
* @param parent 框架类
*/
Board(wxFrame *parent);
/**
* @brief 开始
*
* @details
* 修改部分成员变量
* 创建一个新的图形
* 开始计时
*/
void Start();
/**
* @brief 暂停
*
* @details
* 根据 暂停标志 进行操作
* 暂停 停止计时 分数栏显示 paused
* 不暂停 开始计时 分数栏显示分数
*/
void Pause();
protected:
/**
* @brief 绘制事件
*
* @param event 事件
*
* @details
* 获取画笔
* 获取面板区域大小 // 在框架类中初始化成员列表中定义过
* 获取剩余面板的高度(偏移量)
* 绘制正在移动的图形
* 绘制底层堆积的图形
*/
void OnPaint(wxPaintEvent& event);
/**
* @brief 键盘事件
*
* @param event 事件
*
* @details
* 左移 右移
* 左旋 右旋
* <space> 下落到底
* <d/D> 下降一行
* <p/P> 暂停
*/
void OnKeyDown(wxKeyEvent& event);
/**
* @brief 计时器事件
*
* @param event 事件
*
* @details
* 判断下降标志
* 不在下落过程:更改标志、创建新的图形
* 在下落过程:下降一行
*/
void OnTimer(wxCommandEvent& event);
private:
/**
* @brief 方块个数
*
* @details
* 10*22个方块
*/
enum {BoardWidth = 10,BoardHeight = 22};
/**
* @brief 获取图形类
*
* @details
* 获取(i,j)坐标的图形类型 10*22、
*
* @return Tetrominoes 图形类型
*/
Tetrominoes& ShapeAt(int x,int y){return board[(y*BoardWidth)+x];}
/**
* @brief 获取一个方块的尺寸
*
* @details
* 方块的宽/高 = 面板的宽/高 / 方块个数
*/
int SquareWidth(){return GetClientSize().GetWidth()/BoardWidth;}
int SquareHeight(){return GetClientSize().GetHeight()/BoardHeight;}
/**
* @brief 清空面板
*
* @details
* 将方块面板全部置为NoShape
*/
void ClearBoard();
/**
* @brief 下落到最底层 <space>
*
* @details
* 对y进行--
*/
void DropDown();
/**
* @brief 下降一行
*
* @details
* y-1
*/
void OneLineDown();
/**
* @brief 更新下降图形后的信息
*
* @details
* 将图形基点根据图形类的坐标进行扩展
* 分别将拓展的坐标设置为当前图形的类别对应的颜色
* 有满行消除满的行
* 如果下降结束 创建新的图形
*/
void PieceDropped();
/**
* @brief 消除已满的行
*
* @details
* 从顶端开始一行一行的遍历
* 如果第i行满了,满的行数++,将当前行的图形信息由上一行代替
* 更新计分板数值
*/
void RemoveFullLines();
/**
* @brief 创建新图形
*
* @details
* 设置当前位置的坐标:当前位置位于面板中间最上方
* 如果不能移动则游戏结束
*/
void NewPiece();
/**
* @brief 尝试移动 <->> <<->
*
* @param newPiece(const Shape&)图形(类) newX newY:横纵坐标
*
* @details
* 将当前的图形移动到(newX,newY)
* 移动后不能超边界、不能重合
*
*/
bool TryMove(const Shape& newPiece,int newX,int newY);
/**
* @brief 绘制方块
*
* @param dc(wxPaintDC):画笔 x/y:图形类中坐标 shape(Tetrominoes):图形类别(决定方块颜色)
* @details
* 不同类型的图形 颜色不同
* 绘制一个方块
* x+1,y+1 线宽
*/
void DrawSquare(wxPaintDC &dc,int x,int y,Tetrominoes shape);
wxTimer *timer; // 计时器
bool isStarted; // 标志:游戏开始
bool isPaused; // 标志:游戏暂停
bool isFallingFinished; // 标志:方块下落过程
Shape curPiece; // 类:当前的图形
int curX; // 当前方块的x坐标
int curY; // 当前方块的y坐标 // 当前位置为基点 图形类中的坐标作为基点的扩展
int numLinesRemoved; // 移除的行数->得分
Tetrominoes board[BoardWidth*BoardHeight]; // 方块面板 10*22 用来显示去全部面板的方块信息 存放的图形类别 根据图形类别显示
wxStatusBar *m_stsbar; // 分值状态栏
};
- 代码
// 面板类:继承wxPanel
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 lineRemovedChanged(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 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; // 计时器
bool isStarted; // 开始状态
bool isPaused; // 暂停状态
bool isFallingFinished; // 下落完成
Shape curPiece; // 当前方块
int curX; // 当前方块x坐标
int curY; // 当前方块y坐标
int numLinesRemoved; // 移除的行数
Tetrominoes board[BoardWidth*BoardHeight]; // 方块面板
wxStatusBar *m_stsbar; // 分值状态栏
};
- 框架类
继承wxFrame
创建面板
其他的一些设置(计分板、初始分数、焦点等) - 说明
/**
* @brief 构造函数
*
* @details
* 以无图形创建实例
*/
class Teris:public wxFrame{
public:
/**
* @brief 构造函数
*
* @details
* 以初始化成员列表方式,初始化继承父类的一些参数
* 创建面板
* 设置信息(计分版等)
*
* @param title 标题
*/
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(); // 创建计分状态栏
sb->SetStatusText(wxT("0")); // 设置初始分值为0
Board *board = new Board(this); // 创建面板
board->SetFocus(); // 设置焦点
board->Start(); // 启动
}
};
- 应用程序类
继承wxAPP
创建框架
重载OnInit()
- 说明
/**
* @brief 应用程序类
*
* @details
* 初始化框架
*/
class MyApp:public wxAPP{
public:
/**
* @brief 初始化
*
* @details
* 重写wxAPP类中的OnInit
+ 创建框架
* 对框架的一些设置
* 显示框架
*/
bool OnInit(){}
};
- 代码:
// 应用程序类 继承wxAPP
// 创建框架
// 重载 OnInit()
class MyApp:public wxApp{
public:
bool OnInit()override{
srand(time(NULL)); // 设置随机种子
Teris* teris = new Teris(wxT("Teris")); // 创建框架
teris->Centre(); // 框架居中
teris->Show(true); // 框架显示
return true;
}
};
- 应用程序实例化
调用宏 程序实例化
参数:应用程序类类名
// 调用宏 程序实例化
// 参数:应用程序类类名
wxIMPLEMENT_APP(MyApp);