Entwickler-Ecke

Basistechnologien - Funktion mit Array verbinden


DonKannallie - Fr 29.08.14 12:48
Titel: Funktion mit Array verbinden
Guten Tag Com.

Ich habe eine Funktion geschrieben in dem ich Panels erstelle die eine Click funktion haben. Jedoch habe ich das sehr umstennlich geschrieben und habe mehr oder weniger das gleiche 20x geschrieben was ich mit einem Array ändern möchte


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
            for (int ebene1 = anzahl; ebene1 < anzahlmax; ebene1++)
            {
                panel.Add(new Panel());

                panel[ebene1].Location = new Point(10 + (ebene1 * 21), hoch);
                panel[ebene1].Size = new System.Drawing.Size(2020);
                panel[ebene1].Name = "Panel" + ebene1;
                panel[ebene1].BackColor = System.Drawing.Color.White;
                panel[ebene1].MouseClick += Klicken;
                this.Controls.Add(panel[ebene1]);
                panel[ebene1].TabIndex = ebene1;

               
            }




mit dieser Funktion erstelle ich mehrere Panels neben einander.
Jedoch möchte ich ,dass die Panels auch unter einander sind d.h. das in einer reihe ca. 30 Panels und in der tiefe 60 Panels sind.


Folgendes Problem:

Ich habe ein bischen über Array gelesen (in meinem Buch und im Internet) , weis aber nicht wie ich die beiden sachen mit einander verbinden kann


Ralf Jansen - Fr 29.08.14 13:18

Jetzt erzeugst du ja schon die Panels in einer Schleife und weißt sie der ControlsCollection zu. Auf der Form werden die somit funktionieren und wenn du die Location richtig berechnest hast werden die auch da liegen wo du die haben willst.

Für welche Aufgabe möchtest du dann jetzt noch das Array verwenden?


DonKannallie - Fr 29.08.14 13:27

Für die tiefe , sodass ich einen rechteck mit panels habe

ich habe dafür schon versucht eine "if" bedingung zu benutzen aber aus irgent einem grund hat der bestimmt variabeln nicht mehr benutzt sodass es nicht so funktioniert hat wie ich es wollte.

ein freund(Java user) von mir meinte ich solle es mit einem Array probieren


Ralf Jansen - Fr 29.08.14 13:39

Du mußt nur die Location richtig berechnen.
Beispiel für ein (farbloses) Schachbrett mit 20*20 Pixel Feld Größe.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
    int Anzahl = 64;
    int panelPerRow = 8;            

    for (int i = 0; i < Anzahl; i++)
    {
        var panel = new Panel();
        panel.Location = new Point(i % panelPerRow * 20, i / panelPerRow * 20);
        panel.Size = new System.Drawing.Size(2020);                
        this.Controls.Add(panel);
    }


DonKannallie - Fr 29.08.14 13:52

Sorry aber ich kann dir nicht ganz folgen was deine Location berechnung angeht


Ralf Jansen - Fr 29.08.14 14:09

Erinnere dich an simple Ganzzahldivision aus deiner Grundschulzeit. Stell die vor alle Panels von links oben nach rechts unten durchzunummerieren.

Als Beispiel nehmen wir jetzt das 15.te Panel(da alles 0. basiert ist hat dieses Panel den gedachten Index 14). Auf einem Schachbrett wäre es in der 2.ten Reihe an der 7.ten Spalte. Wie bekomme ich die Reihe raus in die das Panel gehört? Man teilt durch die Panelbreite also 14 : 8 = 1 Rest 6. Da alles 0.basiert entspricht 1 der 2.ten Reihe und 6 der 7 Spalte. Voila.

Die x Position ist also der Rest der Ganzzahldivision (% ist der Modulo Operator also der Divisionsrest). Für eine sinnvolle Spaltenbreite multiplizieren wir das noch mit der Panelbreite also
(Index % AnzahlSpalten) * Spaltenbreite
Die y Position ist der normale Ganze Anteil der Ganzzahldivision wiederum multipliziert mit der Zeilenhöhe. Also
(Index / AnzahlSpalten) * Spaltenbreite

Und x,y schmeißt man dann halt in einen Point und weißt den der Location zu.


DonKannallie - Fr 29.08.14 14:29

Aso , verstehe - vielen dank!


Th69 - Fr 29.08.14 14:32

Die andere Möglichkeit wäre ein zweidimensionales (rectangle) Array:

C#-Quelltext
1:
Panel[,] panels = new Panel[MaxX, MaxY];                    

Und dann mit zwei ineinander verschachtelten Schleifen für x und y die Panels erzeugen.

Die Frage, welche Version besser ist, hängt dann damit zusammen wie man die Panels indizieren möchte (also eindimensional oder zweidimensional).


DonKannallie - Fr 29.08.14 15:43

Jetzt stellt sich die frage für mich (bei Ralf Jansen's version)

wie kann ich ein einzelnen Panel ansprechen?


Ralf Jansen - Fr 29.08.14 15:56

Zitat:
wie kann ich ein einzelnen Panel ansprechen?


Warum? Ohne kann man nicht wirklich was sinnvolles raten. Das hat ja eventuell schon bei meiner letzten Rückfrage nach "Für welche Aufgabe" nicht funktioniert.


DonKannallie - Fr 29.08.14 16:43

Ich will den Panels bestimmte farben zuweisen

Das Programm soll eine hilfe für ein Spiel sein was ich derzeitig spiele - mit den Panels soll man einen weg machen welche monster durch rennen müssen dabei gild es den weg möglichst lang zumachen auf eine bestimmt fläche

mit den färbungen möchte den anfang und das ende des bau bereiches zeigen


Ralf Jansen - Fr 29.08.14 17:17

Okay dann alles zurück auf 0 ;)

Du beginnst dein Spiel von der visuellen Anzeige zu denken das ist problematisch.
Beginne mit der Spiellogik also mit einer Datenstruktur die die aktuelle Situation beschreibt.
Wenn du diese Struktur hast dann überlege dir wie du die auf den Bildschirm bekommst.

Diese Struktur könnte im einfachsten Fall auf ein Array hinauslaufen. 2-Dimensional um jede Spielfläche zu beschreiben. In den Zellen wären aber keine Panels. Sondern etwas das den Zustand diese Fläche beschreibt. Also so Infos wie "ist ein Weg", "ist eine Mauer", "ist der Anfang", "ist das Ende", "hier befindet sich ein Spieler", " hier befindet sich ein Monster" oder was auch immer und irgendwelche weiteren Information die man zur Logik des Spiels braucht. Wenn du die hast dann kannst dir das Mapping dieser Struktur auf den Bildschirm überlegen. Z.B. mit einer adäquaten Matrix von Panels. Wobei 30 * 60 Panels grenzwertig ist. Window Handles sind eine knappe Resource und jedes Panel braucht eine. Wenn man die zu verschwenderisch benutzt zeichnet irgendwann deine Anwendung nicht mehr weil Handles aufgebraucht sind. Wenns dumm läuft zeichnet nicht nur nicht mehr deine Anwendung sondern kein Prozess mehr.


DonKannallie - Fr 29.08.14 17:49

okay ich glaube du hast ich faltsch verstaden :D

so weit wollte ich nicht gehen

nochmal von vorn mit details


Das spiel ist ein Tower Defense spiel - da ich es mit mehreren leuten spiele und man nur wenig zeit hat um die Tower zubauen muss man sich schon im klaren sein wie man seine "maze" bauen möchte

dieses Programm soll helfen damit man vor dem spiel und auch in späteren spielen sich richtig drauf vorbereiten kann - ob sie letzt endlich auch sehr wirklich erweist ist dem überlassen der sie baut


ich habe ca. >1000 Panels , ich habe eine version des Programms schon fertig wo alles tiptop läuft jedoch gefällt es mir von der programmieren nicht da ich nur mit unnötiger mühe änderungen dran vornehmen kann , daher auch die frage nach dem Array :D


hekolliko - Mo 01.09.14 16:55

Wow du nimmst dir ja einiges vor ... :D Aber .. gute Frage, bin gespannt auf den Lösungsansatz!


DonKannallie - Di 02.09.14 15:08

naja eig. ist es relativ einfach wenn man mehr anhnung hat als ich aber danke :D


Ralf Jansen - Di 02.09.14 15:59

naja wir würden auch helfen wenn wir das Problem verstehen würden :(


DonKannallie - Di 02.09.14 17:29

Würde es helfen wenn ich das Programm irgentwie hochladen würde?


wenn ja wo kann man das den machen? :D


Ralf Jansen - Di 02.09.14 17:45

Deinen Project Ordner einfach zippen, vorher den bin und object Ordner löschen wir brauchen ja nur die Sourcen, und hier anhängen.

Es würde aber auch reichen wenn du erklärst was dein Programm tun soll. Du bist leider nicht genauer geworden als das du du eine Matrix von Panels hast die dir irgendwie bei der Planung für ein Spiel helfen soll.


C# - Di 02.09.14 22:24

Hmm. Also wenn ich dich richtig verstehe, dann willst du so eine Art "Paint für Arme". Ich hab mal selber ein bisschen gecoded und das Projekt in den Anhang gehängt.

Mit der linken Maustaste wird das Feld mit der entsprechenden Farbe markiert und mit der Rechten wieder gelöscht.

EDIT
Hab noch eine zweite Version gemacht. Die erste Version arbeitet mit Panels, die zweite hingegen arbeitet mit Graphics (finde ich die bessere Lösung) und hat noch ein extra Feature. Vergleiche mal die beiden Versionen.


DonKannallie - Mi 03.09.14 14:13

Hmm ich wüsste jetzt auch nicht genau was es da noch mehr zu erklähren gibt.


aber gut , auf ein neuen versuch :P

das programm hat die panel matrix.
wenn ich auf ein panel klicke , soll sich die farbe des angeklickten panels ändern:

Links klick : Schwarz
Mausrad klick : Rot
Rechts klick : weiß

es gibt 2 anfänge und 1 ende (man kann sich das feld wie ein gedrehtes "T" vorstellen

ich möchte gerne die anfänge und das ende makieren.

Problem :

Ich habe die Panels Dynamisch erstellt aber weis nicht genau wie ein Panel sagen kann das es keine funktion und nur die farbe blau haben soll.



danke C# du hast eine weitere lösung in deinem Programm. Ich wollte als nächsten schritt das Speichern ermöglichen wusste aber noch nicht genau wie ich dass anstellen - leider reicht mein wissen bei weitem nicht das zu verstehen was du da "gezaubert" hast


hier ist mein Projekt - vl. hilft es auch noch weiter


Ralf Jansen - Mi 03.09.14 15:14

Zitat:
es gibt 2 anfänge und 1 ende (man kann sich das feld wie ein gedrehtes "T" vorstellen

ich möchte gerne die anfänge und das ende makieren.


Aber nicht miteinander verbinden? Woraus folgt ein Panel braucht kein Wissen über seine Nachbar-Panel. Richtig?
Das ist eigentlich der zentrale Punkt um zu entscheiden wie man das am besten strukturiert.
Braucht man Wissen der Gesamtmatrix oder reicht das die einzelnen Panels sich selber kennen?


C# - Mi 03.09.14 15:45

Also so wie ich das sehe hat das Panel nur Augen für sich selbst (ist halt ein egoistisches Panel :mrgreen:). Er braucht die Infos über die umliegenden Panels meiner Ansicht nach nicht - wozu auch? Wenn das jetzt das Spiel wäre, dann ja. Aber das was er vor hat ist ja nur eine Art Gedächtnisstütze, dass er sich die Felder mit den Towers markieren kann.

@DonKannallie
Wenn dir was an meinem Code unklar ist, dann frag konkret was.
Ach und google mal Multidimensionale Arrays (z.B. int[,])


DonKannallie - Mi 03.09.14 17:18

Rein theoretisch bräuchten die Panels schon information über die anderen Panels , da ich ganz gerne noch eine funktion einbauen würde dass wenn ich auf ein Panel klicke soll das darunten liegende , rechtslegende und unten rechts liegende panel sich dem entsprechent färben soll (welche taste man drückt)


@ C#

wenn du mir erklähren könntest wie du das Speichern und laden gemacht hast wäre ich dir sehr dank bar
genau gesagt von Zeile 36 bis 97 bei der ersten Version (ich glaub zwischen den Versionen hast du nichts geändert)
falls du irgentwo eine art "Tutorial" hättest oder irgntwas wo ich sowas nachlesen könnte wäre das auch schön


Ich habe für Mehrdimensionale Array schon gegooglt bzw. auf Galileo Computing geschaut wusste jedoch nicht wie ich dass in mein Prog. einbiden konnte wobei mir Herr Jansen geholfen hat und ich seine Version bevorzuge


Ralf Jansen - Mi 03.09.14 17:46

user profile iconDonKannallie hat folgendes geschrieben Zum zitierten Posting springen:
wobei mir Herr Jansen geholfen hat und ich seine Version bevorzuge


Aua. Ich sag trotzdem du.

Zitat:
Rein theoretisch bräuchten die Panels schon information über die anderen Panels , da ich ganz gerne noch eine funktion einbauen würde dass wenn ich auf ein Panel klicke soll das darunten liegende , rechtslegende und unten rechts liegende panel sich dem entsprechent färben soll (welche taste man drückt)


Ist das ~Spielfeld~ immer rechteckig? Dein Testprojekt deutet etwas anderes an.


PS: bei "rechtslegende" muß ich irgendwie an den Mann mit dem 2-Fingerbart denken. Das war wohl nicht so gemeint ;)


C# - Mi 03.09.14 18:10

Also das Speichern der Daten ist eigentlich recht einfach. Der BinaryWriter speichert die Rohdatensätze von bestimmten Typen (int, string, char, bool, ...).
Meine Klasse Map besteht aus int und string Variablen. Ich gehe die einzelnen Variablen durch und übergebe deren aktuellen Wert einfach dem BinaryWriter, der diesen Wert in der angegebenen Datei ablegt. Jeder weitere Wert wird einfach hinten an die Datei dran gehängt.

Im Prinzip würde die Datei so aussehen:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
map.Name = "Test";
map.Width = 10;
map.Height = 10;
// map.MapData ist dann 10x10 groß

// Die Datei würde im Prinzip so aussehen:
Test\0000000000000000000000... // bis 10x10 Nullen drin stehen.


In Wahrheit sieht die Datei etwas anders aus, weil sie alles Binär speichert. Aber zum Vorstellen ist das hier besser.

Beim auslesen funktioniert das ganze genau anders rum. Der BinaryReader öffnet die Datei und ließt den string zuerst aus (der hört bei dem Zeichen "\0" auf; das ist das String-Abschlusszeichen). Da ein int 4 Byte speichergröße hat, ließt der Reader bei reader.ReadInt(); die nächsten 4 Bytes aus der Datei und wandelt diese in einen Integer um (also int). Diese Werte weiße ich dann wieder der Map-Klasse zu.

Deshalb ist es von Vorteil, wenn du Logik und UI voneinander trennst.

Logik:

Was dein Problem mit den MultiArrays angeht, überleg mal:
Deine Karte im TD ist Zweidimensional, also hast du eine X- und eine Y-Achse. Wenn du also Logik von UI trennst, musst du zuerst mal dieses Feld im Speicher anlegen.
Sagen wir dein Feld ist 20x20 groß, dann erzeugst du ein Array in dieser größe mit einem passenden Typ. Da dir ja nur die Farben der Felder wichtig sind, bietet sich als Typ int oder Color an. Letzteres verursacht bei manchen Speichermethoden problemen, deshalb habe ich int genommen. Wenn du noch mehr Informationen über die Karte wissen solltest (z.B. Name), erstelle dir eine Klasse, die die ganzen Infos bereithält. Bei mir ist das die Klasse Map. Hier wird die Feldgröße, der Name und das eigentliche Spielfeld angelegt.
Das eigentliche Spielfeld ist bei mir map.MapData. Dieses Array wird in den Maßen [Breite, Höhe] angelegt, sodass es den X- und Y-Achsen entspricht.
Der Vorteil darin liegt nun dass du ganz einfach dein Spielfeld ändern kannst.
Wenn du z.B. das rechte, untere und diagonale Feld einer bestimmten Stelle markieren willst, kannst du einfach so vorgehen:

C#-Quelltext
1:
2:
3:
4:
5:
6:
Map map = new Map("Karte schlagmichtot"2020);
int x = 8, y = 10;
map.MapData[x, y] = Color.Red.ToArgb();   // Hier wird jetzt dein "geklicktes" Feld markiert
map.MapData[x + 1, y] = Color.Red.ToArgb();   // und hier das Feld rechts daneben,
map.MapData[x, y + 1] = Color.Red.ToArgb();   // und hier das Feld darunter,
map.MapData[x + 1, y + 1] = Color.Red.ToArgb();   // und hier das Feld diagonal darunter,

Du siehst also, die einzelnen Felder müssen nichts voneinander wissen um sie zu ermitteln oder zu manipulieren.

So das wäre der Logikteil.



Kommen wir zur UI:
Jetzt hast du ja alle Daten schon im Speicher liegen wie du sie brauchst. Nun musst du sie nur noch auf den Bildschirm bringen. In Version 2 von meinem Projekt steht drin wie es richtig geht.
Also zuerst einmal bracuhen wir die Größe der Zeichenfläche. Ich habe bei mir eine Klasse geschrieben die von Control abgeleitet ist. Somit kann man sie einfach im Designer auf die Form ziehen und die Spielfeldgröße selbst bestimmen.

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
class DrawingSurface : Control
{
    Map map;          // dieser Variable wird dann die oben erstelle Map zugewiesen
    SizeF tileSize;   // das ist die Größe eines einzelnen Felds

    public DrawingSurface()
    {
         // Die größe eines Feldes in Pixel wird so berechnet: Die Breite der Zeichenfläche (Width) / die Anzahl der Felder in X-Richtung (map.Width)
         tileSize = new SizeF(Width / map.Width, Height / map.Height);
    }

    protected override void OnPaint(PaintEventArgs e)  // Paint-Methode überschreiben, damit wir das Feld zeichnen können
    {
      base.OnPaint(e);

      Graphics g = e.Graphics;  // Graphics object nehmen

      for (int x = 0; x < Map.Width; x++)  // Das Feld im Speicher in X-Richtung abtasten
      {
        for (int y = 0; y < Map.Height; y++)  // Das Feld im Speicher in Y-Richtung abtasten
        {
          // Das momentane Feld zeichnen
          g.FillRectangle(new SolidBrush(Color.FromArgb(Map.MapData[x, y])), tileSize.Width * x, tileSize.Height * y, tileSize.Width, tileSize.Height);
          g.DrawRectangle(new Pen(Color.Black, 2), tileSize.Width * x, tileSize.Height * y, tileSize.Width, tileSize.Height);
        }
      }
    }


Hoffe ich konnte es klarer machen.

P.S.: Hier sind alle per Du ;)

Nachtrag
Ich habe dir im Anhang noch zwei Bilder. Das eine zeigt dir, wie das Array prinzipiell im Speicher aussieht und das Andere stellt das Spielfeld dar. Du siehst, sie sind beide fast identisch. Sie haben nur eine andere Skalierung. Und genau diese Skalierung wird bei der Paint-Methode umgerechnet (bei g.FillRectangle(...


DonKannallie - Mi 03.09.14 19:24

gut danke , ich werde mich morgen noch mal ran setzten und versuchen das ganze mit einem Array zu machen da es offenbar für meine zwecke einfacher ist xD

genau so hatte ich mir den speicher vorgang auch gedacht jedoch wusste ich nicht dass es diesen befehl gibt :D


ich wollte jetzt auch nicht respektlos sein daher spreche ich eher personen mit nachnamen an ;)