我目前正在与GDI +进行游戏,我知道它不是开发游戏的最佳解决方案,但由于这是一个学校项目,我别无选择。
大约每十次我运行游戏时,图形会在屏幕左上角的表单外呈现。
我正在使用双缓冲,如果这有助于缩小问题范围。
渲染代码如下所示:
while (true)
{
// Create buffer if it don't exist already
if (context == null)
{
context = BufferedGraphicsManager.Current;
this.buffer = context.Allocate(CreateGraphics(), this.DisplayRectangle);
}
// Clear the screen with the forms back color
this.buffer.Graphics.Clear(this.BackColor);
// Stuff is written to the buffer here, example of drawing a game object:
this.buffer.Graphics.DrawImage(
image: SpriteSheet,
destRect: new Rectangle(
this.Position.X
this.Position.Y
this.SpriteSheetSource.Width,
this.SpriteSheetSource.Height),
srcX: this.SpriteSheetSource.X,
srcY: this.SpriteSheetSource.Y,
srcWidth: this.SpriteSheetSource.Width,
srcHeight: this.SpriteSheetSource.Height,
srcUnit: GraphicsUnit.Pixel);
// Transfer buffer to display - aka back/front buffer swapping
this.buffer.Render();
}
使用屏幕截图更容易解释:
答案 0 :(得分:5)
Winforms中的一个设计错误是将BufferedGraphicsXxx类公开。它们是Winforms中双缓冲支持的实现细节,它们对于错误使用它们并不是非常有弹性。
您肯定使用从Allocate()返回的BufferedGraphics错误。您可以在游戏循环内以高速率创建缓冲区。但是你忘了丢弃你在循环结束时使用的缓冲区。这将以高速率消耗设备上下文(HDC)。这不会永远持续下去,如果您的程序没有运行垃圾收集器,那么Windows会拔出插件并且不会让您创建新的设备上下文。内部CreateCompatibleDC()调用将失败并返回NULL。 BufferedGraphicsContext类否则会错过代码来检查此错误并使用NULL句柄进行操作。并开始绘制到桌面窗口而不是表单。
一个修复方法是将Allocate()调用移到循环之外,这样你只需执行一次。但是现在当用户更改窗口大小时,您将遇到一个新问题,缓冲区不再是正确的大小。
更好的捕鼠器是不使用BufferedGraphics类,而是将其留给Winforms以使其正确。有几种方法可以在Winforms中获得游戏循环,但最简单的方法是使用OnPaint()方法渲染场景并立即请求另一个颜色,以便不断被一遍又一遍地调用。与此类似:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e) {
RenderScene(e.Graphics);
this.Invalidate();
}
}
RenderScene()应使用传递的Graphics实例绘制游戏对象。请注意,您不再需要使用已经完成的Clear()。
答案 1 :(得分:2)
大约每十次我运行游戏时,图形会被渲染 在屏幕左上角的表格之外。
从屏幕截图和您的描述中,您偶尔会使用Window的桌面设备上下文(DC);获取DC时使用零(IntPtr.Zero)窗口句柄的效果如何。 这让我相信你可以在创建表单窗口之前启动游戏循环,从而导致图形上下文指向零窗口句柄。
正如评论中所证实的那样,你正在为游戏循环使用一个单独的线程,导致这种情况的随机行为。一旦处理线程,在启动和完成线程的时间(特别是线程可以通过多核/ cpu计算机并行运行)时,并不总是两次获得相同的结果。每次运行游戏应用程序时,游戏循环线程都有可能在创建和显示UI线程上的表单窗口之前启动并执行。