Laden...

Sporadisch auftretender Fehler, vermutlich beim Invalidate(), oder durch Threadübergriffe (Bombrman)

Erstellt von Animal21 vor 13 Jahren Letzter Beitrag vor 13 Jahren 1.907 Views
A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 13 Jahren
Sporadisch auftretender Fehler, vermutlich beim Invalidate(), oder durch Threadübergriffe (Bombrman)

Hey Leute,
ich programmiere z.Z. an nem Bomberman-Spiel auf Wunsch eines Freunden.
Zur Verwaltung der Spieldaten gibt es eine "Game" Klasse und eine "GamePanel"-Klasse, welches wie zu erwarten ein Panel ist und worauf das Spiel auch gezeichnet wird.

Alle relevanten Daten, wie Spieler, Feldobjekte, ect. liegen in "Game".
Tastatureingaben werden über "ProcessCmdKey" des MainForm abgefangen, an die "Game"-Klasse übergeben und dort verarbeitet (betreffende Figur wird bewegt, legt eine bombe...) der Invalidate(), um die Änderung auch sichtbar zu machen, wird über ein Event, welches von "GamePanel" registriert wird, ausgelöst.
Beispiel:
Spieler drückt Leertaste und setzt damit eine Bombe ab:
ProcessCmdKey() bemerkt, dass die Leertaste gedrückt wurde und meldet dies "Game".
"Game" ermittelt welcher Spieler etwas gemacht hat und gibt ihm in diesem fall den Befehl eine Bombe zu legen ("BombeExplodes" wird für die entsprechende bombe bei "Game" registiert")
SpielerX legt die Bombe und startet den Timer zur Explosion.
Bombe explodiert und imformiert alle dies wissen wollen darüber.
"Game" bemerkt, dass eine Bombe explodiert ist entfernt diese vom feld und legt entsprechend der sprengkraft Feuer in die felder (hier wird nun "FeuerErlischt" registriert und der Timer für die dauer des brennens gestartet)
Ist das Feuer 'erloschen', wird es vom feld entfernt un ein invalidate an das "GamePanel" geschickt.

Folgend kommt noch code, aber hier erstmal mein Problem an dem Beispiel:
Sobald das Feuer "erloschen" und entfernt wird, stürzt das "GamePanel" (Also die Zeichenoberfläche) MANCHMAL ab, da der fehler sporadisch auftritt, dacht ich an einen Threadübertritt..., aber gezielte lock-befehle haben bisher nix gebracht.
Zusätzlich, stürzt die Zeichenoberfläche auch MANCHMAL ab, wenn 2 bomben nebeneinander explodieren (undzwar ZUR Explosion und nich erst beim erlischen des Feuers)
das field "feldListe" sieht wie folgt aus:


    private Dictionary<Point, List<FieldObject>> felderListe;

Ein "FieldObject" kann ein Spieler, eine Bombe, Feuer oder ein aufhebbaren Item sein.
Bombe-Klasse:
Im Konstruktor der Bombenklasse wird "BombeZünden()" ausgelöst.


    private void BombeZünden() {
        Thread t = new Thread(new ParameterizedThreadStart(SprengTimer));
        t.IsBackground = false;
        t.Start(sprengkraft);
    }
    private void SprengTimer(object sprengkraft_) {
        Thread.Sleep(3500);
        Sprengen((int)sprengkraft_);
    }
    private void Sprengen(int sprengkraft_) {
        OnExploding(this);
    }

Game-Klasse:


    private void BombExplodes(object sender, Bombe.BombeEventArgs e) {
        lock(felderListe) {
            felderListe[e.Bombe.Point].Remove(e.Bombe);
            CreateFeuerAt(e.Bombe.Point);
            #region North
            for(int i = 1; i <= e.Bombe.Sprengkraft; i++) {
                Point p = new Point(e.Bombe.Point.X, e.Bombe.Point.Y - i);
                if(p.Y >= 0)
                    if(CreateFeuerAt(p)) break;
            }
            #endregion
            #region South
            ...
            #endregion
            #region East
            ...
            #endregion
            #region West
            ...
            #endregion
            e.Bombe.Gamer.GiveItem(Gamer.Items.Bombe);
        }
    }
    private bool CreateFeuerAt(Point p) {
        bool ret = false;
        Feuer f = new Feuer(p);
        f.FeuerErlischt += new Feuer.FeuerEventHandler(FeuerErlischt);
        FelderListe[p].Add(f);

        if(Game.getObjectFromList<Stein>(FelderListe[p], true) != null)
            ret = true;

        OnUpdateGamePanel(p.X, p.Y, fieldSize, fieldSize);
        return ret;
    }
    private void FeuerErlischt(object sender, Feuer.FeuerEventArgs e) {
        lock(felderListe) {
            int index = Game.GetSpecificObjectIndexFromList<Feuer>(felderListe[e.Feuer.Point], e.Feuer);
            if(index >= 0) {
                felderListe[e.Feuer.Point].RemoveAt(index);
            
            OnUpdateGamePanel(e.Feuer.Point.X, e.Feuer.Point.Y, fieldSize, fieldSize);
            }
        }
    }

Feuer-Klasse:
Auch hier wird "FeuerAnzünden()" im Konstruktor ausgelöst.


    private void FeuerAnzünden() {
        Thread t = new Thread(new ThreadStart(BrennTimer));
        t.IsBackground = false;
        t.Start();
    }
    private void BrennTimer() {
        Thread.Sleep(750);
        Löschen();
    }
    private void Löschen() {
        OnLöschen(this);
    }

Ich hoffe ihr könnt mir einen Hinweis geben, wie ich dieses Problem lösen kann, Anregungen zur Fehlersuche (Vorgehensweise) wären auch super, mir gehen nämlich langsam die Ideen aus.

PS: Eine kleine zwischen Frage hät ich noch, ist es klug und OK, wenn man auf ein Panel zeichner, oder wäre da eine PictureBox besser geeignet, gibts da Vor-/Nachteile?

mfg
Ani

5.742 Beiträge seit 2007
vor 13 Jahren

Hallo Animal21,

  
private void FeuerAnzünden() {  
   Thread t = new Thread(new ThreadStart(BrennTimer));  
   t.IsBackground = false;  
   t.Start();  
}  
private void BrennTimer() {  
   Thread.Sleep(750);  
   Löschen();  
}  
  

Ouhh - ganz böse...
Kein Wunder, dass da Probleme auftreten.

Siehe [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) und verwende einen Timer.

A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 13 Jahren

Feuer:

    private void BrennTimer() {
        Thread.Sleep(750);
        Löschen();
    }
    private void Löschen() {
        OnLöschen(this);
    }
    protected virtual void OnLöschen(Feuer feuer_) {
        if(FeuerErlischt != null)
            FeuerErlischt(this, new FeuerEventArgs(this));
    }

Game:


        f.FeuerErlischt += new Feuer.FeuerEventHandler(FeuerErlischt);

    private void FeuerErlischt(object sender, Feuer.FeuerEventArgs e) {
        lock(felderListe) {
            int index = Game.GetSpecificObjectIndexFromList<Feuer>(felderListe[e.Feuer.Point], e.Feuer);
            if(index >= 0) {
                felderListe[e.Feuer.Point].RemoveAt(index);

            //Wichtig:
            OnUpdateGamePanel(e.Feuer.Point.X, e.Feuer.Point.Y, fieldSize, fieldSize);
            }
        }
    }

GamePanel:


    private void game_UpdateGamePanel(object sender, Game.UpdateGamePanelEventArgs e) {
        lock(mutex)
            //Wichtig:
            BeginInvoke(new InvalidateAtEventHandler(InvalidateAt), e.X, e.Y, e.Width, e.Height, true);
    }
5.742 Beiträge seit 2007
vor 13 Jahren

@Animal21:
Den Code hast du doch schon (so ähnlich) gepostet?!?
Hast du den verlinkten Artikel überhaupt [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) angeschaut?

U
1.688 Beiträge seit 2007
vor 13 Jahren

Hallo,

kann mich winSharp93 nur anschließen - nimm einen Timer. Deine Threads sind recht sinnlos. Und das

  
    private void game_UpdateGamePanel(object sender, Game.UpdateGamePanelEventArgs e) {  
        lock(mutex)  
            //Wichtig:  
            BeginInvoke(new InvalidateAtEventHandler(InvalidateAt), e.X, e.Y, e.Width, e.Height, true);  
    }  

ist auch Quatsch. BeginInvoke schreibt den Aufruf nur in eine Art Warteschlange, danach wird der "mutex" wieder frei gegeben. Er nützt also höchstwahrscheinlich überhaupt nichts.

Multithreading ist kein "Spiel" - man muss sich schon genau damit beschäftigen. Und nicht solange daran herumbasteln, bist es funktioniert (wie es herbivore kürzlich ähnlich ausgedrückt hat).

Ach ja - was heißt eigentlich "abstürzen"? Fehlermeldung? Exception?

A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 13 Jahren

ah ok, dann hab ich "BeginInvoke" immer falsch benutzt :<

abstürzen heißt, dass das panel weiß mit nem roten kreuz drinne wird. werd mir den artikel jetzt mal zu gemüte führen...

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Animal21,

werd mir den artikel jetzt mal zu gemüte führen...

falsche Reihenfolge. Bitte immer vor dem Posten in die FAQ schauen.

herbivore