Laden...

XNA: Value does not fall within the expected range

Erstellt von Vhelium vor 11 Jahren Letzter Beitrag vor 11 Jahren 3.356 Views
V
Vhelium Themenstarter:in
28 Beiträge seit 2011
vor 11 Jahren
XNA: Value does not fall within the expected range

Hallo zusammen,

Ich suche schon seit mehreren Tagen für eine Lösung zu meinem Problem, habe aber noch nicht einmal die wirkliche Ursache gefunden.
Es geht darum, dass zur Runtime plötzlich Code übersprungen wird und somit kurz darauf die Exception "Value does not fall within the expected range", weil der ContentManager immer noch bei dem einen Bild hängt und beim 2ten Aufruf die Exception auslöst.

Hier die Fehlermeldung:

Fehlermeldung:
System.ArgumentException wurde nicht behandelt.
Message=Value does not fall within the expected range.
StackTrace:
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary2.Insert(String key, Object value, Boolean add) at System.Collections.Generic.Dictionary2.Add(String key, Object value)
at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
at TheDispatersSoul.Screens.TutorialScreen.LoadContent(Boolean LoadAll)
at TheDispatersSoul.Screen_Management.ScreenManager.AddScreen(Screen oScreen)
at TheDispatersSoul.Levels.Level.LevelEventActivated(LevelEvent oEvent)
at TheDispatersSoul.Levels.Level.Update(GameTime oGameTime, Vector2 oActiveScreen)
at TheDispatersSoul.Screens.GameScreen.Update(GameTime oGameTime, Boolean IsHidden, Boolean bHasFocus)
at TheDispatersSoul.Screen_Management.ScreenManager.Update(GameTime oGameTime)
at Microsoft.Xna.Framework.Game.Update(GameTime gameTime)
at TheDispatersSoul.Game1.Update(GameTime gameTime)
at Microsoft.Xna.Framework.Game.Tick()
at Microsoft.Xna.Framework.Game.HostIdle(Object sender, EventArgs e)
at Microsoft.Xna.Framework.GameHost.OnIdle()
at Microsoft.Xna.Framework.MobileGameHost.RunOneFrame()
at Microsoft.Xna.Framework.MobileGameHost.gameLoopTimer_Tick(Object sender, EventArgs e)
at Microsoft.Xna.Framework.DispatcherTimerWin32.TimerElapsed(IntPtr hWnd, UInt32 uMsg, IntPtr nIDEvent, UInt32 uTime)

Ich probiere mit Ausschnitten aus meinem Code zu zeigen, was genau abläuft.
Anfangen tut das ganze wenn bei meinem Spiel das Level startet. Dann wird überprüft, ob es Ereignisse gibt, die gleich zu Begin des Levels aufgerufen werden:

public void LevelStarted()
        {
            for(int i = m_oLevelEvents.Count - 1; i >= 0; i--)
                if (m_oLevelEvents[i].ActivatedAtStart)
                {
                    LevelEventActivated(m_oLevelEvents[i]);
                    m_oLevelEvents.RemoveAt(i);
                }
        }

Das Ereignis, beim dem dann die Exception zustande kommt, ist das hinzufügen eines neuen Screens.
In der LevelEventActivated wird der Screen dem ScreenManager hinzugefügt:

public void LevelEventActivated(Components.LevelEvent oEvent)
        {
            ...
            m_oOwner.Manager.AddScreen(new Screens.TutorialScreen(Convert.ToInt32(oEvent.Value)));
        }

Wenn ein Screen hinzugefügt wird, wird die Initialize() und die LoadContent() des Screens aufgerufen und nachher wird der Screen angezeigt.

Hier der Code des aufzurufenden Screens:

public class TutorialScreen : Screen_Management.MenuScreen
    {
        private int m_iTutorialNr;
        private Texture2D m_oImage = null;
        private Components.Button m_oNextButton;
        private SpriteFont m_oFont;


        public TutorialScreen(int iTutorialNr)
        {
            m_iTutorialNr = iTutorialNr;
            ...
        }

        public override void Initialize()
        {
            m_oNextButton = new Components.Button("Buttons/Continue", 200, 65);
            m_oNextButton.Name = "cmdNext";
            m_oNextButton.Location = new Vector2(585, 400);
            m_oNextButton.Click += new EventHandler<EventArgs>(NextButton_Click);

            this.Controls.Add(m_oNextButton);

            base.Initialize();
        }

        public override void LoadContent(bool LoadAll)
        {
            m_oFont = Content.Load<SpriteFont>("sf");
            m_oImage = Content.Load<Texture2D>("Tutorials/Tut" + m_iTutorialNr);

            base.LoadContent(LoadAll);
        }
    ...
    ...

Nun sollte also, wenn das Level startet, der Event aufgerufen werden, der Screen dem Manager hinzugefügt werden und folglich aus der Liste wieder rausgelöscht werden, damit er nicht ein zweites mal aufgerufen wird. Das war ja bei:

LevelEventActivated(m_oLevelEvents[i]);
m_oLevelEvents.RemoveAt(i);

Wenn ich jetzt schaue, was genau passiert, sehe ich, dass in der TutorialScreen Klasse der Compiler(?) nur bis zu

m_oImage = Content.Load<Texture2D>("Tutorials/Tut" + m_iTutorialNr);

geht und dann plötzlich die Klasse "verlässt". Sprich

base.LoadContent(LoadAll);

wird gar nicht aufgerufen! Und auch das Ereignis wird nicht aus der Liste gelöscht, wie es eigentlich sollte bei:

m_oLevelEvents.RemoveAt(i);

sondern das wird auch übersprungen!

Da nun der LevelEvent noch in der Liste existiert wird er wenn das Level gestartet hat ein 2tes mal von der Level.Update() Methode des Levels aus aufgerufen
und bringt dann beim Laden des m_oImages (bei dem der Compiler(?) das erste mal stehen geblieben ist) hängen mit der Exception!

Ich habe schon VS 10 neu gestartet, Pc neu gestartet, die Debug Ordner gelöscht aber ich krieg den Fehler immer noch. Am Bild liegt der Fehler nicht, ich habe es schon mit diversen anderen Ressourcen versucht, aber kein Erfolg.

Kennt jemand von euch dieses Problem?
Falls ich etwas vergessen habe, einfach melden.

Danke schon mal im voraus.

Lg Vhelium

[EDIT]for-Schleife wie genannt geändert[/EDIT]

#define TRUE FALSE //Happy debugging suckers

1.346 Beiträge seit 2008
vor 11 Jahren
public void LevelStarted()  
        {  
            for(int i = 0; i < m_oLevelEvents.Count; i++)  
                if (m_oLevelEvents[i].ActivatedAtStart)  
                {  
                    LevelEventActivated(m_oLevelEvents[i]);  
                    m_oLevelEvents.RemoveAt(i);  
                }  
        }  

Wenn du bei Element 0 bist lädtst du das, und entfernst das. Jetzt ist das Element, was auf Position 1 war das neue auf position 0. Jetzt geht die Schleife weiter und i ist 1. Damit hast du das neue Element auf 0 übersprungen. Gehe doch die Liste komplett durch ohne zu entfernen, und leere die Liste zum Schluss

1.552 Beiträge seit 2010
vor 11 Jahren

Oder geh die Liste von hinten nach vorne durch, dann kannst du nach belieben entfernen


for(int i = list.Count; i >= 0; i--)
{
    [...]
}

Mein Blog
Meine WPF-Druckbibliothek: auf Wordpress, myCSharp

Hinweis von herbivore vor 11 Jahren

Auch wenn man von hinten nach vorne vorgeht, bleibt der Aufwand quadratisch, was sich für große Listen verbietet. Wie man die Element mit linearem Aufwand löschen kann, steht in [FAQ] Listenelemente suchen und entfernen.

V
Vhelium Themenstarter:in
28 Beiträge seit 2011
vor 11 Jahren

Das wird in der Tat zu Fehlern führen, ich habe es im Code mal berichtigt.
Jedoch ist dies nicht die Ursache für den oben genannten Fehler.
Die

m_oLevelEvents.RemoveAt(i);

wird nicht einmal aufgerufen..
Das ist ja das komische.

#define TRUE FALSE //Happy debugging suckers

1.552 Beiträge seit 2010
vor 11 Jahren

Dann wird der Fehler wahrscheinlich in der LevelEventActivated zu finden sein.
Ich denke mit der Hilfe des Debuggers wird du den Fehler sicherlich lokalisieren können.

Mein Blog
Meine WPF-Druckbibliothek: auf Wordpress, myCSharp

V
Vhelium Themenstarter:in
28 Beiträge seit 2011
vor 11 Jahren

Die LevelEventActivated() ruft wie oben gezeigt nur die LoafContent() und Initialize() des Screens (in dem Fall der TutorialScreen) auf. Die Pünktchen stehen nur für ne switch schleife.
Ich denke nicht dass dort der Fehler ist, denn ich benutze das selbe System auch am anderen Orten im Projekt und hatte diesen Fehler noch nie.

#define TRUE FALSE //Happy debugging suckers

T
2.224 Beiträge seit 2008
vor 11 Jahren

Ich hätte nur eine Vermutung die aber nicht stimmen muss.
Kann es ggf. sein, dass dein Switch einen Wert erhält der kein case hat?
Und ggf. könnte bei dem fehlenden default dann auch ein Fehler auftretten, da kein passender Case vorhanden ist.

Hatte den Fall noch nicht in C# gehabt würde aber wegen dem Range ggf. Sinn machen.
Müsste aber geprüft werden.
Ansonsten hilft nur Debuggen bis es kracht.
Anders wirst du den Fehler nicht so schnell finden.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

V
Vhelium Themenstarter:in
28 Beiträge seit 2011
vor 11 Jahren

Die switch verhält sich ganz normal, wie es ein sollte. Es wird 2mal der "Tutorial" case aufgerufen.
Vollständiger Code:

public void LevelEventActivated(Components.LevelEvent oEvent)
        {
            switch (oEvent.Typ)
            {
                case "Dialog":
                    OnShowDialogue(Convert.ToInt32(oEvent.Value));
                    break;
                case "Message":
                    OnPlayMessage(oEvent.Value);
                    break;
                case "AddItem":
                    Player.Instance.Inventar.AddItem(AllItems[oEvent.Value].Clone());
                    OnPlayMessage("You obtained '" + oEvent.Value + "'.");
                    break;
                case "Tutorial":
                    m_oOwner.Manager.AddScreen(new Screens.TutorialScreen(Convert.ToInt32(oEvent.Value)));
                    break;
                default:
                    throw new Exception("Eventtyp \"" + oEvent.Typ + "\" wurde nicht gefunden!");
            }
        }

Es hört sich wahrscheinlich etwas komisch an, aber ich weiss echt nicht mehr wo der Fehler auf meiner Seite im Code liegen kann. Der Debugger überspringt einfach Codezeilen. Ich habe schon den halben Code voll Haltepunkten und es wird mir immer misteriöser.

Das einzige, was ich mir vorstellen kann, ist dass 2 Prozesse parallel laufen denn der Debuger verhält sich immer wieder etwas anderes und teilweise sind die Abrufreihenfolgne anders als bei einem vorherigen Durchgang.
Ich wüsste aber nicht, wie ein solcher 2ter Prozess zu Stande kam.

EDIT:
Vermutung:

public class LoadLevelScreen : Screen_Management.MenuScreen
    {
        private Texture2D m_oBackground1;
        private SpriteBatch m_oSprite = null;
        SpriteFont m_oFont = null;

        int LevelNr = 1;

        Thread thread;
        float elapsed = 0.0f;
        string points = "";

        public LoadLevelScreen(int iLevelNr)
        {
            this.IsOpaque = true;
            this.BlendInTime = new TimeSpan(1000000);
            this.BlendOutTime = new TimeSpan(1000000);

            LevelNr = iLevelNr;
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        public override void LoadContent(bool LoadAll)
        {
            base.LoadContent(LoadAll);
            if (LoadAll)
            {
                m_oSprite = new SpriteBatch(this.Device);
                m_oBackground1 = this.Content.Load<Texture2D>(@"Backgrounds\LoadScreen");
                m_oFont = this.Content.Load<SpriteFont>("sf_LoadScreen");

            }

            thread = new Thread(
           LoadGame);
            thread.Start();
        }

        private void LoadGame()
        {
            this.Manager.GameScreen.LoadLevel(LevelNr);

            this.Manager.AddScreen(this.Manager.GameScreen);
            this.Manager.RemoveScreen(this);
            this.Manager.GameScreen.Level.LevelStarted();
        }

In der LoadGame() zu beachten, dass von dem Prozess aus die LevelStarted() aufgerufen wird(Code der LevelStartet im Startpost). In der GameScreen Klasse wird auch jeweils in der Update() die Update des Levels aufgerufen von welcher der genannte 2te Zugriff auf die LevelEventsActivatet() statt findet. Ist es möglich, dass der Prozess noch nicht fertig ist, bevor dann die Update in der LevelKlasse den selben Aufruf macht?

#define TRUE FALSE //Happy debugging suckers

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Vhelium,

Der Debugger überspringt einfach Codezeilen

möglicherweise ist das Projekt nicht aktuell (compiliert) oder in anderer Weise korrupt.

es wird mir immer misteriöser.

Möglicherweise ein Fall für [Tutorial] Vertrackte Fehler durch Vergleich von echtem Projekt mit minimalem Testprojekt finden

StackTrace:

Wenn du als Debug compilierst, sollten die Zeilennummern der Code-Zeilen im StackTrace angezeigt werden. Das sollte helfen. Auch wenn die Zeilennummern nicht mit dem Code übereinstimmen, weil das ein Indiz für meine erste Vermutung oben ist.

herbivore

V
Vhelium Themenstarter:in
28 Beiträge seit 2011
vor 11 Jahren

Besten Dank für die Antworten.
Ich werde es einerseits mit den verschiedenen Threads nochmals genaustens überprüfen und auch den Vorschlag mit dem Stack Trace ist gut, werd ich auch machen.

Momentan setzte ich gerade den Rechner auf welchem ich arbeite neu auf und somit kann ich es erst Abends testen.

Gruss Vhelium

#define TRUE FALSE //Happy debugging suckers

V
Vhelium Themenstarter:in
28 Beiträge seit 2011
vor 11 Jahren

Also, Problem schlussendlich gelöst.
Das Problem lag an 2 gleichzeitig laufenden Threads,
die dann (genau bei dem einen Screen) zur gleichen Zeit mit dem gleichen ContentManager-Objekt auf die gleiche Ressource zugreifen.

Gelöst habe ich es, indem ich auf die Beendung des ersten Threads warte und erst dann weiter gehe.

Gruss Vhelium.

#define TRUE FALSE //Happy debugging suckers