Сейчас я пишу программу-тральщик. Есть функция, которая связана с меню. Когда пользователи нажимают на меню «Уровень», программа покажет подменю из нескольких разных уровней (что означает разное количество блоков). Сейчас я только определил функцию «SetNovice», но обнаружил проблему, заключающуюся в том, что мины не будут снова распределяться случайным образом, как это делает конструктор (который привязывает кнопки с функцией «ButtonOnClicked» для установки мин).
В SetNovice я сначала вызываю функцию «RemoveChild()», чтобы удалить btn в GUIPanel, затем удаляю массив «MineField», а затем снова создаю btn и MineField, как это делает конструктор, но мин просто нет. Мины должны быть установлены при вызове функции "ButtonOnClicked". Как я писал в SetNovice при создании btn, я сделал "Bind" btn to ButtonOnClicked, но никакие мины не устанавливаются.
Предполагается, что программа случайным образом сбрасывает мины при вызове SetNovice, но оказывается, что это не так. Почему?
«MineField» — это массив целых чисел, где «-1» — это место, где находятся мины. Но я не уверен, почему в функции «SetNovice», когда я пытаюсь удалить исходный MineField, чтобы изменить размер на новый, функция, к которой привязаны кнопки, не будет устанавливать мины.
Ниже мой GUIWindow.h
#pragma once
#include "wx/wx.h"
#include "wx/mediactrl.h"
#include "wx/hyperlink.h"
#include <wx/thread.h>
#include "cuppMediaCtrl.h"
class GUIWindow : public wxFrame
{
public:
GUIWindow();
~GUIWindow();
public:
int Width = 20; // default
int Height = 20; // default
int score = 0;
int count = 0;
wxButton **btn; // an array of pointers
int* MineField = nullptr; // a pointer, determining if mines exist
bool ISFirstClick = true;
wxPanel* GUIPanel = new wxPanel(this, wxID_ANY);
wxGridSizer* grid;
wxGridSizer* gridNovice;
wxStatusBar* statusBar = CreateStatusBar(3);
void ButtonOnClicked(wxCommandEvent &evt);
void OnQuit(wxCommandEvent& event);
void ChangeBtnColorRed(wxCommandEvent& evt);
void ChangeBtnColorWhite(wxCommandEvent& evt);
void ChangeBtnColorGreen(wxCommandEvent& evt);
void ChangeBtnColorBlue(wxCommandEvent& evt);
void ChangeBtnColorTokyoHot(wxCommandEvent& evt);
void SetNovice(wxCommandEvent& evt);
void SetIntermediate(wxCommandEvent& evt);
void SetProfessional(wxCommandEvent& evt);
void SetMaster(wxCommandEvent& evt);
void SetGodLevel(wxCommandEvent& evt);
void Website1(wxCommandEvent& evt);
void Website2(wxCommandEvent& evt);
void ShowCreator(wxCommandEvent& evt);
DECLARE_EVENT_TABLE();
};
Ниже приведен GUIWindow.cpp (я удалил несвязанный код и функции)
#include "GUIWindow.h"
wxBEGIN_EVENT_TABLE(GUIWindow, wxFrame)
EVT_BUTTON(wxID_ANY, ButtonOnClicked)
wxEND_EVENT_TABLE()
GUIWindow::GUIWindow() : wxFrame(nullptr, wxID_ANY, "地雷遊戲", wxPoint(30, 30), wxSize(700, 700))
{
btn = new wxButton *[Width * Height]; // btn == an array of pointers (should allocate new memory)
grid = new wxGridSizer(Width, Height, 0, 0); // set a grid pointer to control the grid
MineField = new int[Width * Height]; // This is where mines are distributed, using nField to indicate mines
wxFont font(18, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
wxString str1 = "Current points: ";
statusBar->SetStatusText(str1, 0);
wxMenuBar* menuBar = new wxMenuBar();
wxMenu* menu1 = new wxMenu();
menuBar->Append(menu1, wxT("About"));
wxMenu* menu2 = new wxMenu();
menuBar->Append(menu2, wxT("Game"));
SetMenuBar(menuBar);
menu1->Append(10001, wxT("Creator"));
wxMenu* imp2 = new wxMenu();
imp2->Append(10011, wxT("Tokyo Hot"));
imp2->Append(10012, wxT("White"));
imp2->Append(10013, wxT("Green"));
imp2->Append(10014, wxT("Red"));
imp2->Append(10015, wxT("Blue"));
menu2->AppendSubMenu(imp2, wxT("Theme"));
wxMenu* imp3 = new wxMenu();
imp3->Append(10020, wxT("Novice"));
imp3->Append(wxID_ANY, wxT("Intermediate"));
imp3->Append(wxID_ANY, wxT("Professional"));
imp3->Append(wxID_ANY, wxT("Master"));
imp3->Append(wxID_ANY, wxT("God-Level"));
menu2->AppendSubMenu(imp3, wxT("Level"));
wxMenu* imp4 = new wxMenu();
imp4->Append(10002, "Introduction");
imp4->Append(10003, "Explanation");
menu1->AppendSubMenu(imp4, "Hints");
wxMenuItem* quit = new wxMenuItem();
quit = new wxMenuItem(menu1, wxID_EXIT, wxT("Quit\tCtrl+Q"));
menu1->Append(quit);
Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::OnQuit));
Centre();
Connect(10001, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ShowCreator));
Connect(10011, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorTokyoHot));
Connect(10012, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorWhite));
Connect(10013, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorGreen));
Connect(10014, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorRed));
Connect(10015, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorBlue));
Connect(10020, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::SetNovice));
Connect(10002, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::Website1));
Connect(10003, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::Website2));
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x)); // every pointer points to a button object with class wxButton
btn[y * Width + x]->SetFont(font);
grid->Add(btn[y * Width + x], 1, wxEXPAND, wxALL);
btn[y * Width + x]->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &GUIWindow::ButtonOnClicked, this);
MineField[y * Width + x] = 0; //set each block to 0 (default value)
}
}
GUIPanel->SetSizer(grid); // "this" is pointing at the parent wxFrame: GUIWindow
GUIPanel->Show();
}
GUIWindow::~GUIWindow()
{
delete[] btn;
}
void GUIWindow::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void GUIWindow::SetNovice(wxCommandEvent& evt)
{
Width = 10;
Height = 10;
score = 0;
GUIPanel->DestroyChildren(); // delete btn, but NOT removing grid.
delete[] MineField;
gridNovice = new wxGridSizer(Width, Height, 0, 0);
wxFont font(18, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
btn = new wxButton* [Width * Height];
MineField = new int[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x)); // every pointer points to a button object with class wxButton
btn[y * Width + x]->SetFont(font);
gridNovice->Add(btn[y * Width + x], 1, wxEXPAND, wxALL);
btn[y * Width + x]->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &GUIWindow::ButtonOnClicked, this);
MineField[y * Width + x] = 0; //set each block to 0 (default value)
}
}
GUIPanel->SetSizer(gridNovice);
GUIPanel->Show();
}
void GUIWindow::ButtonOnClicked(wxCommandEvent &evt)
{
// get position of button
int x = (evt.GetId()) % Width;
int y = (evt.GetId()) / Width;
int mine_count = 0;
wxString s1 = "Believe in Yourself.";
wxString s2 = "You are the PRIDE of NTUEE !";
wxString s3 = "TAIWAN need you!!! so DO NOT give up!!!";
wxString s4 = "We are all together, blessing you.";
wxString s5 = "Who's the best minesweeper ? YOU!!!";
wxString s6 = "Get bored? Try other functions in the menu!";
wxString s7 = "想脫魯 ? 踩地雷吧 XDXDXD";
wxString phraseArr[] = { s1, s2, s3, s4, s5, s6, s7};
if (ISFirstClick == true)
{
int mine = Width * Height / 2;
while (mine != 0)
{
int rx = rand() % Width;
int ry = rand() % Height;
if (MineField[ry * Width + rx] == 0 && rx != x && ry != y)
{
MineField[ry * Width + rx] = -1; // -1 indicates the mine
mine--;
}
}
ISFirstClick = false; // avoid mines been redistributed again
}
btn[y * Width + x]->Enable(false);
if (MineField[y * Width + x] == -1)
{
wxMessageBox("BOOM!");
statusBar->SetStatusText(wxT(""), 2); // reset the status bar
this->count++;
//reset mines
this -> score = 0;
ISFirstClick = true;
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
MineField[y * Width + x] = 0;
btn[y * Width + x]->SetLabel("");
btn[y * Width + x]->Enable(true);
}
}
}
else
{
if (this->count < 7)
{
wxString Newstr = phraseArr[this->count];
statusBar->SetStatusText(Newstr, 2);
this->count++;
}
else if (this->count >= 7) this->count = 0;
this -> score += 1;
wxString scoreStr = std::to_string(this->score);
statusBar->SetStatusText(scoreStr, 1);
//count the mines surrounding
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
if (x + i >= 0 && x + i < Width && y + i >= 0 && y + j < Height)
{
if (MineField[(y + j) * Width + (x + i)] == -1)
{
mine_count++;
}
}
}
}
if (mine_count >= 0)
{
btn[y * Width + x]->SetLabel(std::to_string(mine_count));
}
}
evt.Skip();
}
Попробуйте добавить
GUIPanel->Layout();
до конца метода GUIWindow::SetNovice
. Это приведет к тому, что все кнопки, добавленные на панель, будут упорядочены по размеру сетки.
Также линия
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x));
потенциально можно настроить кнопки так, чтобы они имели идентификаторы, которые уже назначены другим вещам. Вы должны использовать что-то вроде:
btn[y * Width + x] = new wxButton(GUIPanel, wxID_ANY);
вместо. Если вам нужно отслеживать координаты кнопки для метода ButtonOnClicked
, вы можете сделать что-то вроде
std::map<wxWindowID,std::pair<int,int>> m_buttonCoords;
в класс GUIWindow.GUIWindow::SetNovice
очистить карту в начале метода и в цикле создания кнопок вызвать m_buttonCoords.insert( std::make_pair(btn[y * Width + x]->GetId(),std::make_pair(x,y)) );
GUIWindow::ButtonOnClicked
извлеките координаты следующим образом:std::pair<int,int> coords = m_buttonCoords[evt.GetId()];
int x = coords.first;
int y = coords.second;
Возможно, есть лучшие способы, но это первое, о чем я подумал.
Я следовал шагу. Я изменил способ установки идентификаторов всех кнопок в загрузочном SetNovice и конструкторе; однако кажется, что мины все еще не установлены, когда я выбираю уровень новичка. Я действительно понятия не имею об этом.
Кстати, я использовал m_buttonCoords.clear(); в начале SetNovice.
есть ли какой-либо C? Пожалуйста, отмечайте только тот язык, который вы используете