MonoGame中的低帧率

时间:2015-09-24 19:03:39

标签: c# xna monogame

我正在用MonoGame写一个2D益智游戏。我刚刚将第一个移动的精灵添加到程序中,并发现我的速度大约是10fps,而且我还没有得到最简单的想法。我不知道我是否能够提供足够的信息来获得帮助,但我认为值得一试。作为参考,移动物体是一个球。

主要更新例程:

protected override void Update(GameTime gameTime)
{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
        Exit();

    MouseState newState = Mouse.GetState();

    if (newState.LeftButton == ButtonState.Pressed && oldState.LeftButton == ButtonState.Released)
    {
        MouseLeftClicked(newState.X, newState.Y);
    }

    oldState = newState;

    if (gameState == GameState.Playing)
    {
        try
        {
            foreach (Ball ball in ballList)
            {
                ball.Update(gameTime);
            }
        }
        catch(NullReferenceException)
        {
        }
    }
    // TODO: Add your update logic here

    base.Update(gameTime);
}

球更新程序:

public void Update(GameTime gameTime)
{
    this.pos += this.motion * (float)gameTime.ElapsedGameTime.TotalSeconds;
}

主要抽奖例程:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    // TODO: Add your drawing code here
    spriteBatch.Begin();

    for (int x = 0; x < 8; x++)
    {
        for (int y = 0; y < 6; y++)
        {
            if (gameGrid[x, y] != null)
            {
                spriteBatch.Draw(backgroundTile, gameGrid[x, y].rect, Color.White);
            }
        }
    }


    //Draw menu
    if (gameState == GameState.StartMenu)
    {
        spriteBatch.Draw(startButton, orbPosition, Color.White);
    }

    //Draw game while playing
    if (gameState == GameState.Playing)
    {
        for (int x = 0; x < 8; x++)
        {
            for (int y = 0; y < 6; y++)
            {
                try
                {
                    gameGrid[x, y].pipe.Draw(spriteBatch);
                }
                catch (NullReferenceException)
                {
                    continue;
                }
            }
        }
        foreach (Wheel wheel in wheelList)
        {
            wheel.Draw(spriteBatch);
        }

        foreach (Ball ball in ballList)
        {
            ball.Draw(spriteBatch);
        }
    }

    spriteBatch.End();

    base.Draw(gameTime);
}

Ball Draw例程:

public void Draw(SpriteBatch spriteBatch)
{
    int sprite = (int)color;

    Rectangle sourceRect = new Rectangle(sprite * spriteSize, 0, spriteSize, spriteSize);
    Rectangle ballRect = new Rectangle((int)this.pos.X, (int)this.pos.Y, spriteSize * scale, spriteSize * scale);

    //spriteBatch.Begin();

    spriteBatch.Draw(this.texture, ballRect, sourceRect, Color.White);

    //spriteBatch.End();
}

1 个答案:

答案 0 :(得分:3)

我测试了你的代码,它在提供纹理时没有滞后。我不得不对你的代码做一些修改,以便在你省略部分之后使其工作。我想省略的部分可能是负责任的,但不太可能。我不知道您正在测试这种机器的类型,但我会提供一些建议以解决您在本地遇到的问题。

在编写高性能代码时,忘记你所知道的关于面向对象行为的所有内容&#39;并思考数据。面向数据的设计就是将数据一起粘贴到大块中并立即处理它们。这要快得多。在许多情况下,对于游戏设计,当有一个时,有很多。使用它对您有利,并传递整个阵列并直接在现场采取行动。

尽可能避免嵌套异常和迭代循环。异常在发生时很昂贵,它们只应在异常时使用,或者如果非常不寻常情况确实发生,并且您希望处理这个边缘&#39;通过抛出异常来强制代码的消费者来处理它。

 for (int x = 0; x < 8; x++)
    {
        for (int y = 0; y < 6; y++)
        {
            try
            {
                gameGrid[x, y].pipe.Draw(spriteBatch);
            }
            catch (NullReferenceException)
            {
                continue;
            }
        }
    }

捕获Null引用嵌套在两个for循环中的异常可能是一个坏主意。如果你需要从其中一个中抛出,那么你可以绘制下一个,如果你希望保持代码原样,那么为什么抛出是必要的。如果它在游戏网格或管道中捕获空,请考虑构造函数应始终将项目置于有效状态,并且列表应始终“完成”#39;如果元素停止存在,则它不应再在列表中。否则,如果一个失败意味着所有失败都将try块移到外面。这种情况比较常见。

分析您的应用程序是一种机制,可以帮助您找到比预期慢的地方,有时甚至为什么。以下是有关如何在visual studio中执行此操作的参考。 Beginners Guide to Performance Profiling以及Walkthrough: Profiling Applications

也就是说,这些都不会使你的应用程序降低到你所描述的程度。因此,我建议您编辑您的问题,并包括可能是原因的代码的其他相关部分。我在下面附上了一个由您的小例子构建的示例,并进行了一些小的修改,以便以更加极端的方式模拟您的环境。这个例子绘制了100个矩形,每个矩形都是50x50,它们都像你的应用程序一样重新缩放和移动。这些是100个磁贴,如果你自己很慢,你应该查看上面的探查器主题,如果你使用visual studio或尝试从Mono Game's official website或最新的显卡驱动程序获取最新的二进制文件。

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

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    public IList<Ball> Balls { get; private set; }

    /// <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);
        var rand = new Random();
        Balls = new List<Ball>(5);
        for(var iii = 0; iii < 100; ++iii)
            Balls.Add(new Ball(GraphicsDevice, new Vector2(rand.Next(50, 500), rand.Next(50, 500))));
    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
    }

    /// <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)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        foreach (var ball in Balls)
            ball.Update(gameTime);

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

        spriteBatch.Begin();

        foreach (var ball in Balls)
            ball.Draw(spriteBatch);

        spriteBatch.End();

        base.Draw(gameTime);
    }
}

public class Ball
{
    public Texture2D Texture { get; }
    public Vector2 Position { get; private set; }
    public double Scale { get; private set; }

    public Ball(GraphicsDevice gd, Vector2 initialPosition)
    {
        Texture = new Texture2D(gd, 50, 50);
        Position = initialPosition;
        var data = new Color[100*100];
        for (var iii = 0; iii < data.Length; ++iii) data[iii] = Color.YellowGreen;
        Texture.SetData(data);
    }

    private bool _increaseScale, _increaseX, _increaseY;

    public void Update(GameTime gameTime)
    {
        if (Scale < 1)
            _increaseScale = true;
        else if (Scale > 4)
            _increaseScale = false;

        if (Position.X < 50)
            _increaseX = true;
        else if (Position.X > 500)
            _increaseX = false;

        if (Position.Y < 50)
            _increaseY = true;
        else if (Position.Y > 500)
            _increaseY = false;

        Scale += (_increaseScale ? 1.5 : -1.5) * gameTime.ElapsedGameTime.TotalSeconds;
        Position += new Vector2((float)((_increaseX ? 100 : -100)*gameTime.ElapsedGameTime.TotalSeconds), (float)((_increaseY ? 100 : -100)*gameTime.ElapsedGameTime.TotalSeconds));
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        var source = new Rectangle(0, 0, Texture.Height, Texture.Width);
        var dest = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width * (int)Scale, Texture.Height* (int)Scale);
        spriteBatch.Draw(Texture, dest, source, Color.White);
    }
}