2010-12-29 12 views
1

Ich versuche, ein Kartenspiel zu schreiben, wo ein Spieler Karten stapeln kann. Z.B. Ass, Zwei, Drei..Net Windows Formulare benutzerdefinierte Layout-Engine

Ich möchte einen Stapel von Karten visualisieren, wo die Ace-Karte teilweise durch die Zwei-Karte abgedeckt ist, und die Zwei-Karte ist teilweise durch die Drei-Karte abgedeckt. Die Drei-Karte ist vollständig sichtbar.

Einfach, dachte ich. Ich mache eine Benutzerkontrolle, wo ich meine Karten hinzufüge: Controls.Add (Ass); Controls.Add (zwei); usw.

Dann brauche ich etwas, das in der Lage ist, meine Steuerelemente auszulegen, also schrieb ich meine eigene LayoutEngine (abgeleitet von LayoutEngine). Mein erster Test tut nichts mehr, dann verschiebe das Steuerelement 50 Pixel.

Nach dem Ausführen der Lösung bemerkte ich, dass die Z-Bestellung falsch war. Anstelle der drei Karte oben zu sein, war das Ass an der Spitze wie folgt aussehen:

Ace Card> Zwei Karten> Drei-Karte, wo: Ace-Karte ist auf der Oberseite Zwei-Karte ist unter dem Ace-Karte Three Card ist unter der Zwei-Karte.

Also begann ich nach einem Weg zu suchen, um die Z-Reihenfolge in WinForms zu ändern und fand heraus, dass es einfach "nicht verfügbar" ist. Wie .. Huh ?!

Die Alternative (von MS bereitgestellt) ist, dass die Z-Reihenfolge geändert werden kann, indem Sie den ChildIndex für die Steuerelemente festlegen. Jikes, also das Stöbern in einer Liste, ändern das Verhalten meiner Anwendung. Way to go MS ...

Wie auch immer, ich habe versucht alle möglichen Dinge, aber es scheint unmöglich, eine Layout-Engine zu schreiben, die den Trick macht.

Ich habe google-d den ganzen Tag, und nichts nützliches gefunden. Ich bin kein GUI-Experte, also bleibe ich bei diesem lahmen Thema hängen. Wer kann mir helfen?

Sehr geschätzt!

Bas

+2

Haben Sie sich das Starterset für Kartenspiele für Windows-Formulare angesehen?http://msdn.microsoft.com/en-us/vcsharp/aa336742 – MattDavey

Antwort

5

Ihre beste Wette ist die Verwendung von Kontrollen ganz zu vermeiden. Sie werden A) zu einer schlechten Leistung führen und B) das Testen/Zeichnen von Treffern erschweren.

Erstellen Sie einfach Objekte, um den Status der Tabelle darzustellen (ich verwende ein CardContainer-Objekt) und Graphics.DrawImage zu verwenden, um alle Karten zu zeichnen, wo sie während des Paint-Ereignisses liegen. Sie können ein einzelnes Steuerelement für die gesamte Tabelle verwenden, wenn Sie auch andere UI-Elemente hinzufügen müssen.

Dadurch wird auch die Animation der Kartenbewegung einfacher, wenn Sie sich für die Animation entscheiden.

Aktualisiert

ich diese Antwort erweitern gedacht, sondern wurde abberufen und einfach geschrieben, was ich hatte. Hier sind einige Details, die Sie nützlich finden können. Ich habe eine "Solitaire Game Engine" erstellt. Die Engine beherbergt jeweils ein Solitaire-Spiel (Klondike, Spider, Strategie, etc.). Es verfolgt Statistiken für jedes Spiel und ermöglicht sowohl das Spielen als auch das Editieren der einzelnen Spiele. Die Spiele sind IronPython-Skripte, die das Hinzufügen neuer Spiele relativ einfach machen.

Mein CardContainer ist ein Objekt, das keine oder mehrere Karten enthält.

Es hat eine LieDirection (Keine, Auf, Ab, Links, Rechts), die bestimmt, wie seine Karten ausgelegt sind.

Es hat eine MaximumDepth, die die Anzahl der in der LieDirection gezeichneten Karten festlegt. Dies ist praktisch für Spiele wie Klondike, wo Sie nur die obersten 3 Karten der Verschwendung zeigen wollen.

Es hat Eigenschaften für den Abstand der Karten. Für aufgedeckte und verdeckte Karten gibt es separate Abstände. Es kann Karten automatisch in einen durch MaximumLength definierten Bereich packen. Und es hat einen "extra Pad" -Wert, einen für jede Karte - ob es eine Karte bei diesem Index gibt oder nicht. Letzterer wird während eines simulierten Maus-Schwebens verwendet, um die Karte, auf die gezeigt wird, "aufzudecken", so dass der Benutzer deutlich eine Karte sehen kann, die durch Karten darüber verdeckt sein könnte. Dies wird erreicht, indem das "extra Pad" der Karte oben auf der Karte gesetzt wird. Dies könnte durch eine "Hover-Karte" und "Hover-Spacing" -Eigenschaft vereinfacht worden sein, aber eine zusätzliche Auffüllung pro Karte ermöglicht es, Solitaire-Spiele mit einer bestimmten 'Reihe' in den Tableau-Stapeln mit Abstand zu markieren.

Es verfügt über eine HitTest-Methode, um eine Karte von einer bestimmten X, Y-Position zurückzugeben.

All dies bedeutet, dass das Card-Objekt keine Ahnung hat, wo es auf dem Tisch gezeichnet wird. Ich habe ein komplexes Animationssystem und der Standort einer Karte kommt letztendlich von der Animations-Engine. Wenn eine Karte gerade nicht animiert, erhält das Animationssystem seinen Standort aus seinem Container.

Beachten Sie, dass die oben genannte Position der Karte ausschließlich zum Zeichnen dient. Alle Karten sind immer an genau einen CardContainer angehängt und werden einfach von einem zum anderen bewegt. Es gibt einen "speziellen" Container namens Deck, der anfangs jede Karte enthält. Es ist zunächst vom Tisch entfernt positioniert. Ein Container verfügt über eine Visible-Eigenschaft. Animationen werden nur abgespielt, wenn eine Karte aus einem sichtbaren Container in einen anderen sichtbaren Container verschoben wird. Dies ermöglicht es Ihnen, Karten bei Bedarf ohne Animation zu bewegen, und Karten können von/zu den vom Tisch positionierten Behältern "herausfliegen".

Der Motor hat auch ein rudimentäres Layout-System für die Positionierung CardContainer relativ zueinander. Eine sehr praktische Sache, die ich tat, war, ein Kartengröße-relatives Koordinatensystem zu verwenden. Die 'Breite' des Tisches beträgt genau 11 Kartenbreiten. Egal wie groß der Benutzer den Tisch auslegt, die Breite beträgt immer 11 Kartenbreiten. Dies bedeutet, dass die Kartengrößen (wie vom Benutzer gesehen) wachsen und schrumpfen. Die Höhe ist variabel, wird aber durch ein festes Kartenformat bestimmt (bestimmt durch die Karten-Bitmaps). Wenn Sie einem CardContainer einen X-Wert von 1.0 geben, bedeutet dies, dass er eine Kartenbreite von der linken Seite der Tabelle entfernt liegt. Die Werte sind Gleitkommawerte, so dass Sie 1/2 Kartenbreite mit 0.5 angeben können. Dies macht es sehr einfach, Elemente im Skript zu positionieren, ohne sich um Bildschirmkoordinaten kümmern zu müssen. Unabhängig davon, wie der Benutzer die Größe des Bildschirms ändert, wird Ihr Spiel genau so angelegt.

Der Motor hat auch unbegrenzte Undo und Redo. Dies bedeutet, dass nicht nur Kartenbewegungen (von einem Container zum anderen) aufgezeichnet werden müssen, sondern auch alle Eigenschaftsänderungen (sowohl Karten- als auch Containereigenschaften) aufgezeichnet werden. Rückgängig machen und Wiederherstellen kann schwierig sein, wenn es nicht von Anfang an geplant ist. Die Skripts haben Zugriff auf eine Game.LogVariableChange-Methode, sodass sie den Wert einer globalen Variablen über den Aufzeichnungsmechanismus ändern können. Dies ist notwendig für so etwas wie Klondikes "Drei-Erledigungen". Das Skript muss die Anzahl der verwendeten Redigierungen speichern. Wenn der Benutzer jedoch eine erneute Eingabe rückgängig gemacht hat, muss auch die Wertänderung dieser Variablen rückgängig gemacht werden.

Dies funktioniert sehr gut für Solitaire, könnte aber für fast jede Art von Kartenspiel funktionieren. Natürlich müssen Sie das alles beim ersten Mal nicht umsetzen. Ich präsentiere die Informationen, um Ihnen ein paar Ideen zu geben.

+1

+1 für die Trennung des Spielstatus von der Benutzeroberfläche. – Ran

+0

Danke für die Antwort! Ich habe versucht, was Sie vorgeschlagen haben, und funktioniert wie ein Zauber. Ich habe jetzt auch einen CardContainter und er zieht meine Karten wie beabsichtigt! Cooles Zeug! Danke noch einmal. –

+0

@Ran: Meine Gedanken genau. –

Verwandte Themen