views:

7890

answers:

7

I need to knock out a quick animation in C#/Windows Forms for a Halloween display. Just some 2D shapes moving about on a solid background. Since this is just a quick one-off project I really don't want to install and learn an entire new set of tools for this. (DirectX dev kits, Silverlight, Flash, etc..) I also have to install this on multiple computers so anything beyond the basic .Net framework (2.0) would be a pain in the arse.

For tools I've got VS2k8, 25 years of development experience, a wheelbarrow, holocaust cloak, and about 2 days to knock this out. I haven't done animation since using assembler on my Atari 130XE (hooray for page flipping and player/missile graphics!)

Advice? Here's some of the things I'd like to know:

  • I can draw on any empty widget (like a panel) by fiddling with it's OnPaint handler, right? That's how I'd draw a custom widget. Is there a better technique than this?
  • Is there a page-flipping technique for this kind of thing in Windows Forms? I'm not looking for a high frame rate, just as little flicker/drawing as necessary.

Thanks.

Post Mortem Edit ... "a couple of coding days later"

Well, the project is done. The links below came in handy although a couple of them were 404. (I wish SO would allow more than one reply to be marked "correct"). The biggest problem I had to overcome was flickering, and a persistent bug when I tried to draw on the form directly.

  • Using the OnPaint event for the Form: bad idea. I never got that to work; lots of mysterious errors (stack overflows, or ArgumentNullExceptions). I wound up using a panel sized to fill the form and that worked fine.
  • Using the OnPaint method is slow anyway. Somewhere online I read that building the PaintEventArgs was slow, and they weren't kidding. Lots of flickering went away when I abandoned this. Skip the OnPaint/Invalidate() and just paint it yourself.
  • Setting all of the "double buffering" options on the form still left some flicker that had to be fixed. (And I found conflicting docs that said "set them on the control" and "set them on the form". Well controls don't have a .SetStyle() method.) I haven't tested without them, so they might be doing something (this is the form):

        this.SetStyle(ControlStyles.UserPaint, true);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    

So the workhorse of the code wound up looking like (pf is the panel control):

    void PaintPlayField()
    {
        Bitmap bufl = new Bitmap(pf.Width, pf.Height);
        using (Graphics g = Graphics.FromImage(bufl))
        {
            g.FillRectangle(Brushes.Black, new Rectangle(0, 0, pf.Width, pf.Height));
            DrawItems(g);
            DrawMoreItems(g);
            pf.CreateGraphics().DrawImageUnscaled(bufl, 0, 0);
        }
    }

And I just called PaintPlayField from the inside of my Timer loop. No flicker at all.

+6  A: 

Set off a timer at your desired frame rate. At each timer firing twiddle the internal representation of the shapes on the screen (your model) per the animation motion you want to achieve, then call Invalidate(true). Inside the OnPaint just draw the model on the screen.

Oh yeah, and you probably want to turn Double Buffering on (this is like automatic page flipping).

Nick
+2  A: 

2d Game Primer

Timer Based Animation

Both of these give good examples of animation. The code is fairly straightforward. i used these when I needed to do a quick animation for my son.

StubbornMule
A: 

see the related question simple animation in winforms.

tdyen
A: 

this code is quite useful , but explanation is inadequate. in first piece of code, "this" is not specified. in second piece of code, "pf" is not specified either.

need some time to figure out what they are corresponding to.

+1  A: 

i could kiss you

Taffy
I'll take a simple upvote on the question instead. :)
clintp
A: 

what do you use in you DrawItems method?

paul
Since I have the the `Graphics` object (`g`) I can draw just about anything. Since this is expository code, I didn't think you'd want to see me placing text, drawing more polygons, etc.. and just implied that other stuff was being drawn similar to the `FillRectangle()` above. The important thing was to show the setup and flow of the drawing.
clintp
A: 

thanks clintp,

sorry if this seems a simple question, but was the purpose to do the drawing on a control instead of the form?

I've done a few programs just drawing on the form using onpaint() itself and sometimes get quite a lot of flicker.

Drawing on the control like you've done here and then invalidating the form, seems to create a whole lot less flicker, though there is still a small amount.

I'm using bitmaps rather than graphics shapes though, so not sure if that has something to do with it.

paul
You know, paul, leaving comments would be more appropriate than posting "answers" to the question which are just commentary.......The purpose in starting to draw on the form (and then abandoning it) is because it seemed like a simpler model. It's a given that in any program the fewer the widgets, the fewer lines of code, the simpler (and possibly faster) it will be. So start with the simple model.
clintp