Laden...

Nach einer bestimmten Zeit zusätzliche Kreise zeichnen

Erstellt von RingYK vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.425 Views
R
RingYK Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren
Nach einer bestimmten Zeit zusätzliche Kreise zeichnen

[Tutorial] Zeichnen in Windows-Forms-Programmen (Paint/OnPaint, PictureBox) [Tutorial] Zeichnen in Windows-Forms-Programmen (Paint/OnPaint, PictureBox) Hallo,

ich bin Anfängfer und bin gerade dabei das Spiel "Asteroids" nachzuprogrammieren.
Es läuft ganz gut, jedoch habe ich ein paar Probleme.

Die Asteroiden sind bei mir Kreise, die ich in einer pictureBox zeichnen lasse, wenn ich auf den "Start"-Button drücke.
Diese lasse ich dann mit einem Timer nach unten bewegen.
Jedoch möchte ich, dass immer nach einem bestimmten Zeitabschnitt wieder neue Kreise gezeichnet werden...
Ich habe das schon mit einem zweiten Timer und einer zweiten pictureBox probiert, aber es will alles nicht hinhauen... 😦
Habt ihr Anhaltspunkte?

Mein Code vom Start-Button:


  private void buttonStart_Click(object sender, EventArgs e)
        {
            timerAsteroid.Start();
            Random Rnd = new Random();
            int RndNumber2 = Rnd.Next(8, 20);

            if (checkBox1.Checked == true)
            {
                triangle = new Triangle() { X = 300, Y = 550, Vx = (int)numericUpDown4.Value, Vy = (int)numericUpDown4.Value, D1 = (int)numericUpDown5.Value, D2 = (int)numericUpDown6.Value, D3 = (int)numericUpDown7.Value, Alpha = (int)numericUpDown8.Value };
                listBox1.Items.Add(triangle);
                pictureBox1.Refresh();
            }
            else if(checkBox2.Checked == true)
            {
                rectangle = new Rectangle() { X = 300, Y = 555, Width = (int)numericUpDown2.Value, Height = (int)numericUpDown3.Value, Vx = (int)numericUpDown4.Value, Vy = (int)numericUpDown4.Value};
                listBox1.Items.Add(rectangle);
                pictureBox1.Refresh();
            }
            for (int i = 0; i < RndNumber2; i++)
            {
                int rndnumber1 = Rnd.Next(0, 1600);
                Asteroid asteroids = new Asteroid() { X = rndnumber1, Radius = (int)numericUpDown1.Value, Height = (int)numericUpDown1.Value * 2, Width = (int)numericUpDown1.Value * 2 };
                listBox1.Items.Add(asteroids);
                pictureBox1.Refresh();
            }
        }

Mein Code in Form1.cs (Ausschnitt):


  private void timerMoving_Tick(object sender, EventArgs e)
        {
            TimeSpan elapsed = DateTime.Now - lastPaint;
            double resultSeconds = elapsed.TotalSeconds;

            List<Shot> toDisposeShot = new List<Shot>();
            List<Asteroid> toDisposeAsteroid = new List<Asteroid>();

            foreach (Object item in listBox1.Items)
            {
                Triangle triangle = item as Triangle;
                Rectangle rectangle = item as Rectangle;
                Asteroid asteroid = item as Asteroid;
                Shot shot = item as Shot;
                spaceship = item as Spaceship;
                
                #region Zusammenführung  
                if (item is Spaceship)
                {
                    Spaceship geoObject = item as Spaceship;
                    if (objPosition == Position.Left)
                    {
                        geoObject.Vx -= 10;
                    }
                    if (objPosition == Position.Right)
                    {
                        geoObject.Vx += 10;
                    }
                    if (objPosition == Position.Up)
                    {
                        geoObject.Vy -= 10;
                    }
                    if (objPosition == Position.Down)
                    {
                        geoObject.Vy += 10;
                    }
                    if (geoObject.X <= 0 && geoObject.Vx < 0 || geoObject.X <= 0 && geoObject.Vy < 0)
                    {
                        geoObject.Vx = 0;
                        geoObject.Vy = 0;
                        geoObject.X = 0;
                    }
                    if (geoObject.Y <= 0 && geoObject.Vy < 0 || geoObject.Y <= 0 && geoObject.Vx < 0 || geoObject.Y <= 0 && geoObject.Vx > 0)
                    {
                        geoObject.Vy = 0;
                        geoObject.Vx = 0;
                        geoObject.Y = 0;
                    }
                    if (geoObject.X >= pictureBox1.Width - geoObject.Width && geoObject.Vx > 0 || geoObject.X >= pictureBox1.Width - geoObject.Width && geoObject.Vy > 0 || geoObject.X >= pictureBox1.Width - geoObject.Width && geoObject.Vy < 0)
                    {
                        geoObject.Vx = 0;
                        geoObject.Vy = 0;
                        geoObject.X = pictureBox1.Width - geoObject.Width;
                    }
                    if (geoObject.Y >= pictureBox1.Height - geoObject.Height && geoObject.Vy > 0 || geoObject.Y >= pictureBox1.Height - geoObject.Height && geoObject.Vx > 0 || geoObject.Y >= pictureBox1.Height - geoObject.Height && geoObject.Vx < 0)
                    {
                        geoObject.Vy = 0;
                        geoObject.Vx = 0;
                        geoObject.Y = pictureBox1.Height - geoObject.Height;
                    }
                    geoObject.X += geoObject.Vx * resultSeconds;
                    geoObject.Y += geoObject.Vy * resultSeconds;

                //    if (geoObject.X < 0)
                //    {
                //        geoObject.X = 0;
                //    }
                //    if (geoObject.Y < 0)
                //    {
                //        geoObject.Y = 0;
                //    }
                //    if (geoObject.X > pictureBox1.Width - geoObject.Width)
                //    {
                //        geoObject.X = pictureBox1.Width - geoObject.Width;
                //    }
                //    if (geoObject.Y > pictureBox1.Height - geoObject.Height)
                //    {
                //        geoObject.Y = pictureBox1.Height - geoObject.Height;
                //    }
                }
                #endregion
                else if (asteroid != null)
                {
                    if (asteroid.Kmy > pictureBox1.Height)
                    {
                        toDisposeAsteroid.Add(asteroid);
                    }
                    else
                    {
                        asteroid.Vy += 2;
                        asteroid.Y += asteroid.Vy * resultSeconds;
                    }
                }
                else if (shot != null)
                {
                    if (objShoot == true)
                    {
                        if (shot.X < 0 || shot.Y < 0)
                        {
                            toDisposeShot.Add(shot);
                        }
                        else
                        {
                            shot.Y += shot.Vy * resultSeconds;
                            shot.X += shot.Vx * resultSeconds;
                        }
                    }
                }
            }
            lastPaint = DateTime.Now;
            for (int i = 0; i < toDisposeShot.Count; i++)
            {
                var shot = toDisposeShot[i];
                listBox1.Items.Remove(shot);
                shot.Dispose();
            }
            for (int i = 0; i < toDisposeAsteroid.Count; i++)
            {
                var asteroid = toDisposeAsteroid[i];
                listBox1.Items.Remove(asteroid);
                asteroid.Dispose();
            }
            //pictureBox1.Refresh();
            UpdatePictureBox();
        }
        public void timerAsteroid_Tick(object sender, EventArgs e)
        {
            //    if (asteroid != null)
            //    {
            //        Random Rnd = new Random();
            //        int RndNumber3 = Rnd.Next(8, 30);

            //        List<GeometricObject> NewAsteroids = new List<GeometricObject>();
            //        for (int i = 0; i < RndNumber3; i++)
            //        {
            //            int RndNumber1 = Rnd.Next(0, 1600);
            //            asteroid = new Asteroid() { X = RndNumber1, Radius = (int)numericUpDown1.Value, Height = (int)numericUpDown1.Value * 2, Width = (int)numericUpDown1.Value * 2 };
            //            listBox1.Items.Add(asteroid);
            //            //NewAsteroids.Add(asteroid);

            //            foreach (object item in listBox1.Items)
            //            {
            //                GeometricObject asteroid = item as GeometricObject;
            //                if (asteroid != null) 
            //                {
            //                pictureBox2.Refresh();
            //                }
            //            }
            //            UpdatePictureBox();
            //    }
            //}
            ////pictureBox1.Refresh();
            //UpdatePictureBox();

            pictureBox2.Refresh();
        }
        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            List<GeometricObject> NewAsteroid = new List<GeometricObject>();
            Random Rnd = new Random();
            int RndNumber1 = Rnd.Next(0, 1600);

            asteroid = new Asteroid() { X = RndNumber1, Radius = (int)numericUpDown1.Value, Height = (int)numericUpDown1.Value * 2, Width = (int)numericUpDown1.Value * 2 };
            NewAsteroid.Add(asteroid);
            
        }
        private void UpdatePictureBox()
        {
            if (pictureBox1.Image == null) pictureBox1.Image = new Bitmap(2400, 2050);

            List<GeometricObject> toDeleteSpaceship = new List<GeometricObject>();

            using (var g = Graphics.FromImage(pictureBox1.Image))
            {
                g.DrawImage(Resources.weltraum001_1400x1050, 0, 0);

                // grap all astroids
                List<GeometricObject> astroids = new List<GeometricObject>();
                foreach (var item in listBox1.Items)
                {
                    if (item is Asteroid)
                    {
                        astroids.Add((Asteroid)item);
                    }
                }
                foreach (Object item in listBox1.Items)
                {
                    Spaceship rectangle = item as Spaceship;
                    Spaceship triangle = item as Spaceship;
                    Spaceship spaceship = item as Spaceship;
                    GeometricObject asteroid = item as GeometricObject;
                    GeometricObject shot = item as GeometricObject;

                    if (triangle != null)
                    {
                        triangle.Draw(g);
                    }
                    if (rectangle != null)
                    {
                        rectangle.Draw(g);
                    }
                    if (asteroid != null)
                    {
                        asteroid.Draw(g);
                    }

                    if (item is Spaceship)
                    {
                        var collison = ((Spaceship)item).CheckCollison(astroids);
                        if (collison != null)
                        {
                            toDisposeSpaceship.Add(spaceship);
                            MessageBox.Show("Bum, das Raumschiff wurde zerstört durch: " + collison.Object2.ToString());
                            //toDeleteAfterShot();
                        }
                        if (item is Shot)
                        {
                            if (objShoot == true)
                            {
                                shot.Draw(g);
                            }
                        }
                    }
                }
                      pictureBox1.Refresh();
            }
        }

Gruß und Danke im Vorraus! 😄

W
195 Beiträge seit 2008
vor 7 Jahren

Beschäftige Dich mal mit dem Thema Debugging/Debugger - damit solltest Du den/die Fehler finden können. Ich denke nicht, dass Dir hier jemand diese Arbeit abnehmen möchte/wird...

R
RingYK Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Wenn ich debugge, geht der Programmablauf gar nicht erst in den "TimerAsteroid_Tick"-Event hinein....

R
RingYK Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Es muss ja auch keiner eine Komplettlösung hier schreiben.
Aber ein paar Anhaltspunkte, wie ich das schaffen könnte wäre echt nett. 😃

709 Beiträge seit 2008
vor 7 Jahren

Hast du evtl. den Timer nicht gestartet oder den EventHandler nicht ans Event gehängt?

2.207 Beiträge seit 2011
vor 7 Jahren

Hallo RingYK,

du startest den Timer zwar, aber sagst ihm nie, dass er beim Ablauf auch in die Methode soll. Zumindest ist das in dem Code oben nicht ersichtlich.

Gruss

Coffeebean

R
RingYK Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Hallo @Coffeebean,

Ich dachte mit "pictureBox2.Refresh()" sage ich, dass er dahingehen soll?

In der pictureBox habe ich noch hinzugefügt:


  foreach (object item in NewAsteroid)
            {
                GeometricObject asteroid = item as GeometricObject;

                if (asteroid != null) 
                {
                    asteroid.Draw(e.Graphics);

                }
            }
            pictureBox2.Refresh();

Leider klappt auch das nicht...

1.040 Beiträge seit 2007
vor 7 Jahren

Coffeebean meint, ob du den Eventhandler für das Ticken des Timers angemeldet hat.
Timer.Tick-Ereignis

R
RingYK Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Also in meinem Form1.Designer.cs sieht es folgendermaßen aus:


  // timerAsteroid
            // 
            this.timerAsteroid.Enabled = true;
            this.timerAsteroid.Interval = 4000;
            this.timerAsteroid.Tick += new System.EventHandler(this.timerAsteroid_Tick);
            // 

Mit "timerMoving" sieht es auch so aus und der funktioniert ja...

16.806 Beiträge seit 2008
vor 7 Jahren

Hast Du mal den Debugger aufgerufen, ob überhaupt das in der Reihenfolge und mit den Informationen ausgeführt wird, wie Du Dir das im Kopf vorstellst?
[Artikel] Debugger: Wie verwende ich den von Visual Studio?

Zur Not mal Dir den Workflow auf ein Blatt papier, dann sieht man oft einen Denkfehler leicht als im Code.
Danach Debugger anwerfen und schauen, ob der erdachte Workflow so auch umgesetzt wurde.

1.040 Beiträge seit 2007
vor 7 Jahren

this.timerAsteroid.Enabled = true;

Auch wenn es wahrscheinlich nicht das Problem löst:
Wenn ein Timer manuell (per Start-Methode) gestartet werden soll, dann muss Enabled auf jeden Fall erstmal auf false stehen - ist der Wert true, startet der Timer sofort.

R
RingYK Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Danke @p!lle, ich habe es angepasst. ^^

Soo, jetzt habe ich es soweit, dass er in den Timer geht, auf einen neuen Kreis verweist und dann in die Draw-Methode des Kreises hineingeht. Aber genau dann gibt er mir eine Null-Referenz züruck...

Also: > Fehlermeldung:

Additional information: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.

Hier mein Code:



  public void timerAsteroid_Tick(object sender, EventArgs e)
        {
               NewAsteroids(g);
        }
        public void NewAsteroids(Graphics g)
        {
            List<GeometricObject> NewAsteroid = new List<GeometricObject>();
            Random Rnd = new Random();
            int RndNumber1 = Rnd.Next(0, 1600);

            Asteroid asteroid = new Asteroid() { X = RndNumber1, Radius = (int)numericUpDown1.Value, Height = (int)numericUpDown1.Value * 2, Width = (int)numericUpDown1.Value * 2 };
            NewAsteroid.Add(asteroid);

            foreach (object item in NewAsteroid)
            {
                //GeometricObject asteroid = item as GeometricObject;

                if (asteroid != null)
                {
                    asteroid.Draw(g);
                }
            }
        }

Und meine Draw-Methode in der Klasse "Asteroid":



  public class Asteroid : GeometricObject, IDisposable
    {
        public override void Draw(Graphics g)
        {
            g.FillEllipse(Brushes.Brown, (float)X, (float)Y, (float)Height, (float)Width);

            //g.DrawLine(Pens.Beige, (float)Kmx, (float)Kmy, (float)Kmx + (float)radius, (float)Kmy + (float)radius);  
        }

g.FillEllipse(Brushes.Brown, (float)X, (float)Y, (float)Height, (float)Width); ---> Hier kommt die Null-Referenz.

Ich frage mich wieso, da ich doch überall die Kreise instanziiert habe?
Oder könnte das an etwas anderem liegen?

Gruß

1.040 Beiträge seit 2007
vor 7 Jahren
R
RingYK Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Ahh, jetzt funktioniert es! Danke! 😄

5.299 Beiträge seit 2008
vor 7 Jahren

Ich find bei deim Code schlecht gelöst ist, dass du deine ZeichenObjekte in einer Listbox verwaltest.
In diesem Listbox-Control kann ja eiglich nichts vernünftiges angezeigt werden.
Da nimm lieber eine List<Object> - die ist eh kein Gui-Element.

Auch kannst du bei diesem Problem sehr schön Vererbungslehre und OOP walten lassen:
Schaff dir eine abstrakte Basisklasse "ZeichenObjekt", von der deine verschiedenen ZeichenObjekte erben, und die je nach ihrer Art die Basis-Zeichen-Methoden überschreiben.
Dann kann jedes Objekt - egal welcher Art - sich selber zeichnen, mit seiner je spezifischen Überschreibung, und die ganzen Typ-Überprüfungen sind überflüssig.
Tu die ZeichenObjekte dann nicht in eine List<Object>, sondern in eine List<ZeichenObjekt>.

Hier ist sowas vorgeturnt mit noch paar weiteren Schikanen: Performantes OwnerDrawing

Der frühe Apfel fängt den Wurm.