2011-01-17 7 views
3

Ich versuche, mein erstes XNA-Spiel zu schreiben, und ich möchte ALT + ENTER-Funktionalität enthalten, um zwischen Vollbildmodus und Fenstermodus umzuschalten. Mein Spiel ist momentan extrem einfach, nichts Besonderes. Ich kann es entweder in Fenster (Standard) oder Vollbild (durch Aufruf von ToggleFullScreen) in meiner LoadContent-Funktion ausführen, und alles funktioniert gut.ToggleFullScreen in XNA verursacht verlorene Geräte wie Fehler

Wenn ich jedoch ToggleFullScreen in meiner Update-Funktion unter Verwendung des exakt gleichen Codes aufrufen, schlägt mein Spiel fehl. Wenn ich windowed starte und auf Vollbild umschalte, scheint es einzufrieren, es wird nur ein Frame angezeigt und keine Tastatureingabe akzeptiert (ich muss STRG + ALT + ENTF drücken). Wenn ich fullscreen starte und auf windowed umschalte, wird ein Fehler in DrawIndexedPrimitive mit der Meldung "Die aktuelle Vertex-Deklaration enthält nicht alle vom aktuellen Vertex-Shader benötigten Elemente. Normal0 fehlt." (Das ist nicht wahr; mein Vertex-Puffer ist VertexPositionNormalTexture und enthält Daten, und der GraphicsDevice-Status ist Normal).

Beide Probleme scheinen irgendwie auf eine verlorene Verbindung mit dem Gerät zu verweisen, aber ich kann nicht herausfinden, warum. Jede Ressource, die ich online gefunden habe, sagt, das Umschalten im Vollbildmodus ist so einfach wie das Aufrufen der ToggleFullScreen-Funktion, ohne dass das Zurücksetzen von Geräten oder Puffern erwähnt wird. Irgendwelche Ideen?

Edit: Hier ist ein Code, mit fremden Sachen aus links:

protected override void LoadContent() 
    { 
     graphics.PreferredBackBufferWidth = 800; 
     graphics.PreferredBackBufferHeight = 600; 
     graphics.ToggleFullScreen(); 

     basicEffect = new BasicEffect(graphics.GraphicsDevice); 
     // etc. 
    } 

    protected override void Update(GameTime gameTime) 
    { 
     if (k.IsKeyDown(Keys.Enter) && (k.IsKeyDown(Keys.LeftAlt) || k.IsKeyDown(Keys.RightAlt)) && !oldKeys.Contains(Keys.Enter)) { 
      graphics.ToggleFullScreen(); 
      gameWorld.Refresh(); 
     } 
     // update the world's graphics; this fills my buffers 
     GraphicsBuffers gb = gameWorld.Render(graphics.GraphicsDevice); 
     graphics.GraphicsDevice.SetVertexBuffer(gb.vb, 0); 
     graphics.GraphicsDevice.Indices = gb.ib; 
    } 

    protected override void Draw(GameTime gameTime) 
    { 
     // buffer reference again; these are persistent once filled in Update, they don't get created anew 
     GraphicsBuffers gb = gameWorld.Render(graphics.GraphicsDevice); 

     foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes) 
     { 
      effectPass.Apply(); 

      basicEffect.Texture = textures; 
      graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, gb.vb.VertexCount, 0, gb.ib.IndexCount/3); 
     } 
    } 
+2

Können Sie einen Code eingeben? –

Antwort

4

Die XNA Game-Klasse und der dazugehörige GraphicsDeviceManager basieren auf dem Konzept, dass die Update-Funktion den Status Ihres Spiels aktualisiert und die Draw-Funktion diesen Zustand wiederherstellt. Wenn Sie den Status des Grafikgeräts während der Aktualisierungsfunktion festlegen, brechen Sie diese Annahme ab.

Wenn Sie auf Vollbild umschalten, ist das Gerät verloren. Hinter den Kulissen des Frameworks passiert eine Menge Magie, um die Komplexität davon vor dir zu verbergen. Die mit dem Gerät verbundenen Ressourcen müssen neu erstellt werden usw. Das Framework geht davon aus, dass Sie bis zur nächsten Draw-Funktion keine Rendering-Operationen mehr ausführen werden und somit keinen konsistenten Zustand garantieren, bis dies geschieht.

So ... Stellen Sie Ihre Puffer nicht auf dem Grafikgerät während der Update-Funktion. Tun Sie dies zu Beginn der Zeichenfunktion.

Ich bin mir nicht sicher, dass dies nirgendwo explizit dokumentiert ist. Dies wird mehr durch die Methodendokumentation angedeutet. Die Game.Draw Dokumentation sagt:

Wird aufgerufen, wenn das Spiel bestimmt, ist es Zeit, einen Rahmen zu zeichnen. Überschreiben Sie diese Methode mit spielspezifischem Rendering Code.

und die Update-Methode sagt:

aufgerufen, wenn das Spiel dass Spiellogik verarbeitet werden muss, bestimmt hat. Dies kann die Verwaltung von der Spielstatus, die Verarbeitung von Benutzer Eingabe oder die Aktualisierung der Simulation Daten umfassen. Überschreiben Sie diese Methode mit der spielspezifischen Logik .

+0

Danke, das ist, was ich gesucht habe. Jetzt ist meine Frage: muss ich wirklich die Puffer auf * jeden * Aufruf zu Draw setzen? Das scheint mir viel zusätzlichen Aufwand zu verursachen, da sich meine Puffer nur ändern, wenn das Spiel aktualisiert wird. Wenn ich sie * nicht * bei jedem Zeichenaufruf setzen muss, wie bestimme ich, wann ich sie setzen soll? – tloflin

+0

Sie müssen die Puffer nicht bei jedem Aufruf zum Zeichnen setzen, aber der Overhead ist nicht sehr hoch. Aus Gründen der Einfachheit und Zuverlässigkeit würde ich es jeden Frame machen. In meinem Code deaktiviere ich auch die Puffer am Ende des Zeichnens. Dies liegt daran, dass eine Ausnahme ausgelöst wird, wenn Sie versuchen, SetData für eine Ressource aufzurufen, während sie im GraphicsDevice aktiv ist. – Empyrean

+0

Um sie nur bei Bedarf auf das Gerät zu setzen, musst du ein boolesches Flag in deinem Spiel 'deviceBuffersDirty' behalten, das du auf true setzt, wenn sich deine Puffer ändern. Dann können Sie in Ihrer Zeichenfunktion dieses Flag überprüfen, die Puffer auf das Gerät setzen, wenn es gesetzt ist, und dann das Flag löschen. – Empyrean

3

Nach einer Antwort auf den App Hub Foren Sie müssen sicherstellen, dass Sie Ihre Projektionsmatrix wieder aufzubauen.

http://forums.create.msdn.com/forums/p/31773/182231.aspx

von diesem Link ....

Auch über verschiedene Seitenverhältnisse vorsichtig sein, wenn sie voll Bildschirm Makel. Wenn Sie in 3D sind, benötigen Sie , um Ihre Projektionsmatrix neu zu erstellen. Wenn Sie in 2D sind, müssen Sie wahrscheinlich eine Skalierungsmatrix an Ihre SpriteBatch.Begin-Funktionen übergeben. Dieser Artikel hat Informationen darüber, dies in 2D zu tun.

So sollten Sie sicherstellen, dass Sie das tun (leider den Artikel, Links zu war auf Ziggyware und dass vor Ort Beitrag wurde bereits für eine ganze Weile so zu finden, um die 2D-Beispiel, das sie mit Ihnen in Verbindung gegangen könnte eine Archivseite wie die WaybackMachine verwenden, wenn Sie daran interessiert sind, es zu betrachten, da Sie 3D machen, dachte ich, dass es vielleicht nicht von Interesse ist).

+0

Danke, aber ich denke, das war nur die Rede davon, wenn Sie die Auflösung ändern. Ich bleibe die ganze Zeit in 800x600. Edit: Es sieht auch so aus, als würde ich meine Projektionsmatrix nach dem Umschalten neu aufbauen. – tloflin

2

konnte ich Vollbild/Fenster-Modus unter Verwendung der folgenden Klasse wechseln:

/// <summary> 
/// This is the main type for your game 
/// </summary> 
public class Game1 : Microsoft.Xna.Framework.Game 
{ 
    GraphicsDeviceManager graphics; 
    SpriteBatch spriteBatch; 

    private Model grid; 
    Model tank; 
    int zoom = 1; 

    Texture2D thumb; 
    Vector2 position = new Vector2(400, 240); 
    Vector2 velocity = new Vector2(-1, -1); 
    Matrix projection, view; 
    public Game1() 
    { 
     graphics = new GraphicsDeviceManager(this); 
     Content.RootDirectory = "Content"; 
    } 

    /// <summary> 
    /// Allows the game to perform any initialization it needs to before starting to run. 
    /// This is where it can query for any required services and load any non-graphic 
    /// related content. Calling base.Initialize will enumerate through any components 
    /// and initialize them as well. 
    /// </summary> 
    protected override void Initialize() 
    { 
     // TODO: Add your initialization logic here 

     base.Initialize(); 

     projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 
                   GraphicsDevice.Viewport.AspectRatio, 
                   10, 
                   20000); 

     view = Matrix.CreateLookAt(new Vector3(1500, 550, 0) * zoom + new Vector3(0, 150, 0), 
              new Vector3(0, 150, 0), 
              Vector3.Up); 

    } 

    /// <summary> 
    /// LoadContent will be called once per game and is the place to load 
    /// all of your content. 
    /// </summary> 
    protected override void LoadContent() 
    { 
     // Create a new SpriteBatch, which can be used to draw textures. 
     spriteBatch = new SpriteBatch(GraphicsDevice); 

     // TODO: use this.Content to load your game content here 
     thumb = Content.Load<Texture2D>("GameThumbnail"); 
     grid = Content.Load<Model>("grid"); 
     tank = Content.Load<Model>("tank"); 

    } 

    /// <summary> 
    /// UnloadContent will be called once per game and is the place to unload 
    /// all content. 
    /// </summary> 
    protected override void UnloadContent() 
    { 
     // TODO: Unload any non ContentManager content here 
    } 

    int ToggleDelay = 0; 
    /// <summary> 
    /// Allows the game to run logic such as updating the world, 
    /// checking for collisions, gathering input, and playing audio. 
    /// </summary> 
    /// <param name="gameTime">Provides a snapshot of timing values.</param> 
    protected override void Update(GameTime gameTime) 
    { 
     // Allows the game to exit 
     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) 
      this.Exit(); 

     var kbState = Keyboard.GetState(); 
     if ((kbState.IsKeyDown(Keys.LeftAlt) || kbState.IsKeyDown(Keys.RightAlt)) && 
      kbState.IsKeyDown(Keys.Enter) && ToggleDelay <= 0) 
     { 
      graphics.ToggleFullScreen(); 
      ToggleDelay = 1000; 
     } 

     if (ToggleDelay >= 0) 
     { 
      ToggleDelay -= gameTime.ElapsedGameTime.Milliseconds; 
     } 
     // TODO: Add your update logic here 

     int x, y; 
     x = (int)position.X; 
     y = (int)position.Y; 

     x += (int)velocity.X; 
     y += (int)velocity.Y; 
     if (x > 480 - 64) 
      velocity.X = +1; 
     if (x < 0) 
      velocity.X = -1; 
     if (y < 0) 
      velocity.Y = +1; 
     if (y > 800 - 64) 
      velocity.Y = -1; 

     position.X = x; 
     position.Y = y; 

     base.Update(gameTime); 
    } 

    /// <summary> 
    /// This is called when the game should draw itself. 
    /// </summary> 
    /// <param name="gameTime">Provides a snapshot of timing values.</param> 
    protected override void Draw(GameTime gameTime) 
    { 
     GraphicsDevice.Clear(Color.CornflowerBlue); 

     Matrix rotation = Matrix.CreateRotationY(gameTime.TotalGameTime.Seconds * 0.1f); 

     // Set render states. 
     GraphicsDevice.BlendState = BlendState.Opaque; 
     GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; 
     GraphicsDevice.DepthStencilState = DepthStencilState.Default; 
     GraphicsDevice.SamplerStates[ 0 ] = SamplerState.LinearWrap; 

     grid.Draw(rotation, view, projection); 

     DrawModel(tank, rotation, view, projection); 

     // TODO: Add your drawing code here 
     spriteBatch.Begin(); 
     spriteBatch.Draw(thumb, new Rectangle((int)position.X, (int)position.Y, 64, 64), Color.White); 
     spriteBatch.End(); 

     base.Draw(gameTime); 
    } 

    public void DrawModel(Model model, Matrix world, Matrix view, Matrix projection) 
    { 
     // Set the world matrix as the root transform of the model. 
     model.Root.Transform = world; 

     // Allocate the transform matrix array. 
     Matrix[] boneTransforms = new Matrix[ model.Bones.Count ]; 

     // Look up combined bone matrices for the entire model. 
     model.CopyAbsoluteBoneTransformsTo(boneTransforms); 

     // Draw the model. 
     foreach (ModelMesh mesh in model.Meshes) 
     { 
      foreach (BasicEffect effect in mesh.Effects) 
      { 
       effect.World = boneTransforms[ mesh.ParentBone.Index ]; 
       effect.View = view; 
       effect.Projection = projection; 

       //switch (lightMode) 
       //{ 
       // case LightingMode.NoLighting: 
       //  effect.LightingEnabled = false; 
       //  break; 

       // case LightingMode.OneVertexLight: 
       //  effect.EnableDefaultLighting(); 
       //  effect.PreferPerPixelLighting = false; 
       //  effect.DirectionalLight1.Enabled = false; 
       //  effect.DirectionalLight2.Enabled = false; 
       //  break; 

       // case LightingMode.ThreeVertexLights: 
       //effect.EnableDefaultLighting(); 
       //effect.PreferPerPixelLighting = false; 
       //  break; 

       // case LightingMode.ThreePixelLights: 
       //  effect.EnableDefaultLighting(); 
       //  effect.PreferPerPixelLighting = true; 
       //  break; 
       //} 

       effect.SpecularColor = new Vector3(0.8f, 0.8f, 0.6f); 
       effect.SpecularPower = 16; 
       effect.TextureEnabled = true; 
      } 

      mesh.Draw(); 
     } 
    } 
} 
+0

Das funktioniert auch für mich, aber es benutzt nur spriteBatch. Ich vermute, dass mein Problem mit dem Zeichnen von indizierten Primitiven zu tun hat. – tloflin

+0

Ich habe das Beispiel aktualisiert, um ein 3D-Modell zu verwenden, das sollte auch funktionieren. Ich werde sehen, was ich mit einem benutzerdefinierten Vertex-Puffer erstellen kann. – borrillis

+1

Anstatt eine Wand des Codes in eine Antwort zu schlagen, können Sie bitte zu dem wichtigen Teil bearbeiten? –

0

So, nach einigen Tests, es Vollbild wie Makeln verliert den Eckpunktpufferspeicher aussieht. Das heißt, mein Puffer ist noch vorhanden, aber es ist nicht mehr mit dem Gerät verbunden. Als ich diese Linie nahm von meiner Update-Funktion und es zu meiner Funktion Draw, alles hat funktioniert:

graphics.GraphicsDevice.SetVertexBuffer(gb.vb, 0); 

Merkwürdigerweise nicht der Indexpuffer betroffen zu sein scheint.

Ich verstehe immer noch nicht, warum dies der Fall ist, obwohl ich keinen einzigen Verweis auf dieses Phänomen irgendwo finden kann.Ich weiß nicht einmal, ob dies etwas Spezifisches für meine Konfiguration ist, oder ein generelles Problem mit Vertex Buffern und Vollbild oder was? Wenn jemand irgendwelche Hinweise hat, wäre ich dankbar.