C#-WinForms ||多个图形实例用于旋转

本文关键字:用于 旋转 实例 C#-WinForms 图形 | 更新日期: 2023-09-27 18:24:38

我目前正在进行一个学校项目,该项目基本上是一个涉及GDI Winforms动画的游戏。我选择了我的游戏,就像越狱一样,你试图逃离监狱,而拿着手电筒的警卫在房间里走来走去。

因此,我有Guard类,该类代表警卫,并有路径可供行走。此外,警卫可以90度旋转。角度。(动画化到一定程度)当我旋转防护装置时,我实际上旋转了通过Form_Paint事件的Graphics对象:

    void Game_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        ply.Draw(e.Graphics); // Draws the player
        grd.Draw(e.Graphics); // Draws the guard
        e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
    }

当我只和一个后卫打交道的时候,效果很好。很顺利,做了他应该做的事。当我试图增加1名后卫时,他们开始抓狂。很快,我发现这是因为我派了两个警卫来绘制图形的同一个实例。这也意味着,他们两个都在旋转它。假设一个旋转-90,另一个也会,但他的角度类成员变量不会是-90,那么所有的地狱都会散开。

图形旋转方法(位于防护类内部):

 public Graphics RotateGuard(Graphics g, Point pivot, float angle) // returns the rotated Graphics object
    {
        if (!float.IsNaN(angle))
        {
            using (Matrix m = new Matrix())
            {
                m.RotateAt(angle, pivot);
                g.Transform = m;
            }
        }
        return g;
    }

我做的下一件事是给他们每人this.CreateGraphics()

void Game_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        ply.Draw(e.Graphics); // Draws the player
        foreach (Guard grd in this.guards )
        {
            grd.Draw(this.CreateGraphics()); // Draws the guard
            e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
        }        
    }

然后一切都很好。唯一的问题是,对于GPU来说,它似乎真的很重。它比他应该画的少了大约每5帧就画一次警卫。

我在谷歌上搜索了一下,但除了人们说"没有必要克隆Graphics对象"之外,什么都找不到,但我仍然想不出任何更好的WORKING解决方案。

我怎样才能很好地解决这个问题?提前感谢。

C#-WinForms ||多个图形实例用于旋转

CreateGraphics()是个非常糟糕的主意。您应该对PaintEventArgs传递的Graphics进行所有绘制。所以您的初始代码很好。但是,您需要做的是确保在其Draw方法中接收Graphics的每个对象在完成其工作后保持不变。这可以通过使用Graphics.Save和Graphics.Restore来实现

class Guard
{
    public void Draw(Graphics g)
    {
        var state = g.Save();
        try
        {
            // The actual drawing code
        }
        finally
        {
            g.Restore(state);
        }
    }
}

一种有效的方法是维护一个整体变换,并为您想要绘制的每一个东西都有一个矩阵。绘制之前,请将当前变换与对象的变换相乘。然后,在绘制下一个之前重置变换。

void Game_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
    var g = e.Graphics;
    g.ResetTransform();
    g.MultiplyTransform (playerMatrix);
    ply.Draw(e.Graphics); // Draws the player
    g.ResetTransform();
    foreach (Guard grd in this.guards )
    {
        g.MultiplyTransform (grd.Matrix);
        grd.Draw(this.CreateGraphics()); // Draws the guard
        e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
        g.ResetTransform();
    }        
}

这样的概念类似于在诸如Direct3D的3D图形中如何进行事情;XNA;OpenGL和Unity3D。

使用旋转的Graphics工具对象绘制后,只需调用e.Graphics.ResetTransform()

此外,如果您已经进行了一些要返回的设置,您可能需要查看Graphics.Save()Graphics.Restore()。它可以保存一些状态,并在完成后将其恢复。非常好,至少如果你记着自己在做什么的话。

当然,可以通过以相反的顺序执行反向调用来撤消平移/旋转,但其他方法更简单,只适合您的情况。

请注意,Graphics不包含任何图形,它是一个工具,用于将绘制到关联的Bitmap或控件的表面上。。

最后:Never,Never使用CreateGraphics!!!它的结果是非持久性,这是您很少想要的。。

我弄清楚了它是什么,然后我使用了@Ivan所做的。

 void Game_Paint(object sender, PaintEventArgs e)
    {
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        var saved = e.Graphics.Save();
        ply.Draw(e.Graphics); // Draws the player
        foreach (Guard grd in this.guards )
        {
            grd.Draw(e.Graphics); // Draws the guard
            e.Graphics.Restore(saved);
            e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
        }

    }

我所要做的就是不使用this.DoubleBuffered,而是使用SetStyle(ControlStyles.OptimizedDoubleBuffer, true);,起初我认为两者都一样,但显然不是。然后,我保存了当前的图形状态并重新绘制。

非常感谢大家!