Autor Beitrag
Testobjekt
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Di 30.12.14 13:27 
Hallo!
Ich bin dabei in C# Schiffe versenken zu programmieren.
Jetzt stellt sich bei mir die Frage wie ich größere "Schiffe" darstellen kann.

Ich hab ein 10x10 großes Feld, sprich 100 Pictureboxen.
Wenn ich ein horizontal gelegenes 4 Felder langes Schiff erstellen will, wie kann ich die 3 weiteren Felder (das Feld wo ich draufklick hab ich ja) herausfinden/speichern?

Ich hab da keinen Ansatz, ich denke mir es kann vielleicht mit einem zweidimensionalem Array funktionieren, jedoch hab ich damit noch nicht viel gearbeitet.

Auf rasche Antworten danke ich bereits im Voraus! :D

mfg
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Di 30.12.14 14:29 
Hallo und :welcome: hier in der EE

Du solltest bei Spielen (auch bei Minispielen wie Schiffe versenken) auf jeden Fall Logik und Grafik so gut wie möglich trennen. Verwende keine PictureBoxen. Die brauchen viele Ressourcen und du kannst nicht unbegrenzt viele auf deinem Fenster platzieren!

Prinzipiell kannst du so vorgehen:
1. Erstelle für jeden Schiffstyp ein Bild. Ein kleines Schiff hat z.B. 32x32 Pixel, ein Schiff das über 2 Felder geht hat dann 64x32 Pixel, usw.

2. Lade diese Schiffe jeweils in ein Image, z.B. so:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
Image[] gameObjects = new [] {
    Image.FromFile("Pfad zum Wasser (also leere Felder, auf denen kein Schiff ist)"),
    Image.FromFile("Pfad zum kleinen Schiff"),
    Image.FromFile("Pfad zum 2 Felder Schiff"),
    Image.FromFile("Pfad zum 3 Felder Schiff"),
};


3. Lege dir das Spielfeld im Speicher an, also auch als Array. Geschickt wäre hier z.B.:
ausblenden C#-Quelltext
1:
int[,] map = new int[10,10]					

Dadurch kannst du die map mit dem entsprechenden Zahlenwert für ein Element aus gameObjects füttern. Da das Integer Array am Anfang überall 0 ist, wird auch das 0te Element aus dem gameObjects Array verwendet, was zufällig dem Wasser (oder halt leeren Feld) entspricht. Wenn an einer Stelle der Karte jetzt ein kleines Schiff ist, trägst du an der entsprechenden Stelle im Array eine 1 ein. Wenn es ein 2 Felder Schiff ist, eine 2, usw.
Diese Methode erleichtert dir später das zeichnen der Grafik.

4. Zum Darstellen der Grafik musst du nun selbst zeichnen. Dafür musst du das Paint-Event des Controls auf dem du zeichnest Abfangen. Das kann natürlich auch die Form selbst sein.
Benutze ausschließlich e.Graphics zum Zeichnen des Spielfelds.
4.1 Berechne zuerst, wie groß eine Kachel auf der Form ist (also ein Feld aus dem 10x10 Array). Das kannst du einfach mit
ausblenden C#-Quelltext
1:
Size tileSize = new Size(Width / map.GetLength(0), Height / map.GetLength(1));					

machen.
4.2 Nun zeichnest du alle Elemente auf die Form. Dafür iterierst du durch die map (das 10x10 Array):
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Size tileSize = new Size(Width / map.GetLength(0), Height / map.GetLength(1));

            for (int x = 0; x < map.GetLength(0); x++)
            {
                for (int y = 0; y < map.GetLength(1); y++)
                {
                    e.Graphics.DrawImage(gameObjects[map[x, y]], x * tileSize.Width, y * tileSize.Height, tileSize.Width, tileSize.Height);
                }
            }
         }



Ich habe jetzt gerade leider keine Zeit mehr. Ich werde die Anleitung in ein paar Stunden vervollständigen.

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Di 30.12.14 15:09 
Ich würde eher einen etwas anderen Weg wählen:

Jedes Bild hat eine gleiche Größe, Bilder gibt es folgende:
Wasser, Bug, Heck, Mittelteil und ein Ein-Feld-Schiff.
Diese Teile können dann zu verschiedenen Schiffen kombiniert werden.

Dazu würde ich dann eine Klasse erstellen, die ein zweidimensinales Array verwaltet, wo die Zustände drin stehen.
Ein Schiff ist ein Objekt, das für jedes Teil einzelne Objekte bereit stellt, die dann in dem jeweiligen Feld der Matrix stehen. Dort kann auf das ganze Schiff zugegriffen werden, auf die anderen Felder, der Zustand und Richtung des Teil-Bildes.

Zum Zeichnen ist dann nichts weiter nötig, als die Matrix, also das zweidimensionale Array, Element für Element abzulaufen, das Bild auslesen und je nach Ausrichtung zu zeichnen.

Das ist einfacher, als unterschiedliche Bildgrößen nach Ausrichtung und Größe zu zeichnen. Außerdem hat es den Vorteil, dass man so recht einfach die Feldgröße erweitern, oder neue Schiff-Größen hinzu fügen kann.


PS:
Das Zeichnen, was C# erklärt, sollte aber dennoch gemacht werden, 100 Pictureboxen sind recht viel und Forms wird alt :D


Zuletzt bearbeitet von Palladin007 am Di 30.12.14 15:46, insgesamt 1-mal bearbeitet
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Di 30.12.14 15:41 
@C#
Werd das heute probieren, muss dann eben praktisch von vorne beginnen, aber das dachte ich mir auch.. ^^
Vielen Dank bis dahin.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4796
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 30.12.14 16:46 
Hallo,

zum Zeichnen kannst du dir auch mal die von mir geposteten Links unter Hintergrundbild in Spiel? durchlesen.
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Di 30.12.14 16:55 
@Palladin007

Ja das mit einer eigenen Klasse zu realisieren habe ich auch überlegt. Das wäre bei Weiterführung des Projekts auf jeden Fall sinnvoller.
Das Schiff in Einzelteile zerlegen wollte ich nicht, da die Logik und der Aufwand komplizierter werden und ich nicht weiß, wie erfahren der Threadstarter ist.

Beim Zeichnen unterscheiden sich unsere zwei Varianten ja nicht wirklich. Und die Images müssen ja nicht gedreht werden. Dafür gibt es ja die Transformationsmatrix der Graphics-Klasse.
Ich setzte mich selbst mal dran. Mal sehen welche Version einfacher zu handhaben ist.

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Di 30.12.14 17:18 
Was das Zeichnen angeht bin ich nicht fitt, das Spiel an sich habe ich aber gerade grob umgesetzt und ich finde meine Variante sehr angenehm zu handhaben ;) Außerdem kann ich das einfache Feld auch für andere ähnliche Spiele wieder verwenden, das Feld selber habe ich z.B. aus meinem Sudoku-Anfang genommen.

Den größten Vorteil darin, dass das ganze Schiff in einzelne Feld-Teile geteilt wird, sehe ich darin, dass der Zugriff dann simpel über Koordinaten läuft. Es muss nicht die Größe jedes Schiffs beachtet werden, es interessiert nur, ob das Feld getroffen wurde, ob ein Schiffs-Teil vorhanden ist und die Art des Schiff-Teils, sowie dessen Ausrichtung. Mit diesen Informationen kann jedes Schiff korrekt gezeichnet werden.
Für jedes Schiff ein einzelnes Bild zu verwenden hätte den Vorteil, dass es weniger Bilder gibt (was der Performance zu Gute kommen kann) und der augenscheinliche Umfang (Menge der Klassen) vermutlich geringer wird. Den Nachteil sehe ich darin, dass die Ausrichtung der Bilder und die Position schwerer zu verwalten ist. Es kann nicht einfach Zeile für Zeile jedes Feld betrachtet werden, sondern neben einem großen Schiff müssen Fehler ignoriert werden, da es diesen Bereich für sich ein nimmt.


Vielleicht denke ich einfach nur schief, aber ich kann mir einzelne Felder im Koordinaten-System einfacher vorstellen.
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Di 30.12.14 23:50 
Danke für eure Antworten, ich werde die Variante von "C#" ausprogrammieren, erscheint mir aus jetziger Sicht logischer.
Und ich will es so simpel wie möglich halten, da ich das Spiel mit Client-Server erweitern will dazu einfach ein simples Minispiel.
mfg
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mi 31.12.14 18:13 
Also ich hab mal selbst etwas rumgespielt und stelle meinen Fortschritt jetzt hier rein. Das Projekt ist nocht nicht fertig, aber ich werde es nicht mehr weiterverfolgen, weil ich keine Zeit dafür habe.

Was bisher funktioniert:
- Spieler erstellen (Name, Farbe)
- beliebig viele Spieler hinzufügen
- jedem Spieler beliebig viele Schiffe hinzufügen
- beliebige Spielfeldgröße
- rundenbasiertes spielen (es startet ein zufälliger Spieler und dann wird immer durchrotiert)
- wurde ein Schiff getroffen, wird der Treffer auf dem Spielfeld markiert und für alle Spieler sichtbar
- zerstörte Schiffe werden auf der Karte für jeden Spieler angezeigt
- wurden alle Schiffe eines Spielers zerstört, fällt dieser aus dem Spielzyklus
- wenn nur noch ein Spieler im Spielzyklus ist, ist das Spiel vorüber

Was nicht funktioniert bzw. vorhanden ist:
- Markierung der Felder die abgeschossen wurden, aber keinen Treffer erzielten (Schuss ins leere)
- Felder gegen mehrfachen Beschuss sichern
- Prüfung, ob sich Schiffe überlagern oder außerhalb des Spielfeldes befinden

Ich hoffe ich habe jetzt alle relevanten Sachen aufgezählt. Ich hänge das gesamte Projekt als ZIP Datei in den Anhang.

Hier noch ein Screenshot des Spiels:
Bild
Einloggen, um Attachments anzusehen!
_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: So 04.01.15 17:58 
Hallo!
Ich komm gerade einfach nicht weiter, wie es aussieht bin ich für das Projekt noch nicht gewachsen...
@ C# dein Projekt konnte ich in Visual Studio nicht starten aufgrund einiger Fehlermeldungen, sonst denke ich würde mir das Projekt weiterhelfen.
Kurz einen Blick darauf werfen, welches hochgeladen wurde?
Benutze übrigens Visual Studio 2013.
mfg
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: So 04.01.15 18:16 
Hallo,

was bekommst du denn für eine Fehlermeldung? Bekommt sonst noch jemand eine Fehlermeldung? Ich habe es gerade geöffnet und es hat alles funktioniert. Allerdings verwende ich die Visual Studio 2015 Preview, aber das sollte doch kein Problem sein :gruebel:

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: So 04.01.15 18:53 
Du weißt schon, das du in deinem Projekt ein paar der neuen Features von C#6 nutzt? :D

Natürlich gibt es Fehler, VS 2013 unterstützt das noch nicht ^^


Die Fehler sind nur in der Ship-Klasse.
Tausche das:
ausblenden C#-Quelltext
1:
2:
public bool ShipDestroyed => destroyedPoints.Count == size;
public bool IsDamaged => destroyedPoints.Count > 0;

mit dem:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
public bool ShipDestroyed
{
    get { return destroyedPoints.Count == size; }
}
public bool IsDamaged
{
    get { return destroyedPoints.Count > 0; }
}

aus, dann müsste es gehen.


PS:
Ich mag die Variante, eine Methode mit Lambda zu implementieren nicht, das sieht hässlich aus. :/
Am meisten freue ich mich auf die String-Operationen und den nameof-Operator ^^
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: So 04.01.15 19:07 
:D Hoppla.
Ist mir gar nicht aufgefallen. Ja ich hab C#6 schon voll im Einsatz. Und mit ReSharper fällt das einem dann gar nicht mehr auf :mrgreen:

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler