低帧率在一夫一妻制

本文关键字:一夫一妻制 帧率 | 更新日期: 2023-09-27 18:14:29

我正在用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;
}

主Draw例程:

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

取球例程:

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

低帧率在一夫一妻制

我测试了你的代码,当提供纹理时它不会延迟。我不得不对你的代码做一些修改,以使它工作,因为你省略了部分。我想遗漏的部分可能是原因,但不太可能。我不知道你在什么机器上测试这个,但是,为了解决你在本地遇到的问题,我将提供一些建议。

在编写高性能代码时,忘记你所知道的关于"面向对象行为"的一切,考虑数据。面向数据的设计就是将属于一起的数据放入大的块中并同时处理它们。这个快多了。在游戏设计的许多情况下,只要有一个,就会有很多。利用这一点,传递整个数组,并在现场直接对其进行操作。

如果可能的话,避免嵌套异常和迭代循环。异常发生时是昂贵的,它们应该只在非常异常的情况下使用,或者如果非常不寻常的情况确实发生了,并且您希望通过抛出异常来处理这种"边缘"情况,以迫使代码的消费者处理它。
 for (int x = 0; x < 8; x++)
    {
        for (int y = 0; y < 6; y++)
        {
            try
            {
                gameGrid[x, y].pipe.Draw(spriteBatch);
            }
            catch (NullReferenceException)
            {
                continue;
            }
        }
    }

捕捉嵌套在两个for循环中的Null引用异常可能是一个坏主意。如果您需要从其中一个抛出,并且允许您绘制下一个,请考虑如果您希望保持代码原样,为什么需要抛出。如果它是用来捕捉gameGrid或pipe中的空项,那么构造函数应该总是将项目置于有效状态,并且列表应该始终是"完整的"。如果一个元素不再存在,它就不应该再出现在列表中。否则,如果一个失败意味着所有失败,则将try块移到外面。这是比较常见的。

分析您的应用程序是一种机制,可以帮助您找到比您预期的慢的地方,有时甚至是为什么。这里有一个关于如何在visual studio中做到这一点的参考。性能分析入门指南以及演练:分析应用程序。

也就是说,这些都不会使您的应用程序减慢到您所描述的那种程度。因此,我建议您编辑您的问题,并包括可能是原因的代码的其他相关部分。我在下面附上了一个从你的小例子中构造的样本,其中有一些小的修改,以便以更极端的方式模拟你的环境。这个例子绘制了100个矩形,每个矩形都是50x50,它们都可以像应用程序一样重新缩放和移动。这些是100个贴图,如果这对你自己来说很慢,你一定要看看上面的profiler主题,如果你正在使用visual studio或尝试从Mono Game的官方网站或最新的显卡驱动程序中获取最新的二进制文件。

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