我的游戏窗口允许手动调整大小,这意味着它可以像任何其他普通窗口一样通过拖动其边缘来调整大小。游戏还使用了RenderTarget2D rt2d
,主渲染目标在主Draw方法中设置为GraphicsDevice.SetRenderTarget(rt2d)
,但它会重置回null
(默认渲染目标)在主Draw方法的最后,这让它有点混乱:这真的是问题的根源,在渲染目标设置为rt2d
之间调整游戏窗口的大小,并且不会重置为默认?现在它看起来像。
主Draw方法中的代码应始终将主渲染目标重置为null
,因此没有预期的情况,通常不会发生这种情况。
然而,调整游戏窗口大小的结果有时会导致GraphicsDevice.isDisposed
返回true
,然后游戏会在第一个System.ObjectDisposedException
处抛出SpriteBatch.End()
。我发现有关这个错误的帖子可以追溯到XNA的第一天,但没有一个很好的解释(也没有提到更改渲染目标,所以它可能也是这些海报问题的根源)。
现在我可以通过多次调用此方法来触发此错误:
graphics.PreferredBackBufferWidth = graphics.PreferredBackBufferWidth;
graphics.PreferredBackBufferHeight = graphics.PreferredBackBufferHeight;
graphics.ApplyChanges();
...主Draw方法中有以下几行:
RenderTarget2D rt2d = new RenderTarget2D(GraphicsDevice,
graphics.PreferredBackBufferWidth,
graphics.PreferredBackBufferHeight);
GraphicsDevice.SetRenderTarget(rt2d);
sb.Begin();
// main draw method here, it's pretty big, so it might be taking long
// enough to process to actually resize before resetting render target
sb.End();
GraphicsDevice.SetRenderTarget(null);
sb.Begin();
// draw the whole rt2d to the screen
sb.End();
我的猜测是,如果在重置渲染目标之前调整大小,我应该中止帧绘制并重置渲染目标,但我仍然不确定这究竟是导致这种情况的原因。
UPD :有Window.ClientSizeChanged
和graphics.PreparingDeviceSettings
个事件,但即使它们触发,默认渲染目标似乎也无济于事。
我猜这不是“调整客户区域和应用新图形设置之间的超时”等等。这很可能是由非默认渲染目标引起的。
并且可能不是渲染目标大小与新屏幕大小不同,因为当将图形设备尺寸更改为完全相同的值时,这也会引发异常。
UPD2 :我刚尝试全屏切换待处理操作,将 F11 设置为isFullscreenTogglePending
为 true 并检查在主Update
方法的开头,它根本没有帮助。然后我发现以前的全屏模式也是从主Update方法中切换出来的,只是在开始时没有,而是在输入更新方法的中途,所以在主Update
方法中的位置并不重要它运行,它仍然会导致此错误。有趣的是,抛出异常时GraphicsDevice.isDisposed
为 false 。
这是异常消息:
System.ObjectDisposedException occurred
Message=Cannot access a disposed object.
Object name: 'GraphicsDevice'.
Source=Microsoft.Xna.Framework
ObjectName=GraphicsDevice
StackTrace:
at Microsoft.Xna.Framework.Helpers.CheckDisposed(Object obj, IntPtr pComPtr)
at Microsoft.Xna.Framework.Graphics.BlendState.Apply(GraphicsDevice device)
at Microsoft.Xna.Framework.Graphics.GraphicsDevice.set_BlendState(BlendState value)
at Microsoft.Xna.Framework.Graphics.SpriteBatch.SetRenderState()
at Microsoft.Xna.Framework.Graphics.SpriteBatch.End()
at secret_project.Game1.Draw(GameTime gameTime) in P:\msvs projects\secret_project\Game1.cs:line 3310
InnerException:
它位于主要的Draw调用中的spriteBatch.End()
。
如何防止此错误?
可能相关的问题:
答案 0 :(得分:1)
两件事: 1。 我不熟悉渲染目标......但也许这有用吗?来自MSDN:
“渲染目标代表显示内存的线性区域,通常位于显卡的显示内存中。因此,必须在重置设备时重新创建RenderTarget对象。”
2。 除此之外,我有一个类似的问题。我在绘制调用结束时处理了一个纹理。除非我试图移动窗户,否则这样可以正常工作。每隔一段时间,当我试图移动游戏窗口时,会发生ObjectDisposed异常(对于纹理)。我对推理的最好猜测是更新线程和绘制线程会错位,如果只是短时间,并且纹理会在它有机会重置之前再次被调用。我从来没有找到一种方法来阻止这种效果,除了确保在尝试绘制之前没有丢弃对象。
当然,我们的情况可能完全不相关,但作为一种可能的解决方案,只需添加一个标志,在最近重新调整窗口大小时停止任何绘制调用。
如果这不能解决问题,希望它能帮助缩小问题的范围。
答案 1 :(得分:1)
您不应该像使用RenderTarget2D
那样在Draw调用中创建任何图形资源。首先,创建此类资源的速度很慢,只应对GraphicsDevice
进行一次。只有Set调用应该在Draw
方法内部,因为设置已经创建的资源要快得多,因为它们已经在图形设备内存中。
您应该做什么 - 将所有图形资源创建(包括RenderTarget2D
)移到LoadContent
调用中,并仅在Draw
内保留Set方法。每当重新创建LoadContent
时(例如,当您调整视口大小时),都会调用GraphicsDevice
方法。因此,所有以前创建的资源也将被重新创建。
答案 2 :(得分:1)
我认为您的异常是因为您在绘制方法处于活动状态时重新创建图形设备。您只应在游戏运行后更改更新方法中的设备设置。如果要更改分辨率,请将某个变量(如bool)设置为true,在更新方法中检查该值并在那里应用新分辨率。
public class Game1 : Microsoft.Xna.Framework.Game
{
protected override void Update(GameTime gameTime)
{
if(resolutionChanged)
{
graphics.PreferredBackBufferHeight = userRequestedHeight;
graphics.PreferredBackBufferWidth = userRequestedWidth;
graphics.ApplyChanges();
}
// ...
}
// ...
}
我也对OpenMinded进行了委托:永远不要在每帧的基础上创建资源。使用GraphicsDevicerManagers PreparingDeviceSettings事件。重置或重新创建图形设备时会触发它。