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



BeitragVerfasst: Di 04.05.21 11:36 
Hallo Leute!
Zu meinem Problem: Ich bin gerade dabei ein kleines Spiel zu entwickeln indem eine Spielfigur durch eine Welt bewegt werden kann. In dieser Welt sind auch Wände die natürlich nicht durchquert werden dürfen (ist ja klar) aber eben das zu programmieren klappt bei mir nicht. Hier mein Code:
ausblenden volle Höhe 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:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
public class MyForm : Form
    {
        public char[,] Welt { get; set; }

        string wand = "|";
        string weg = " ";
        string spieler = "X";

        int x = 0;
        int y = 0;

        int x_spieler;
        int y_spieler;

        public MyForm()
        {
            Width = 500;
            Height = 500;
            Text = "Spiel";
        }

        override
        protected void OnPaint(PaintEventArgs e)
        {

            FontFamily font = new FontFamily("Arial");
            Font fontWand = new Font(font, 24, FontStyle.Regular, GraphicsUnit.Pixel);
            Font fontWeg = new Font(font, 18, FontStyle.Bold, GraphicsUnit.Pixel);
            Font fontSpieler = new Font(font, 20, FontStyle.Regular, GraphicsUnit.Pixel);

            StringFormat format1 = new StringFormat(StringFormatFlags.NoFontFallback);
            format1.Alignment = StringAlignment.Center;
            format1.LineAlignment = StringAlignment.Center;

            for (int j = 0; j < Welt.GetLength(0); j++)
            {
                for (int i = 0; i < Welt.GetLength(1); i++)
                {
                    if (Welt[j, i].ToString() == wand)
                    {
                        Rectangle rect = new Rectangle(x + (22 * i), y + (22 * j), 2222);

                        e.Graphics.DrawString(wand, fontWand, new SolidBrush(Color.Green), rect, format1);
                    }
                    if (Welt[j, i].ToString() == weg)
                    {
                        Rectangle rect = new Rectangle(x + (22 * i), y + (22 * j), 2222);

                        e.Graphics.DrawString(weg, fontWeg, new SolidBrush(Color.Blue), rect, format1);
                    }
                    if (Welt[j, i].ToString() == spieler)
                    {
                        Rectangle rect_spieler = new Rectangle(x_spieler + (22 * i), y_spieler + (22 * j), 2222);

                        e.Graphics.DrawString(spieler, fontSpieler, new SolidBrush(Color.Red), rect_spieler, format1);
                    }
                }
            }
        }

        //Methode, die dafür sorgen soll, dass die Wände nicht durchquert werden können
        public bool keineWand(int x, int y)
        {
            if (Welt[y,x].ToString() == wand)
            {
                return false;
            }
            return true;
        }


        override
        protected void OnKeyDown(KeyEventArgs e)
        {

            if (e.KeyData == Keys.Up)
            {
                if (keineWand(x_spieler, y_spieler))
                {
                    y_spieler -= 22;
                    Refresh();
                }
            }
            if (e.KeyData == Keys.Down)
            {
                if (keineWand(x_spieler, y_spieler))
                {
                    y_spieler += 22;
                    Refresh();
                }
            }
            if (e.KeyData == Keys.Right)
            {
                if (keineWand(x_spieler, y_spieler))
                {
                    x_spieler += 22;
                    Refresh();
                }
            }
            if (e.KeyData == Keys.Left)
            {
                if (keineWand(x_spieler, y_spieler))
                {
                    x_spieler -= 22;
                    Refresh();
                }
            }
        }
    }


Die Spielewelt inklusive die Wände und den Spieler werden über eine Text bzw. dat-Datei über die Konsole aufgerufen.
Hab auch schon versucht die for-schleife von der OnPaint-Methode in die "keineWand" Methode zu implementieren aber das funktioniert nicht, da bekomme ich entweder eine "OutOfRange" Exception oder der Spieler lässt sich wie hier vorgestellt überhaupt nicht bewegen.
Ich hoffe mir kann jemand helfen. Ich hänge an dem Problem jetzt schon ein Weilchen fest.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 04.05.21 12:20 
Könntest du erst einmal zu deinem letzten Thema Mehrdimensionales Array über DrawString ausgeben auf meinen Beitrag antworten, bevor du neue (ähnliche) Fragen stellst?!

Aber als genereller Hinweis: du solltest Spiellogik und UI komplett voneinander trennen und in verschiedenen Klassen implementieren (so daß die UI die Spiellogik-Klasse kennt und benutzt, aber nicht umgekehrt).
Und als Hinweis auf dein Problem: Mit welchen Dimensionen hast du denn die Welt definiert (pixelbasiert oder kastchenweise)?

PS: Auf die ToString()-Methode beim Vergleich kannst du verzichten, wenn du wand, weg, spieler gleich als (const) char definierst.
Und auf Magic Numbers (wie z.B. 22) solltest du verzichten und dafür ebenfalls Konstanten anlegen.

Für diesen Beitrag haben gedankt: noiiZeee
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Di 04.05.21 13:36 
Sorry, dass ich dir da nicht geantwortet habe. Da war ich zu fixiert darauf, endlich das Ding zum laufen zu bringen ^^'
Wie meinst du das mit der Definition der Welt? Ich denke mal pixelbasiert, da ja alle 22 Pixel ein neues Element neben dem vorherigen gelegt wird.
Danke übrigens für die schnelle Antwort.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 04.05.21 14:22 
Irgendwo mußt du doch Welt initialisieren, d.h.
ausblenden C#-Quelltext
1:
Welt = new char[Y, X];					

Welche Werte haben hier Y und X?

Nur welchen Sinn soll es machen, eine pixelbasierte Welt (als internes Feld) anzulegen, wenn sich die Spielerfigur nur in bestimmten Abständen (22) bewegt?
Du hast doch nicht pro Pixel eine Wand, einen Weg oder die Spielerfigur?

Und bei deinen Abfragen bzgl. "keine Wand" mußt du in die zu ziehende Richtung abfragen, nicht die aktuelle Position (x_spieler, y_spieler).

Für diesen Beitrag haben gedankt: noiiZeee
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Di 04.05.21 22:34 
Achso ja. Ich habe nen int mit zeilen und spalten sieht dann so aus:
ausblenden C#-Quelltext
1:
2:
3:
4:
int zeilen = 15;
int spalten = 15;

char[,] welt = new char[zeilen, spalten];


ich glaube ich weis nun worauf du hinaus möchtest.
Das probier ich gleich morgen früh mal aus!
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Mi 05.05.21 12:54 
Habe jetzt den ganzen Morgen rumprobiert und es immernoch nicht zum laufen gebracht.
Habe dir eine PN geschickt. Kannst du mir da bitte antworten? :nixweiss:
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 05.05.21 13:55 
Deine Fehler sind:
1. Du speicherst die Position des Spielers einerseits in x_spieler und y_spieler, andererseits direkt in der Welt (als '@'), synchronisierst aber diese nicht!
Die Darstellung der Spielerfigur sollte daher nur eine der beiden Möglichkeiten nutzen (bisher ist sie in der OnPaint-Methode falsch).

2. Da du die Welt ja in logischen Koordinaten (richtigerweise) abgespeichert hast, darfst du darauf dann aber nicht pixelbasiert zugreifen, wie du es z.B. fälschlicherweise mittels folgendem Code tust:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
if (keineWand(x_spieler, y_spieler))
{
    y_spieler -= 22// hier sollte nur y_spieler -= 1 hin!
    Refresh();
}

Und wie schon geschrieben mußt du die Koordinaten der neuen Position abfragen, also
ausblenden C#-Quelltext
1:
if (keineWand(x_spieler, y_spieler - 1))					

(analog dazu bei den anderen Richtungen)

Außerdem mußt du bei der Bewegung noch die Grenzen der Weltkoordinaten abfragen (damit du keine OutOfRangeException erhältst).

Und daher auch der Hinweis bzgl. einer eigenen Spiellogik-Klasse (so daß du dort dann nur Weltkoordinaten benutzt - und keine Pixel)!

Stelle dir die 22 einfach als Zoom-Faktor vor (ein anderer Grund warum du daraus eine Konstante erzeugen solltest) - nur bei 1 stimmen Welt- und Pixelkoordinaten überein.

Für diesen Beitrag haben gedankt: noiiZeee
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Do 06.05.21 09:53 
Hab jetzt wieder einige Stunden rumprobiert und es immernoch nicht zum laufen gebracht..
Ich hab die 22 in Konstanten gepackt, hab die chars "wand, weg und spieler" entfernt und frag direkt bei der OnPaint-Methode ab, if(Welt[j,i] == '@') z.B.
Hab bei der OnKeyDown-Methode auch auf 1 umgestellt aber so wie es aussieht, ist der Weg immernoch Pixel und nicht Kästchen.
Hab auch das Programm kopiert und alles in eigene Klassen gepackt. Da kam ich auf das gleiche Ergebnis.
Keine Ahnung was ich jetzt noch machen könnte :nixweiss:
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 06.05.21 10:25 
Ich meinte nicht eliminieren, sondern
ausblenden C#-Quelltext
1:
2:
3:
const char wand = '|';
const char weg = ' ';
const char spieler = 'X'// oder '@' ?

Mit '@' etc. direkt im Code hast du ja wieder Magic Numbers!

Was genau funktioniert denn noch nicht?

Für diesen Beitrag haben gedankt: noiiZeee
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Do 06.05.21 10:38 
Problem ist einfach, dass ich mich mit der OnKeyDown - Methode also:
ausblenden volle Höhe 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:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
public bool keineWand(int x, int y)
        {
            /*if (Labyrinth[y, x] == '#') // Zum testen ausgeschaltet
            {
                return false; 
            }*/

            return true;
        }


        override
        protected void OnKeyDown(KeyEventArgs e)
        {

            if (e.KeyData == Keys.Up)
            {
                if (keineWand(x_spieler, y_spieler - 1))
                {
                    y_spieler -= 1;
                    Refresh();
                }
            }
            if (e.KeyData == Keys.Down)
            {
                if (keineWand(x_spieler, y_spieler + 1))
                {
                    y_spieler += 1;
                    Refresh();
                }
            }
            if (e.KeyData == Keys.Right)
            {
                if (keineWand(x_spieler + 1, y_spieler))
                {
                    x_spieler += 1;
                    Refresh();
                }
            }
            if (e.KeyData == Keys.Left)
            {
                if (keineWand(x_spieler - 1, y_spieler))
                {
                    x_spieler - 1;
                    Refresh();
                }
            }
        }


um exakt einen Pixel bewege und nicht wie gewollt um das eine Kästchen bzw. um [+1, 0] z.B.
und sobald ich if-Abfrage bei "keineWand" aktiviere kommt direkt die OutOfRangeException.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 06.05.21 10:54 
Wie zeichnest du denn jetzt die Spielerfigur?
Ich hoffe, du hattest verstanden, was an deinem bisherigen Code in OnPaint diesbzgl. falsch gewesen ist?

Für diesen Beitrag haben gedankt: noiiZeee
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Do 06.05.21 11:20 
Scheinbar hab ich es nicht verstanden, sonst würde es wahrscheinlich funktionieren :o

Hab jetzt nochmal etwas rumprobiert und überlegt und habe jetzt die for-schleife folgendermaßen geschrieben:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
for (int j = 0; j < Welt.GetLength(0); j++)
            {
                for (int i = 0; i < Welt.GetLength(1); i++)
                {
                    if (Welt[j, i] == wand)
                    {
                        Rectangle rect = new Rectangle(pixelgröße * i, pixelgröße * j, pixelgröße, pixelgröße);
                        e.Graphics.DrawString(wand.ToString(), fontWand, new SolidBrush(Color.Green), rect, format1);
                    }
                    if (Welt[j, i] == weg)
                    {
                        Rectangle rect = new Rectangle(pixelgröße * i, pixelgröße * j, pixelgröße, pixelgröße);
                        e.Graphics.DrawString(weg.ToString(), fontWeg, new SolidBrush(Color.Blue), rect, format1);
                    }
                    if (Welt[j, i] == spieler)
                    {
                        Rectangle rect_spieler = new Rectangle(pixelgröße * i, pixelgröße * j, pixelgröße, pixelgröße);
                        e.Graphics.DrawString(spieler.ToString(), fontSpieler, new SolidBrush(Color.Red), rect_spieler, format1);
                    }

                }
            }


Pixelgröße ist übrigens  const int pixelgröße = 22;

Jetzt hab ich aber Schwierigkeiten auf den Spieler zuzugreifen oder ist es falsch, wie ich gedacht habe?
Gezeichnet wird es nämlich so wie ich es mir vorstelle.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 06.05.21 11:31 
Ganau, denn darum hatte ich folgendes geschrieben:
Th hat folgendes geschrieben:
1. Du speicherst die Position des Spielers einerseits in x_spieler und y_spieler, andererseits direkt in der Welt (als '@'), synchronisierst aber diese nicht!
Die Darstellung der Spielerfigur sollte daher nur eine der beiden Möglichkeiten nutzen (bisher ist sie in der OnPaint-Methode falsch).

Du zeichnest bisher anhand der Position der Spielfigur in dem Welt-Array, bewegst aber die Figur anhand der beiden Variablen x_spieler und y_spieler - somit änderst sich die Spielfigur beim Zeichnen nicht.

Wenn du bei den beiden Variablen bleiben möchtest, dann
- zeichne nach den Schleifen die Spielfigur an der Spielfigurposition
oder
- beim Bewegen ändere die Spielfigur in dem Weltarray: Welt[y_spieler, x_spieler] = spieler (und lösche vorher mittels Welt[y_spieler, x_spieler] = weg die alte Position).
Initial mußt du aber die beiden Variablen für die Spielfigurposition korrekt setzen (anhand der Position von spieler im Welt-Array)!

Für diesen Beitrag haben gedankt: noiiZeee
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Do 06.05.21 11:42 
Okay ich habs hinbekommen. Geil!
Vielen dank dir! Hast mir echt weiter geholfen!
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Do 06.05.21 19:15 
Aber jetzt nochmal aus reinem Interesse,..
Wie hätte ich es denn noch programmieren können, wenn ich die Variablen nicht benutzen wollen würde? Weil die Position der Spielfigur stimmt ja, wenn ich sie so in der for-schleife zeichnen lasse. Würde mich freuen, wenn du mir das noch beantworten könntest :)
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 07.05.21 06:40 
Das habe ich wohl etwas ungenau ausgedrückt, ich meinte eher:
Wenn du bei den beiden Variablen in der OnPaint-Methode bleiben möchtest, dann zeichne nach den Schleifen die Spielfigur an der Spielfigurposition
oder
beim Bewegen ändere die Spielfigur in dem Weltarray: Welt[y_spieler, x_spieler] = spieler (und lösche vorher mittels Welt[y_spieler, x_spieler] = weg die alte Position).
Initial mußt du aber die beiden Variablen für die Spielfigurposition korrekt setzen (anhand der Position von spieler im Welt-Array)!

Ohne die Variablen müßte ja jedesmal im Welt-Array geschaut werden, wo sich die Spielerfigur befindet, und das wäre nicht sehr performant.
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Fr 07.05.21 10:07 
Danke für deine Antwort.
Brauche aber nochmal kurz deine Hilfe :lol:
Wenn ich jetzt in dieser Welt sagen wir mal auf Position Welt[1,3] ein Item habe, was ich aufsammeln möchte indem ich mich mit dem Spieler drüber bewege also ganz genau auf der Position Welt[y_spieler, x_spieler] wie kann ich dann dieses Item aus dieser Position löschen?
Hab im Internet schon länger jetzt recherchiert und nichts dazu gefunden bzw. für mehrdimensionale Arrays.
Bin zwar auf Dinge wie Array.Clear() gestoßen, was auch ziemlich praktisch wäre, würde ich nur die exakte Position angeben können.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 07.05.21 10:33 
Wenn ein Item auch einfach nur ein Wert des Welt-Arrays ist, dann überschreibe diesen Wert einfach mit dem spieler oder weg (gleicher Code wie oben beim Bewegen der Spielerfigur).

Für diesen Beitrag haben gedankt: noiiZeee
noiiZeee Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: Fr 07.05.21 10:38 
Okay hat geklappt! Das ging schnell :D
Vielen Dank für den Tipp!