2

我正在创建一个具有相同形式的两个对象的游戏,需要每隔几秒单独重绘一次(用于动画)。我在一个单独的线程上有一个。他们时不时地产生一个错误,说明图形对象已在使用中。由于我在同一个表单上构造了 2 个图形对象,因此该错误出现的频率较低:

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

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

4

3 回答 3

4

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

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

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

于 2012-04-28T18:00:02.730 回答
3

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

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


请参阅Wikipedia 上的游戏编程中的“游戏结构”一章。

于 2012-04-28T17:11:12.480 回答
1

如上所述,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());
    }

显然,这不是解决这个问题的唯一方法,但应该作为指导来激励你找到自己的解决方案。:)

于 2012-05-17T18:35:15.540 回答