对于我游戏中的菜单,我将它们绘制到屏幕一次,然后只有在被认为是脏的时才重绘。每当用户执行应该导致重绘的操作时,都会通过布尔值设置为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,但这似乎没有任何影响,除非我错误地应用它。
答案 0 :(得分:13)
以下是对XNA游戏中调用内容的概述:
Update
BeginDraw
Draw
EndDraw
EndDraw
的默认实施最终会调用GraphicsDevice.Present
。打开双缓冲后,可以交换前后缓冲区。
所以发生的事情是:
第一次:您将场景绘制到后台缓冲区,然后让XNA将其交换到前面。
第二次:你没有画任何东西,让表面充满了DirectX初始化这些表面的紫色,并将那个交换到前面!
< / LI>后续时间:你什么都没画,所以你会看到这两个表面之间的显示闪烁。
有几种方法可以抑制XNA中的绘图。我在this answer to a similar question中查看了它们。在答案中,我建议你覆盖BeginDraw,如下所示:
protected override bool BeginDraw()
{
if(_isDirty)
return base.BeginDraw();
else
return false;
}
当BeginDraw
返回false时,Draw
和EndDraw
(以及Present
)将不会被调用该帧。什么都不会画,前/后缓冲区不会交换。
答案 1 :(得分: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中的行为
答案 2 :(得分:0)
在我的特定情况下,我有一个状态机,可以在负责绘图的状态之间切换。因此,当我在两个游戏状态之间转换时,没有任何东西可以绘制,并且屏幕闪烁紫色直到下一个游戏状态处于活动状态。
我要解决的问题很简单,在Update中,如果我没有状态,请在游戏对象中调用 SuppressDraw()方法。这可以防止在下一次更新发生之前进行绘制。
我想除了你设置一个isDirty标志外,没有什么可以阻止你总是在Update中调用它。但是你必须准备好处理屏幕可能被破坏的其他事件/案例。