Search code examples
c#winformsgraphicsgdi+gdi

C#-WinForms || Multiple instances of Graphics for rotation


im currently working on a school project which is basically a game that involves GDI-Winforms animations. I chose my game to be something like prison break where you are trying to escape the prison while guards with light-torches are walking around the room.

So, i have the Guard class which represents the guard and has A Path to walk on. Furthermore, the guard can rotate in 90deg. angles. (animated to a certain degree) When i rotate the guard, i actually rotate the Graphics object that was passed through the Form_Paint event:

    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

    }

When i was dealing with only 1 guard, it was working great. Smooth and did the exactly what he was supposed to do. When i tried to add 1 more guard, they started to freak out. Soon enough i figured out that its because im sending both guards to draw the same instance of Graphics. Which also means, both of them are rotating it. Lets say one is rotating by -90, the other will too, but his angle class member variable will not be -90, then all hell breaks loose.

Rotation of graphics method (sits inside the guard class):

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

The next thing i did was to give each of them 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
        }        

    }

Then everything worked just fine. The only thing is, it seemed like it was really heavy for the GPU to process or something. It drew the guard once about every 5 frames less than he supposed to be drawn.

I googled but couldn't find anything but people saying that "There is no need to clone a Graphics object", but still, i can't think of any better WORKING solution.

How can i solve this problem nicely? Thanks in advanced.


Solution

  • CreateGraphics() is a really bad idea. You should do all the drawing to the Graphics passed by PaintEventArgs. So your initial code is just fine. However, what you need to do is to ensure that every object that receives a Graphics in its Draw method leaves it unchanged after doing its job. This can be achieved by using Graphics.Save and Graphics.Restore like this

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