Schulprojekt: Snake programmieren mit C#

Nymphalis

Mitglied
@rd4eva
Ich weiß, aber eigentlich sollte ich böse sein, weil das jetzt die doppelte Arbeit mit dem Dokumentieren wird - hab ich auch verdient;)

@sheel
die kommen ganz sicher

@rd4eva
Woher kommt eigentlich der okay-Button am Schluss?
Im Text konnte ich ihn nicht finden.
Vielleicht sollte ich da noch einen Restart-Button einfügen wenn ich mal alles verstehe...
 

sheel

I love Asm
Der beim Gane-over-Fenster?

Das ist das MessageBox.Show
Ein fertiges Fenster mit einem bestimmten Text
(übergeben, eben "Game over") und einem OK-Button.

(yeah, snake spielen :D)
 

Nymphalis

Mitglied
Ach so, so etwas Ähnliches hatte ich mir schon gedacht.
War ja die einzige Möglichkeit irgendwie...
bis später, ich geh mal vom Pc weg, sonst überwinde ich mich nie endlich anzufangen;)
 

Nymphalis

Mitglied
Also ich hätte da mal drei Fragen an dich rd4eva:

1.)Was bedeutet
Code:
if (nri+ri !=0 && nri !=ri)
{
}
?
Vor allem das !=0 versteh ich da nicht.
Wofür steht das "!"?

2.) Dann wüsste ich noch gern was "InvokeRequired" heißt bzw. warum es da eingesetzt wird bei MoveSnake()
Heißt required nicht benötigen?:rolleyes:
Bei derselben Stelle wüsste ich auch gerne wofür mal delegate braucht, davon hab ich noch nie gehört. :(

Code:
private void MoveSnake()
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate { MoveSnake(); }));
            }
            else
            {

                PictureBox head = snakeSegments[0];
                head.Tag = head.Location;

                switch (ri)
                {
                    case -1:
                    case 1:
                        head.Left += 10 * ri;
                        break;
                    case -2:
                    case 2:
                        head.Top -= 5 * ri;
                        break;
                }

3.) Meine letzte Frage ist, was du mit "fp" gemeint hast.
Ist das die Abkürzung für "fruitPoints"?
und was heißt dabei:
Code:
fp.Dipose();
Das wären dann alle meine Fragen:rolleyes:
warte gespannt auf Antwort und ob meine Ideen richtig sind...
 

sheel

I love Asm
Hoffe, du hast nichts dagegen, wenn statt rd4eva ich antworte :)

1)
rd4eva hat andere Nummern für die 4 Richtungen vergeben.
Links/Rechts sind -1 und 1
Unten/Oben -2 und 2

ri ist die aktuelle Richtung, nri die neue Richtung laut Tastendruck
Wenn ri+nri 0 ist, was bedeutet das dann? Entgegengesetzte Richtung.
zB. Schlange geht nach links und Benutzer will 180-Drehung nach rechts. Nicht erlaubt.
!= bedeutet "nicht gleich"
Also nur, wenn bei ri+nri nicht 0 rauskommt darf die neue Richtung übernommen werden
(und MoveSnake etc.)

Zweiter Teil der if-Bedingung: Wenn der Bentzer die Richtung drückt,
die die Schlange sowieso schon hat, kann man sich den Richtungswechselcode sparen.

3)
fp ist die Picturebox vom Futter, das gerade gefressen wurde.
Die wird unter Anderem aus der Futtersammlung rausgenommen etc., und dann das Dispose:
Mit "new Picturebox()" und Ähnlichem legt man ja neue Instanzen/Objekte von Klassen an.
Man muss sich aber nicht ums Aufräumen kümmern.
Hier:
C#:
Picturebox p;
p = new Picturebox();
p.machirgendwas();
...
p = new Picturebox();
p.machnochwas();
legst du beim zweiten new ja eine zweite Picbox an und schmeißt die erste praktisch weg.
Das wird beim C# automatisch erkannt, und alles von der ersten Picbox noch vorhandene
wird gelöscht. Großes Aber: Das muss nicht sofort passieren.

Je nachedem, wie ausgelastet der Prozessor gerade ist (etc.etc)
wartet der Aufräumdienst eventuell auf einen günstigen Zeitpunkt,
in dem der Computer gerade nicht so beansprucht ist,
und löscht dann allen angefallenen Müll in einem Schwung.

Es kann also passieren, dass sich "im Inneren" von C# viele alte Variablen ansammeln,
die in deinem Programm selbst eigentlich nicht mehr existieren.
Die brauchen Speicher. Und eine PictureBox mit ihrem Bild drin braucht
verglichen zu int´s und so extrem viel Speicher.

Um den Müllberg etwas einzudämmen kann man mit Dispose quasi eine Empfehlung abgeben,
das Ding jetzt schon wegzuräumen. Grade bei speicherhungrigen Sachen gut,
um zu viel Anhäufung zu Vermeiden.
Wenn man kein Dispose macht könnte das Programm zeitweise eben mehr Arbeitsspeicher
brauchen. Bei anderen Programmen (nicht Snake) eventuell mehr, als man überhaupt hat.
Deswegen Dispose.

2)
Zugriff auf die Variablen der grafischen Oberfläche (Pictureboxen, Panel, usw.)
soll/darf nur in dem Thread erfolgen, in dem das Fenster erstellt wurde.
Wenn man also zusätzlich zum schon vorhandenen Hauptthread weitere eigene erzeugt,
die gleichzeitig dazu ablaufen, kann man von dort aus eigentlich nichts mit dem Fenster machen.

Ein Workaround ist diese Invokegeschichte. Am Beispiel Movesnake:
"wenn ich hier in einem nichtpassenden Thread bin starte Movesnake
mit Grafikzugriff nocheinmal. Sonst eben das normale Zeug machen"

Movesnake kommt ja uA. im Timer vor.
Der wartet nebenbei, neben der Fensteranzeigung, und führt regelmäßig was aus.
Von daher könnte man ihn eigentlich als Thread sehen.

Soweit ich weiß ists aber kein Thread (sondern gehört zu den normalen Fensterereignissen,
"This Windows timer is designed for a single-threaded environment"),
das Invokezeug könnte man sich dann also sparen.
Andererseits schadet es nicht, auch wenn es keine anderen Threads gibt.

edit: Je nach verwendetem Timer ists einer oder nicht.
System.Timers.Timer ist mit Threadzeug, Windows.Forms.Timer ohne...

Was genau ein delegate ist und wie das hier rein passt...
unterhalten wir uns nach der Abgabe darüber :D
Über Delegates kann man auch einiges erzählen.
 

Nymphalis

Mitglied
Wow, das ging schnell...
Danke schön!
kannst du mir noch meine zweite Frage fertig beantworten?
Was ist jetzt eigentlich mit delegate und InvokeRequired?
Dann hab ich wirklich alles! ;)
 

rd4eva

Erfahrenes Mitglied
(yeah, snake spielen )
Hihi.
Ansonsten: Danke alles richtig => 1 mit Stern ;)

Das InvokeRequired und BeginInvooke ist nötig da die System.Timers.Timer Klassen nicht im gleichen Thread laufen wie die Windows Form.
Würde man versuchen von der Elapsed Methode (MoveSnake() wird von der Elapsed Methode aufgerufen. Ist also das gleiche) aus auf die Form zu zugreifen würde man eine InvalidOperationException Exception bekommen.
Mit BeginInvoke wird die Methode in den gleichen Thread geholt und alles ist gut. (stark vereinfacht ausgedrückt).

Threading ist zugegebenermaßen nicht gerade das einfachste Thema, also um dir das wirklich zu erklären würden wir vermutlich etwas länger brauchen.
 
Zuletzt bearbeitet:

sheel

I love Asm
Ist oben reineditiert.
Das if paar Zeile über dem Dispose und so ist klar? ;-]
@rd: Zum Thread steht jetzt oben noch was (Doku...)

Gerade probiert: Ein getimertes Sleep hängt ein ganzes Fenster auf.
Dürfte also kein eigenständiger Thread sein.
 

rd4eva

Erfahrenes Mitglied
@rd: Zum Thread steht jetzt oben noch was (Doku...)
Danke wesentlich besser erklärt als ich.
Aber zur Timer Geschichte. Kanns sein das du dir die falsche Timer Klasse angeschaut hast?
C# hat ein paar davon System.Threading.Timer, System.Timers.Timer, System.Windows.Forms.Timer.
Ich hab den System.Timers.Timer verwendet und da sagt die Doku folgendes:
The server-based Timer is designed for use with worker threads in a multithreaded environment. Server timers can move among threads to handle the raised Elapsed event, resulting in more accuracy than Windows timers in raising the event on time.
[...]
If the SynchronizingObject property is null, the Elapsed event is raised on a ThreadPool thread. If processing of the Elapsed event lasts longer than Interval, the event might be raised again on another ThreadPool thread. In this situation, the event handler should be reentrant.
 

sheel

I love Asm
Richtig, mein Fehler.
Hatte noch zu sehr an Nymphalis´Original gedacht, da war der Windowsforms-Timer.
Oben korrigieren...done.