这个俄罗斯方块如何集成到我的应用程序中(使用一个文件)?

这是wxWidgets论坛的中文版本。在这里,您可以用您的母语汉语讨论上面任一子论坛所涉及的所有关于wxWidgets的话题。欢迎大家参与到对有价值的帖子的中英互译工作中来!
Post Reply
whz
Earned a small fee
Earned a small fee
Posts: 11
Joined: Tue Oct 05, 2010 1:01 am

这个俄罗斯方块如何集成到我的应用程序中(使用一个文件)?

Post by whz » Sat Jul 23, 2011 2:38 am

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

};
#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

#ifndef BOARD_H
#define BOARD_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

//shape.cpp
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 } }, //ZShape
{ { 0, -1 }, { 0, 0 }, { 1, 0 }, { 1, 1 } }, //SShape
{ { 0, -1 }, { 0, 0 }, { 0, 1 }, { 0, 2 } }, //LineShape
{ { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, -1 } }, //TShape
{ { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } }, //SquareShape
{ { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }, //LShape
{ { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } } //MirroredLShape
};
for (int i = 0; i < 4 ; i++) {
for (int j = 0; j < 2; ++j)
coords[j] = coordsTable[shape][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[0]);
}
return m;
}

int Shape::MaxX() const
{
int m = coords[0][0];
for (int i=0; i<4; i++) {
m = max(m, coords[0]);
}
return m;
}

int Shape::MinY() const
{
int m = coords[0][1];
for (int i=0; i<4; i++) {
m = min(m, coords[1]);
}
return m;
}

int Shape::MaxY() const
{
int m = coords[0][1];
for (int i=0; i<4; i++) {
m = max(m, coords[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.cpp
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_ESCAPE:
{
//wxCommandEvent et;
//et.SetEventType(wxEVT_CLOSE_WINDOW);
//et.SetId(GetId());
//GetParent()->GetEventHandler()->ProcessEvent(et);
GetParent()->Close(true);
break;
}
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);
OneLineDown();
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 = 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);
}
//teris.cpp
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();
}
IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
srand(time(NULL));
Tetris *tetris = new Tetris(wxT("俄罗斯方块"));
tetris->Centre();
tetris->Show(true);
return true;
}
上面是它的源代码,在主程序中我把MyApp屏蔽掉了,使用下面的方法调用
void MyFrame::OnGame(wxCommandEvent& WXUNUSED(event))
{
srand(time(NULL));
Tetris *tetris = new Tetris(wxT("俄罗斯方块"));
tetris->Centre();
tetris->Show(true);
}
一旦按ESC退出俄罗斯方块时,主程序也退出了。但关闭主程序窗口时,俄罗斯方块不会自动关闭???
我的意思是这是一个办公软件,在老板不在的时候可把它调出玩一下,老板来了,按ESC回到主界面。而且需要把它集成到一个文件中???
如何实现,请各位高手指教。

fancyivan
Experienced Solver
Experienced Solver
Posts: 80
Joined: Wed May 26, 2010 8:42 am
Location: Beijing, China
Contact:

Re: 这个俄罗斯方块如何集成到我的应用程序中(使用一个文件)?

Post by fancyivan » Mon Jul 25, 2011 2:00 am

whz wrote: 上面是它的源代码,在主程序中我把MyApp屏蔽掉了,使用下面的方法调用
void MyFrame::OnGame(wxCommandEvent& WXUNUSED(event))
{
srand(time(NULL));
Tetris *tetris = new Tetris(wxT("俄罗斯方块"));
tetris->Centre();
tetris->Show(true);
}
一旦按ESC退出俄罗斯方块时,主程序也退出了。但关闭主程序窗口时,俄罗斯方块不会自动关闭???
我的意思是这是一个办公软件,在老板不在的时候可把它调出玩一下,老板来了,按ESC回到主界面。而且需要把它集成到一个文件中???
如何实现,请各位高手指教。
主要原因我觉得是"Tetris继承自wxFrame",所以有主程序和tetris两个frame存在。
达到你要的效果有很多方法,我觉得最干净的方法就是修改一下源码,使它继承自wxPanel,这样在new Tetris()时将它放到你的主界面MyFrame的某个wxSizer里去并设为可见,同时将你的主程序设为不可见。然后对你程序的ESC按键处理一下就行。

如果不想修改源码,依然继承自wxFrame,也可以。

如下:
在class MyFrame定义中添加private: Tetris *tetris;
OnGame事件中:
void MyFrame::OnGame(wxCommandEvent& WXUNUSED(event))
{
srand(time(NULL));
if(!tetris)
{
tetris = new Tetris(wxT("俄罗斯方块"));
tetris->Centre();
}
tetris->Show(true);
//也许用ShowModal的方式更好?
}
在MyFrame::~MyFrame析构中添加:
if(tetris)
{
tetris->Close();
delete tetris;
}


关于按ESC键达到你想要的效果,你需要对按键事件进行处理才可以。
在你的MyApp中添加:

//最高级别的事件过滤器,可能接收到所有的事件信息
int MyApp::FilterEvent(wxEvent &event)
{
if((event.GetEventType() == wxEVT_KEY_DOWN) && (((wxKeyEvent&)event).GetKeyCode() == WXK_ESCAPE))
{
//如果按下的是ESC键,交给MyFrame::OnESC方法来处理
你的Frame->OnESC();
return true;
}
return -1;
}
//这是你的OnESC方法
void MyFrame::OnESC()
{
//如果没打开tetris,不做任何处理
if(!tetris)
{
return;
}
//如果正在玩tetris
tetris->Pause();先暂停
tetris->Show(false);

//你如果想彻底kaka掉它,就直接用
delete tetris;
tetris=null;
}

代码未经测试,拒绝负责。
OS: Win7 Ultimate SP1 x64(Windows XP Pro SP3 in VirtualBox)
Compiler: MinGW32 (gcc4.8.1 + gdb7.6.1)
IDE: Code::Blocks 12.11
Lib: wxWidgets3.0.0

whz
Earned a small fee
Earned a small fee
Posts: 11
Joined: Tue Oct 05, 2010 1:01 am

Re: 这个俄罗斯方块如何集成到我的应用程序中(使用一个文件)?

Post by whz » Fri Jul 29, 2011 2:12 am

谢谢回复!
使用以下方法解决:
在MyFrame 中增加以下变量
wxAuiManager* m_mgr;
void MyFrame::OnGame(wxCommandEvent& WXUNUSED(event))
{
srand(time(NULL));
Board *board = new Board(this);
board->SetFocus();
board->Start();
m_mgr.AddPane(board, wxAuiPaneInfo().
Caption(wxT("俄罗斯方块")).
//Float().FloatingPosition(GetStartPosition()).
Float().FloatingPosition(wxDefaultPosition).
FloatingSize(wxSize(180, 380)));
m_mgr.Update();
board->SetFocus();
}

whz
Earned a small fee
Earned a small fee
Posts: 11
Joined: Tue Oct 05, 2010 1:01 am

Re: 这个俄罗斯方块如何集成到我的应用程序中(使用一个文件)?

Post by whz » Tue Feb 05, 2013 2:58 am

上面的程序还有一个问题,按ESC时用GetParent()->Close(true);将此panel关闭了,但程序退出时会出现Segmentation fault!为什么?
当使用鼠标点X关闭此panel时则不会出现错误提示

ollydbg23
I live to help wx-kind
I live to help wx-kind
Posts: 188
Joined: Fri Dec 12, 2008 10:31 am

Re: 这个俄罗斯方块如何集成到我的应用程序中(使用一个文件)?

Post by ollydbg23 » Mon Feb 18, 2013 2:44 am

贴代码建议用标签,否则帖子看的我头晕,呵呵。

Post Reply