ToggleFullScreen в XNA вызывает потерю устройства как ошибки - PullRequest
3 голосов
/ 17 января 2011

Я пытаюсь написать свою первую игру для XNA, и я хотел бы включить функцию ALT + ENTER для переключения между полноэкранным и оконным режимами. Моя игра сейчас очень проста, ничего особенного. Я могу заставить его работать либо в оконном (по умолчанию), либо в полноэкранном режиме (вызывая ToggleFullScreen) в моей функции LoadContent, и все отлично работает.

Однако, когда я вызываю ToggleFullScreen в своей функции обновления, используя точно такой же код, моя игра заканчивается неудачей. Если я запускаю окно и переключаюсь в полноэкранный режим, кажется, что он зависает, отображая только один кадр и не принимая ввод с клавиатуры (мне нужно CTRL + ALT + DEL). Если я запускаю полноэкранный режим и переключаюсь в оконный режим, он вызывает ошибку DrawIndexedPrimitive с сообщением «Текущее объявление вершины не включает в себя все элементы, необходимые текущему вершинному шейдеру. Normal0 отсутствует». (что не соответствует действительности; мой буфер вершин - VertexPositionNormalTexture и содержит данные, а состояние GraphicsDevice - Normal).

Обе эти проблемы, похоже, указывают на потерянное соединение с устройством, но я не могу понять, почему. Каждый ресурс, который я нашел в Интернете, говорит, что переключение в полноэкранный режим так же просто, как вызов функции ToggleFullScreen, без упоминания о сбросе устройств или буферов. Есть идеи?

Редактировать: Вот некоторый код с пропущенными посторонними вещами:

    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);
        }
    }

Ответы [ 4 ]

4 голосов
/ 18 января 2011

Класс XNA Game и связанный с ним GraphicsDeviceManager построены вокруг концепции, согласно которой функция Update предназначена для обновления состояния вашей игры, а функция Draw - для отображения этого состояния.Когда вы устанавливаете состояние графического устройства во время функции обновления, вы нарушаете это предположение.

При переключении в полноэкранный режим устройство теряется.За кулисами в Framework происходит много магии, чтобы скрыть сложность этого от вас.Ресурсы, связанные с устройством, должны быть воссозданы и т. Д. Фреймворк предполагает, что вы не будете снова выполнять какие-либо операции рендеринга до следующей функции Draw, и поэтому он не гарантирует согласованное состояние до тех пор, пока это не произойдет.Итак ... Не устанавливайте свои буферы на графическом устройстве во время функции обновления.Сделайте это в начале функции рисования.

Я не уверен, что это где-то явно задокументировано.Это больше подразумевается в документации по методу.Документация Game.Draw гласит:

Вызывается, когда игра определяет, что пришло время нарисовать рамку.Переопределите этот метод с помощью специфичного для игры кода рендеринга.

и метод обновления сообщает:

Вызывается, когда игра определила, что игровая логика должна быть обработана.Это может включать управление состоянием игры, обработку пользовательского ввода или обновление данных моделирования.Переопределите этот метод с помощью логики, специфичной для игры.

3 голосов
/ 17 января 2011

Согласно одному ответу на форумах App Hub, вы должны убедиться, что вы перестроили матрицу проекций.

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

по этой ссылке ....

Также будьте осторожны с разными пропорции при переключении полного экран. Если вы находитесь в 3D, вам понадобится восстановить вашу матрицу проекции. Если вы в 2D, вам, вероятно, нужно передать масштабную матрицу вашему Функции SpriteBatch.Begin. это статья содержит информацию о выполнении это в 2D.

Таким образом, вы захотите убедиться, что вы делаете это (к сожалению, статья, на которую публикуются ссылки, была на Ziggyware и на этом сайте уже давно нет, поэтому, чтобы найти 2D-пример, который они связали с вами, возможно, придется используйте какой-нибудь архивный сайт, например WaybackMachine, если вам интересно посмотреть на него, так как вы делаете 3D, я подумал, что он может быть неинтересен).

2 голосов
/ 17 января 2011

Мне удалось переключить полноэкранный / оконный режим с помощью следующего класса:

/// <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 голосов
/ 18 января 2011

Итак, после некоторого тестирования похоже, что переключение в полноэкранный режим приводит к потере буфера вершин.То есть буфер my все еще существует, но он больше не подключен к устройству.Когда я взял эту строку из функции Update и добавил ее в функцию Draw, все заработало:

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

Как ни странно, буфер индекса, похоже, не был затронут.

Я все ещеЯ не понимаю, почему это так, учитывая, что я нигде не могу найти единственную ссылку на это явление.Я даже не знаю, является ли это чем-то конкретным для моей конфигурации, или общей проблемой с буферами вершин и полноэкранным режимом, или чем-то еще.Если у кого-то есть какие-либо подсказки, я был бы благодарен.

...