6

对于我游戏中的菜单,我将它们绘制到屏幕上一次,然后仅在它们被认为是脏的时候才重新绘制。每当用户执行应该导致重绘的操作时,这通过设置为 true 的布尔值来处理,然后绘图循环将在绘制菜单之前检查该值。此逻辑在 3.1 中完美运行,但在 4.0 中,菜单会闪烁(绘制 1 帧)然后显示紫色屏幕,直到再次绘制。

我在 4.0 中创建了一个非常简单的测试游戏来演示如下所示的问题。您会注意到屏幕看起来只是紫色。如果将行设置 _isDirty 删除为 false,您将看到矢车菊蓝色背景。

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    bool _isDirty = true;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
    }
    protected override void Draw(GameTime gameTime)
    {
        if (_isDirty)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            _isDirty = false;
        }
        base.Draw(gameTime);
    }
}

我将如何从 XNA 3.1 中获取行为?我见过几个人提到 PreserveContents,但这似乎在 4.0 中没有任何影响,除非我应用不正确。

4

3 回答 3

13

以下是 XNA 游戏中调用内容的粗略概述:

Update
BeginDraw
Draw
EndDraw

EndDraw最终调用的默认实现GraphicsDevice.Present。打开双缓冲后,这会交换前后缓冲区。

所以正在发生的事情是:

  • 第一次:您将场景绘制到后台缓冲区,然后让 XNA 将其交换到前台。

  • 第二次:你什么也没画,让表面充满了 DirectX 初始化这些表面的紫色,并将其交换前面!

  • 随后的时间:你什么也没画,所以你会看到这两个表面之间的显示闪烁。

有几种方法可以抑制 XNA 中的绘图。我在这个类似问题的答案中回顾了它们。在那个答案中,我建议您覆盖 BeginDraw,如下所示:

protected override bool BeginDraw()
{
    if(_isDirty)
        return base.BeginDraw();
    else
        return false;
}

BeginDraw返回 false,Draw并且EndDraw(等等Present)将不会被称为该帧。什么都不会绘制,并且前/后缓冲区不会交换。

于 2010-11-21T04:24:16.063 回答
2

我最近也偶然发现了这个;我不认为翻转缓冲区有问题。这在 3.1 和 4.0 中应该是相同的。我用 ILSpy 查看了 GraphicsDevice 并发现了这一点:

改变的是,在 4.0 中,如果设置了私有标志,则首先在 GraphicsDevice.Present() 中清除当前渲染目标。默认情况下,最初会设置此标志(渲染目标是干净的)。绘制到渲染目标会清除此标志(渲染目标现在很脏)。完成任务后,GraphicsDevice.Present() 再次设置标志(渲染目标被“刷新”到您的窗口,现在再次被认为是干净的)。

要从 GraphicsDevice.Present() 获取结果,您需要按严格的顺序调用 Draw() 和 EndDraw()。

如果您在没有事先调用 Draw() 的情况下调用 EndDraw(),您将不会获得渲染目标的内容(被清除),而只会获得紫色屏幕。

不幸的是,旗帜是私有的,没有干净的方法可以得到它。您可以像这样使用反射来清除标志,强制渲染目标变脏而不向其中绘制任何内容:

graphicsDevice.GetType().GetField("lazyClearFlags", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(graphicsDevice, 0);

(graphicsDevice 是您当前的 GraphicsDevice 实例)

将上面的行放在调用 EndDraw() 之前,行为会恢复到 3.1 中的行为

于 2011-06-08T18:08:13.243 回答
0

在我的特殊情况下,我有一个状态机,可以在负责绘图的状态之间切换。所以当我在两个游戏状态之间转换时,没有什么可画的,屏幕呈紫色闪烁,直到下一个游戏状态处于活动状态。

我解决它的方法很简单,在更新中,如果我没有状态,请在游戏对象中调用SuppressDraw()方法。这可以防止在下一次更新发生之前进行绘制。

我想没有什么能阻止你总是在 Update 中调用它,除非你设置了 isDirty 标志。但是您必须准备好处理屏幕可能被破坏的其他事件/情况。

于 2011-11-10T04:11:54.263 回答