Frage

Ich bin Unterfangen, ein Spiel mit einer Kombination aus c++ in visual Studio 2010 und Qt 4.7 (beide windows).Das Spiel ist ein Klon Schlachtschiff und Konsole Eingabe basiert.Ich habe meine gui, wie ich es Aussehen soll, und auf der Qt-Seite in Qt designer, meine gui besteht aus einem grid-layout 10x10, mit Etiketten zu halten pixmaps des Spiels Zellen:

current look of the game

Ich habe mühsam benannt jedes label vertreten, seine position in der 2d-array (dh.Flotte map => F_00 => F[0,0] => F[i],[j]).Ich kann manuell wählen Sie, was pixmap ich möchte die Anzeige über den Eigenschaften-editor, aber ich würde gerne etwas dynamischer.

Ich benutze ein update mapboard Klasse neu zu zeichnen, die Spiel Bord, nachdem ein Spieler feuert, hält die Lagerung über ein char-array.Ich würde wie zu aktualisieren mein pixmaps für jeden, der mit einem generischen getupdatearray Typ Funktion.Als wir Durchlaufen das array wird aktualisiert pixmap derzeit im Zusammenhang mit individuellen Etiketten passend zu Ihren Vettern aus dem array.(sagen F[5][6] = 'X' für Treffer, dann, wenn die loops habe die position im array, es wäre update das raster pixmaps bei F_56 gleich Treffer.png, ersetzen Sie die leer.png.

Ich habe eine Idee, wie die Schleife, die dies erreichen sollte, aber nicht sicher, wie ich gehen würde, über den von pixmap-für jedes Etikett, um mehr entlang der Linien von einem runtime-Funktion, gegen das jetzt der Kompilierung (statisch) Funktion.Ich habe gelesen, über QPainter und anderen Qt-Klasse, die sich mit Bildern, aber immer noch schwer, gehen Sie.

Frage an alle von Euch, wie kann ich diese aktualisieren pixmaps basiert auf einem 2d-array?

  1. loop-Struktur - ich herausfinden kann
  2. Bedingungsanweisungen - ich herausfinden kann
  3. qt-spezifische syntax, den Umgang mit Etiketten - Neuling, also keine Ahnung atm.

Hier ist ein pseudocode von dem, was ich versuche zu tun, mit Karte.h:

#include <QtCore>
#include <QtGui>

// WARNING: PSEUDOCODE, DOES NOT COMPILE
// AT A LOSS ON HOW TO SELECT THE CORRECT LABEL
// MAYBE A CHILD CLASS FOR THAT?

class map {
public:
    char updateboard(char mapname, char b[][10]){
        for(int i=0;i<10;i++){
            for(int j=0;j<10;j++){
                char C = b[i][j]; 
                if (C == 'M'){//miss
                    Qlabel mapname_[i][j](<img src='images/missspace.png'/>);
                    mapname_[i][j].show();
                } 
                else if(C == 'X'){//hit
                    Qlabel mapname_[i][j](<img src='images/hitspace.png'/>);
                    mapname_[i][j].show();
                }
                else if(C == ' '){//undiscovered space
                    Qlabel mapname_[i][j](<img src='image/emptyspace.png'/>);
                    mapname_[i][j].show();
                }
            }
        }
    }
};

Dann in meinem mainwindow.cpp habe ich auch anzeigen.h und sagen:

// calls class function update board
// takes updated array values and replaces old pixmap with new

map.updateboard(T,b[][10]); // target map update
map.updateboard(F,v[][10]); // fleet map update

Vielen Dank im Voraus

UPDATE:

Ich bin zu dem Punkt, wo ich tauschen kann pixmaps mit Tasten drückt, aber ich würde gern etwas mehr Dynamik.Ich wollte einen Qstring in die ich die Namen der label, die ich ändern möchten die Verwendung von Anhängen x y-Werte verwenden:

TR_position.append(QString::number(xvalue));

Wenn ich zu nennen versuchen Sie es mit:

ui->TR_position->setPixmap(QPixmap(":/images/missspace.png"));

...es offensichtlich nicht funktioniert.Gibt es eine Möglichkeit zu geben, falls es, oder verwenden Sie den Inhalt der Zeichenfolge als Qlabel name?

War es hilfreich?

Lösung

Sie manuell eingetragen und benannt 200-label-widgets?Niemand nennen Sie faul.:)

Pro update, die Sie jetzt wissen, wie Sie zu verwenden QLabel::setPixmap().Was Sie denken Sie müssen immer ein QLabel-Zeiger-aus einen Namen, die eine Kombination von zwei Dingen:

Wenn Sie diesen Weg gehen, was würden Sie am Ende mit so etwas wie:

QWidget* cellWidget = ui->findChild(TR_position);
QLabel* cellLabel = qobject_cast<QLabel*>(cellWidget);
cellLabel->setPixmap(QPixmap(":/images/missspace.png"));

Aber VORSICHT!Es gibt viele Dinge, die falsch mit diesem Ansatz.

  • Es ist spröde:Was, wenn es nicht passiert, werden alle widgets, die mit diesem Namen (mysteriöse Absturz)?Oder noch schlimmer, was, wenn es sind mehrere widgets-mit diesem Namen und dieser code Märsche entlang selig unwissend von dieser seltsamen Zustand, dass ist wahrscheinlich ein bug?

  • Es ist schlechte OOP:Zwar gibt es einige anständige Fällen eine dynamische Gießen (oder "downcasting"), in der Regel gibt es einen Fehler in einem design.Sie wissen, dass alle QLabels sind QWidgets, aber nicht alle QWidgets sind QLabels, so dass... qobject_cast rufen Sie könnte Rückkehr NULL.Es ist nur eine weitere Fehlerquelle.Manchmal kann man nicht vermeiden, aber gibt es wirklich keinen Grund, Ihr Programm muss in einer Weise strukturiert.

  • Es ist furchtbar langsam:Suchen Sie ein widget, indem sein name ist im wesentlichen eine naive rekursive Suche.Wenn Sie schon beiseite legen, ein separates widget-Rahmen für jedes Netz und Suche nur dass, Qt zu tun haben 100 string vergleicht zu finden, das Letzte element (also 50 im durchschnittlichen Fall).Sich vorstellen clearing-raster mit einer Schleife...jetzt sprechen Sie über 100*50 string vergleicht!

All diese Dinge, die vermeidbar sind.Genauso wie es möglich ist, verwenden Sie Schleifen, um die Bilder auf die Kontrollen durch die name, es ist möglich, Schleifen zu erstellen, die widgets in den ersten Platz.Sie im Grunde würde das Gebiet verlassen das Spielfeld leer in die design-tool, und dann dynamisch erstellen Sie die Steuerelemente mit code - ...legen Sie das layout mit code...und speichern Sie die Zeiger, um Sie in 2D-array.(Sie würden nicht auf Sie zugreifen, indem Sie label-Namen an diesem Punkt, Sie würde indizieren Sie so wie Sie sind, der Indizierung des board.)

Könnte man erstellen Sie Ihre eigene Klasse, die von QLabel (wie ein GameCell Klasse), die die Informationen für dein board Zelle und Methoden in Bezug auf Sie.Dann würden Sie nicht brauchen, ein array von label-widgets parallel zu einem array mit deinem board.Sie würde einfach ein array von Objekten, kümmerte sich um beide Aspekte der Umsetzung.


UPDATE:Da fragte man in den Kommentaren für Besonderheiten, hier ist ein GameCell-Klasse:

class GameCell : public QLabel
{
    Q_OBJECT    

public:
    enum State { Undiscovered, Hit, Miss };

    GameCell (QWidget *parent = 0) : QLabel (parent),
        currentState (Undiscovered)
    {
        syncBitmap();
    }

    State getState() const { return currentState; }

    void setState(State newState) {
        if (currentState != newState) {
            currentState = newState;
            syncBitmap();
        }
    }

private:
    void syncBitmap() { // you'd use setPixmap instead of setText
        switch (currentState) {
        case Undiscovered: setText("U"); break;
        case Hit: setText("H"); break;
        case Miss: setText("M"); break;
        }
    }

    State currentState;
};

Dies tut doppelte Aufgabe, indem Sie sich wie ein QWidget als auch die Pflege ein Stück internen Zustand.Dann eine GameMap widget verwenden können, ein QGridLayout dieser GameCells:

class GameMap : public QWidget {
    Q_OBJECT

public:
    static const int Rows = 10;
    static const int Columns = 10;

    GameMap (QWidget* parent = 0) :
        QWidget (parent)
    {
        layout = new QGridLayout (this);
        for (int column = 0; column < Columns; column++) {
            for (int row = 0; row < Rows; row++) {
                GameCell* cell = new GameCell (this);
                cells[column][row] = cell;
                layout->addWidget(cell, row, column);
            }
        }
    }

private:
    GameCell* cells[Columns][Rows];
    QGridLayout* layout;
};

Wenn Sie wollten, könnten Sie nur lassen Sie Leerzeichen in Ihrem layout im designer, Sie wollte zu füllen, die mit dem GameMap widget.Oder können Sie push-und die ganze Sache programmgesteuert.Der Einfachheit halber werde ich nur zwei Bretter nebeneinander mit einer senkrechten Trennlinien auf der Oberfläche eines dialogs:

class Game : public QDialog
{
    Q_OBJECT

public:
    Game (QWidget *parent = 0)
        : QDialog(parent)
    {
        targetMap = new GameMap (this);
        fleetMap = new GameMap (this);

        verticalSeparator = new QFrame (this);
        verticalSeparator->setFrameShape(QFrame::VLine);
        verticalSeparator->setFrameShadow(QFrame::Sunken);

        layout = new QHBoxLayout (this);
        layout->addWidget(targetMap);
        layout->addWidget(verticalSeparator);
        layout->addWidget(fleetMap);
        setLayout(layout);

        setWindowTitle(tr("Battleship"));
    }

private:
    GameMap* targetMap;
    QFrame* verticalSeparator;
    GameMap* fleetMap;
    QHBoxLayout* layout;
};

Ich werde nicht schreiben Sie ein ganzes Spiel hier oder es sich einbilden.Das ist gerade das wesentliche, die zeigen, wie man 200 Etiketten in einer programmatischen Art und Weise:

simple battleship

Mit meinem code immer eine GameCell aus einem (x,y) - Koordinate nicht erforderlich ist, ein Durchschnitt von 50 string vergleicht.Aufgrund der formalisierten und vorhersehbare Natur des 2D-arrays, die Indizierung in cells[x][y] erfordert nur eine einzige Multiplikation und eine einzelne addition.Es gibt keine downcasting, und Sie können einfach schreiben:

cells[x][y].setState(GameCell::Miss);

NACHTRAG:Erstellen eines QWidget für jede raster-Zelle ist nicht unbedingt der Weg zu gehen in den ersten Platz.Einige sind vielleicht der Auffassung, dass "Schwergewicht".Wenn Ihr das Spiel gespielt auf einer großen virtuellen Raum der Fliesen, dann könnte es viel zu langsam.Vielleicht finden Sie es nützlich zu schauen, QGraphicsGridLayout,, das könnte ein geeigneter Ansatz in der lange run:

http://doc.qt.io/qt-5/qtwidgets-graphicsview-basicgraphicslayouts-example.html

Mit QWidgets nicht viel von einem Problem mit einem 10x10-raster, allerdings, also, wenn Sie wollen einfach nur mit dem stick, dann können Sie.Wenn du gehst, um es so zu machen, dann wenigstens sollten Sie nicht platziert werden Ihnen alle von hand!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top