在 C# 中同时使用图形对象时出错

本文关键字:图形 对象 出错 | 更新日期: 2023-09-27 18:32:38

我正在创建一个游戏,其中包含同一表单上的两个对象,需要每隔几秒钟单独重绘一次(用于动画(。 我在单独的线程上有一个。 它们时不时地产生一个错误,指出图形对象已在使用中。 该错误出现的频率较低,因为我在同一表单上构造了 2 个图形对象:

Graphics sprite1 = this.CreateGraphics();
Graphics sprite2 = this.CreateGraphics();

然后,我将这些对象传递给适当的方法。我担心这可能是一个非常糟糕的做法。任何帮助解决此问题将不胜感激。

在 C# 中同时使用图形对象时出错

是的,你有问题。 您看到的实际错误来自 GDI+ 中的代码,该代码确保同一图形对象不能在多个线程中使用。 但是,这是一个更深层次的问题,您正在创建一个直接引用窗口的图形对象。 基础 Windows 对象是 HDC,即设备上下文的句柄。 设备上下文不是线程安全的。 事故是偶尔的绘画伪影、死锁和访问违规。

您可以使由 FromImage(( 图像创建的图形对象正常工作,只要它们是从不同的位图创建的,或者您确保两个线程不会尝试同时使用相同的图形对象。 这在游戏编程中不会非常有用。

您可以通过创建具有 32bppPArgb 像素格式的位图来制作快速绘制的精灵。 它们绘制速度比大多数机器上的任何其他格式快十倍。 这是你可以用普通GDI+推动它的程度,下一步是使用图形库,该库使用驻留在视频内存中的位图并使用GPU硬件。 此类库的流行托管包装器是XNA,SlimDX和SharpDX。

通常游戏不是多线程的,而是有一个游戏循环。在每次迭代中,都会根据当前时间重新计算所有对象的位置。然后重新绘制它们。

您可以考虑以多线程方式进行计算;但是,所有绘图都应发生在游戏循环内的主线程(所谓的UI线程(中。


请参阅维基百科上游戏编程中的"游戏结构"一章。

如上所述,this.CreateGraphics()实际上是创建对同一图形对象的引用,即整个表单本身。 撇开线程问题不谈,通过确保绘图不会超出预定义的绘图区域来表示不同的屏幕,"理论上"可以在代码中处理此问题。 这将涉及一些硬编码,不是我的风格或偏好。

恕我直言,更好的方法是在内存中创建两个 Image 对象......

    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());
    }

显然,这不是解决这个问题的唯一方法,但应该用作激励您自己解决问题的指南。 :)