我正在创build一个游戏,在同一个窗体上有两个对象,每隔几秒需要重新绘制一次(对于animation)。 我有一个单独的线程。 他们偶尔会产生一个错误,说graphics对象已经被使用了。 该错误出现次数较less,因为我在同一个表单上构build了2个graphics对象:
Graphics sprite1 = this.CreateGraphics(); Graphics sprite2 = this.CreateGraphics();
然后我将这些对象传递给适当的方法。 我很害怕这可能是一个非常糟糕的做法。 任何帮助解决这个问题,将不胜感激。
通常游戏不是多线程的,但有一个游戏循环。 在每次迭代中,所有对象的位置根据当前时间重新计算。 然后他们重绘。
您可能会考虑以多线程方式进行计算; 然而,所有的绘图应该出现在游戏循环内部的主线程(所谓的UI线程)中。
请参阅维基百科游戏编程中的 “游戏结构”一章。
是的,你有一个问题。 你看到的实际错误是来自GDI +内部的代码,确保相同的Graphics对象不能在多个线程中使用。 然而,这是一个更深层的问题,你正在创建一个直接引用窗口的Graphics对象。 底层的Windows对象是一个HDC,一个设备上下文的句柄。 设备上下文不是线程安全的。 不幸的是偶尔的绘画工件,僵局和访问违规。
您可以创建一个由FromImage()图像创建的Graphics对象,只要它们是从不同的位图创建的,或者确保两个线程不会同时尝试使用同一个Graphics对象。 这在游戏编程中不会非常有用。
你可以通过创建一个32bppPArgb像素格式的位图来制作精灵。 在大多数机器上,它们比其他格式快十倍。 就像你可以使用普通的GDI +一样,下一步就是使用一个图形库,它使用驻留在视频内存中的位图,并使用GPU硬件。 这种库的流行托管包装是XNA,SlimDX和SharpDX。
如上所述, this.CreateGraphics()
实际上是创建一个对同一个图形对象的引用,这是整个形式本身。 除了线程问题之外,通过确保绘图不会超出预定义的绘图区域来表示不同的屏幕,您可以在代码中(理论上)处理此问题。 这将涉及到一些硬编码,是不是我的风格和偏好。
恕我直言,一个更好的方法是在内存中创建两个图像对象…
Image screen1 = new Bitmap(100, 100); // (width, height) Image screen2 = new Bitmap(100, 100); // (width, height)
然后,您可以分别在每个图像表面上绘制,并使用像图像查看器一样简单的方法来保存每个“视图”…
private void DrawGame() { DrawSprite1(Graphics.FromImage(screen1)); DrawSprite2(Graphics.FromImage(screen2)); } public void DrawSprite1(Graphics g) { g.FillEllipse(Pens.Blue, screen1.GetBounds()); } public void DrawSprite2(Graphics g) { g.FillRect(Brushes.Red, screen2.GetBounds()); }
显然,这不是解决这个问题的唯一方法,而应该是一个用来激励你自己的解决方案的指南。 🙂