1

I'm trying to make a breakout game for an assignment in windows form and I've made games before, but I've just not used winforms before. I've found things that are supposed to help like OnPaint() (which you're supposed to override), DoubleBuffered andInvalidate`. But I'm just struggling applying it to my code

Here's what I have:

 int xSpeed, ySpeed;

    Graphics display;
    Brush brush;

    Rectangle ballRect;

    public Form1()
    {
        InitializeComponent();



        timer1.Enabled = true;
        timer1.Interval = 1;

        xSpeed = 5;
        ySpeed = 5;
        ballRect = new Rectangle(10, 10, 20, 20);

        display = this.CreateGraphics();

        brush = new SolidBrush(Color.Red);


    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        DoubleBuffered = true;

        ballRect.X += xSpeed;
        ballRect.Y += ySpeed;

        if (ballRect.X >= 469)
            xSpeed = -xSpeed;

        if (ballRect.Y >= 457)
            ySpeed = -ySpeed;

        if (ballRect.X <= 0)
            xSpeed = -xSpeed;

        if (ballRect.Y <= 0)
            ySpeed = -ySpeed;

        display.Clear(Color.White);
        display.FillEllipse(brush, ballRect);

    }

I'm drawing the ball in Update method (timer1_tick), but I feel like I shouldn't be. Thanks :)

Dan Scott
  • 41
  • 1
  • 1
  • 7
  • Don't store the Graphic object. Don't use CreateGraphics. Use the control's paint event and use the Graphic object from that. Set the form's DoubleBuffered property to true. – LarsTech Nov 19 '15 at 20:17
  • DoubleBuffered can cause an issue on Windows 10 see: [Redraw issue on Windows10 with DoubleBuffering](https://stackoverflow.com/q/51824224/7713750) – Rekshino Oct 02 '18 at 07:08

2 Answers2

2

You can create your custom Control or Form and:

  • In constructor Set styles for flicker free painting OptimizedDoubleBuffer, UserPaint, AllPaintingInWmPaint
  • In constructor Set style for redraw if the size changes ResizeRedraw
  • In Tick event just update position of ball and then Invalidate the control
  • override OnPaint method of your control and put the paint logic there
  • Also you should use properties for speed or ball size, to be able to invalidate the control if those values changed. (I didn't implement properties in below code.) Also you can increase the interval a little.

For example:

public partial class CustomControl1 : Control
{
    public CustomControl1()
    {
        InitializeComponent();

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

        timer1.Enabled = true;
        timer1.Interval = 1;

        xSpeed = 5;
        ySpeed = 5;

        ballRect = new Rectangle(10, 10, 20, 20);
        brush = new SolidBrush(Color.Red);
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        pe.Graphics.FillEllipse(brush, ballRect);
    }

    int xSpeed, ySpeed;
    Brush brush;
    Rectangle ballRect;
    private void timer1_Tick(object sender, EventArgs e)
    {
        ballRect.X += xSpeed;
        ballRect.Y += ySpeed;

        if (ballRect.X + ballRect.Width >= this.Width)
            xSpeed = -xSpeed;

        if (ballRect.Y + ballRect.Height >= this.Height)
            ySpeed = -ySpeed;

        if (ballRect.X <= 0)
            xSpeed = -xSpeed;

        if (ballRect.Y <= 0)
            ySpeed = -ySpeed;

        this.Invalidate();
    }

}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
0

The flickering is happening because you are drawing directly to the display. First you clear the display, which the user will see for a split second, then you draw a circle over it, which is only displayed for a short time before the process repeats. That "overdrawing" is where the flicker is coming from.

One method to get rid of it is to do all your drawing to a memory bitmap and, once complete, move the entire bitmap to the display.

Once other issue I see with your code is that you create a Graphics instance in the constructor and keep it around for the life of the program. That's a pattern that you should avoid. Instead you should create a new Graphics object, preferably in a using statement for each "frame". That will make sure that everything is getting cleaned up properly.

Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76