C#-WinForms ||用于旋转的多个Graphics实例

时间:2015-11-27 08:43:17

标签: c# winforms graphics gdi+ gdi

我目前正在开展一个学校项目,这个项目基本上是一个涉及GDI-Winforms动画的游戏。 我选择了我的游戏,就像在越狱时试图逃离监狱,而带有轻型火把的守卫正在房间里走来走去。

所以,我有 Guard 类代表守卫并且有 A Path 来继续。此外,护罩可以旋转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帧就比他应该画的那样抽出一次守卫。

我用谷歌搜索但找不到任何东西,但人们说“没有必要克隆图形对象”,但是,我仍然无法想到任何更好的工作解决方案。

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

4 个答案:

答案 0 :(得分:0)

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

如果您想要返回一些设置,您可能还需要查看Graphics.Save()Graphics.Restore()。它可以保存很少的状态,完成后再将它们恢复原状。非常好,至少如果你记得你在做什么。

当然,可以通过反向调用反向调用来撤消翻译/旋转,但其他方法更简单,只针对您的情况。

请注意,Graphics并非包含任何图形,它是用于绘制到关联的Bitmap或其中的工具对照的表面......

最后:从不<永远使用CreateGraphics!它的结果是非持久性,你很少想要它......

答案 1 :(得分:0)

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

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

}

这些概念类似于3D图形(如Direct3D)的工作方式; XNA; OpenGL和Unity3D。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

我弄清楚它是什么,然后我用了@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);,起初我认为两者都是一样的,但显然它没有。 然后,我保存了当前的图形状态并重新绘制它。

非常感谢大家!