Threadproblem -> Variablendeklaration



  • Hallo,

    wir programmieren im Moment das Spiel "Pong" im Unterricht mithilfe von C# und WPF. Dabei möchte ich natürlich gleichzeitig Tastaturabfragen abfragen und die Spielschleife laufen lassen.
    Dabei bin ich auf den Backroundworker gestoßen, der dabei ganz hilfreich sein soll. Ich möchte Zugriff auf folgende Variablen innerhalb der Spielschleife haben:

    double computerX = Canvas.GetLeft(spieler2_pad), computerY = Canvas.GetTop(spieler2_pad),           // Spielvariablen initialisieren
                      menschX = Canvas.GetRight(spieler1_pad), menschY = Canvas.GetTop(spieler1_pad),
                      ballX = Canvas.GetLeft(ball), ballY = Canvas.GetTop(ball), ballX_alt = 0, ballY_alt = 0;
    

    Wenn ich diese vor der Schleife, also innerhalb von "spielen(object sender, DoWorkEventArgs e)" erstellen möchte, bekomme ich die Meldung

    "System.InvalidOperationException" Zusätzliche Informationen: Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet.

    Rest des Codes:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Media.Animation;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.ComponentModel;
    
    namespace Pong_Reloaded_
    {
        public partial class MainWindow : Window
        {
            #region "Globale" Variablen
    
            private int zaehlen = 0;
    
            private bool pause = false;
    
            private string  sp1unten_taste = "A", 
                            sp1oben_taste = "D", 
                            sp2unten_taste = Key.Left.ToString(), 
                            sp2oben_taste = Key.Right.ToString(), 
                            sp1pause_taste = "P", 
                            sp2pause_taste = "0", 
                            sp1beenden_taste = "Q", 
                            sp2beenden_taste = "1";
    
            private TextBox txt;
            private Storyboard myStoryboard; 
            private BackgroundWorker worker = new BackgroundWorker();
    
            #endregion
    
            public MainWindow()
            {
                InitializeComponent();
    
                #region Events hinzufügen
                obenSp1Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);
                obenSp2Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);
                untenSp1Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);
                untenSp2Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);             // Ereignisse für Textboxen "angeklickt" hinzufügen
                pauseSp1Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);
                pauseSp2Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);
                beendenSp1Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);
                beendenSp2Tbox.AddHandler(UIElement.PreviewMouseDownEvent, new MouseButtonEventHandler(textbox_Click), true);
    
                obenSp1Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
                obenSp2Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
                untenSp1Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
                untenSp2Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
                pauseSp1Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
                pauseSp2Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
                beendenSp1Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
                beendenSp2Tbox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(textbox_eingabe), true);
    
                this.worker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.spielen);                                       
    
                #endregion
    
                #region Startanimation
                myStoryboard = new Storyboard();
                ThicknessAnimation commGridAnimation = new ThicknessAnimation();
                commGridAnimation.From = new Thickness(-200, 180, 0, 0);
                commGridAnimation.To = new Thickness(194, 180, 0, 0);
                commGridAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.4));
                myStoryboard.Children.Add(commGridAnimation);
                Storyboard.SetTargetName(commGridAnimation, loslegen1.Name);
                Storyboard.SetTargetProperty(commGridAnimation, new PropertyPath(Button.MarginProperty));
                myStoryboard.Begin(loslegen1); 
                #endregion
    
            }
    
            private void padbewegung(object sender, KeyEventArgs e)
            {
    
                if (Key.Enter == e.Key)
                {
                    if (startenLabel.Visibility != Visibility.Hidden) startenLabel.Visibility = Visibility.Hidden;
    
                    if (!worker.IsBusy)worker.RunWorkerAsync();             
    
                if (sp1pause_taste == e.Key.ToString())
                {
                    zaehlen++;
                    if (zaehlen == 1)
                    {
                        pause = true;
                        pauseLabel.Visibility = Visibility.Visible;
                    }
    
                    if (zaehlen == 2)
                    {
                        pause = false;
                        zaehlen = 0;
                        pauseLabel.Visibility = Visibility.Hidden;
                    }
                }
    
                if (startenLabel.Visibility == Visibility.Hidden && pause == false)
                {
                    if (sp1unten_taste == e.Key.ToString())
                    {
                        double sp1_unten = Canvas.GetTop(spieler1_pad);
                        if (sp1_unten < canvas1.Height / 2 + 70) Canvas.SetTop(spieler1_pad, sp1_unten += 15);
                    }
    
                    if (sp1oben_taste == e.Key.ToString())
                    {
                        double sp1_oben = Canvas.GetTop(spieler1_pad);
                        if (sp1_oben > canvas1.Height / 2 - 140) Canvas.SetTop(spieler1_pad, sp1_oben -= 15);
                    }
    
                    if (sp2oben_taste == e.Key.ToString())
                    {
                        double sp2_oben = Canvas.GetTop(spieler2_pad);
                        if (sp2_oben > canvas1.Height / 2 + 70) Canvas.SetTop(spieler2_pad, sp2_oben += 15);
                    }
    
                    if (sp2unten_taste == e.Key.ToString())
                    {
                        double sp2_unten = Canvas.GetTop(spieler2_pad);
                        if (sp2_unten > canvas1.Height / 2 - 140) Canvas.SetTop(spieler2_pad, sp2_unten -= 15);
                    }
                }
    
            }
    
            private void spielen(object sender, DoWorkEventArgs e)
            {
                double computerX = Canvas.GetLeft(spieler2_pad), computerY = Canvas.GetTop(spieler2_pad),           // Spielvariablen initialisieren
                      menschX = Canvas.GetRight(spieler1_pad), menschY = Canvas.GetTop(spieler1_pad),
                      ballX = Canvas.GetLeft(ball), ballY = Canvas.GetTop(ball), ballX_alt = 0, ballY_alt = 0;
    
                Random rnd = new Random();
                double x = rnd.Next(1, 5);
                double y = rnd.Next(1, 5);
                x /= 2;                                    // Zufällige x- und y Startrichtung berechnen
                y /= 2;
                ballX_alt += x;
                ballY_alt += y;
    
                do
                {
    
                    if (ballY < canvas1.Height / 2 - 140)
                    {
                        ballY_alt *= -1;
                    }
                    if (ballY > canvas1.Height / 2 + 70)
                    {
                        ballY_alt *= -1;
                    }
    
                    if (ballY_alt < 0) ballY += ballY_alt;
                    if (ballY_alt > 0) ballY -= ballY_alt;
    
                    ballX -= ballX_alt;
    
                    Canvas.SetLeft(ball, ballX);
                    Canvas.SetTop(ball, ballY);
    
                } while (true);
    
            }
    

    Wo genau deklariere ich nun meine Variablen bzw. was für eine Lösung gäbe es für das Thread Problem?
    Vielleicht gibt es ja auch noch bessere Alternativen zum Backroundworker ... wäre über jeden Vorschlag sehr froh... :p



  • Hallo,

    verwende für WPF statt BackgroundWorker besser den DispatcherTimer.
    Da dein Spielball sicherlich nicht mit höchster Geschwindigkeit (wie bisher in der do-while-Schleife) über den Bildschirm sausen soll, setze die Eigenschaft 'Intervall' entsprechend (z.B. 50 [ms]) und führe die Positionsänderung in dem Tick-Ereignis aus.
    Der DispatcherTimer synchronisiert sich automatisch mit dem WPF-Hauptthread, so daß du innerhalb des Tick-Ereignisses direkt auf WPF-Elemente, wie z.B. den Canvas, zugreifen darfst/kannst.



  • Danke dir es funkt. jetzt das hat mein Leben gerettet 🙂 Allerdings hab ich noch eine Frage und möchte dafür jetzt keinen neuen Thread starten.
    Wenn ihr im obenstehenden Code unter "private void padbewegung(object sender, KeyEventArgs e)" schaut, da ist die Bewegung der Pads programmiert.
    Diese bewegen sich allerdings sehr ruckelartig bzw. nicht flüssig. Gibt es eine bessere Lösung um das ganze "smooth" wirken zu lassen?
    Die Schritte verkleinern wäre eine Möglichkeit, jedoch ist das auch nicht optimal. Hoffentlich habt ihr einen besseren Vorschlag für mich :p


Log in to reply