Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flicker in C# WinForms program on screen clear

Tags:

c#

winforms

Alright, so I've done some research into this topic and most of the solutions I've found claim to fix the problem but I am finding that they aren't quite working right. I'm in the early stages of implementing just a simple little particle engine, nothing crazy I'm just doing it out of boredom. I have not done anything like this with WinForms before, I have certainly with C/C++ but this is a new thing for me. The following is the code I am using to draw the particles to the screen, the boiler plate code for the particles is not relevant as it works fine, I am more curious about my actual game loop.

Here is the main code for updates and redraws

public MainWindow()
    {
        this.DoubleBuffered = true;
        InitializeComponent();
        Application.Idle += HandleApplicationIdle;
    }

    void HandleApplicationIdle(object sender, EventArgs e)
    {
        Graphics g = CreateGraphics();

        while (IsApplicationIdle())
        {
            UpdateParticles();
            RenderParticles(g);
            g.Dispose();
        }
    }

    //Variables for drawing the particle
    Pen pen = new Pen(Color.Black, 5);
    Brush brush = new SolidBrush(Color.Blue);
    public bool emmiter = false;

    private void EmitterBtn_Click(object sender, EventArgs e)
    {
        //Determine which emitter to use
        if (emmiter == true)
        {
            //Creates a new particle
            Particle particle = new Particle(EmitterOne.Left, EmitterOne.Top, .5f, .5f, 20, 20);
            emmiter = false;
        }
        else if(emmiter == false)
        {
            Particle particle = new Particle(EmitterTwo.Left, EmitterTwo.Top, -.5f, .5f, 20, 20);
            emmiter = true;
        }
    }

    public void RenderParticles(Graphics renderer)
    {


        Invalidate();
        Thread.Sleep(0);

        //Iterate though the static list of particles
        for (int i = 0; i < Particle.activeParticles.Count; i++)
        {


            //Draw Particles
            renderer.DrawRectangle(pen, Particle.activeParticles[i].x,
                                 Particle.activeParticles[i].y,
                                 Particle.activeParticles[i].w,
                                 Particle.activeParticles[i].h);
        }
    }

    public void UpdateParticles()
    {
        for (int i = 0; i < Particle.activeParticles.Count; i++)
        {
            //Move particles
            Particle.activeParticles[i].MoveParticle();
        }
    }

The issue I am running into is that anytime the screen is getting cleared and updated, it gets this awful flickering, and not only that but it sometimes won't whenever I emit a particle.

The form is basically just using labels as invisible locations on the screen to say where to render each particle.

Anyway, I've seen this topic before but nothing has fixed anything, the current implementation is the least flickery/laggy but is not solving the issue.

Any help is appreciated, thanks!

EDIT* I realized I was never deallocating the graphics object each loop so I did that and there is no more delay whenever I click the emitter button, however the flicker is still there, I updated the code accordingly.

like image 458
Trevor Hart Avatar asked Dec 14 '15 05:12

Trevor Hart


People also ask

What is flicker used for?

Flicker is used intentionally by developers on low-end systems to create the illusion of more objects or colors/shades than are actually possible on the system, or as a speedy way of simulating transparency.

What is flicker in computer?

Screen flickering in Windows is usually caused by display drivers. To update your display driver, you'll need to start your PC in safe mode, uninstall your current display adapter, and then check for driver updates. Start your PC in safe mode, then select and hold (or right-click) Start and select Device Manager.


1 Answers

Getting rid of the visible paint artifacts requires double-buffering. In other words, render the scene into a back-buffer that, when ready, gets quickly blitted to the screen surface in a single step. That's a built-in feature in Winforms, simply set the DoubleBuffered property to true in the form constructor. You must use the Paint event to take advantage of that. Override OnPaint() and call RenderParticles(e.Graphics).

You need to take care of timing, right now your UI thread is burning 100% core and animation speed completely depends on the number of particles and the speed of the machine. Instead of Application.Idle, drop a Timer from the toolbox onto your form. In the Tick event handler, call UpdateParticles() and this.Invalidate() to get the Paint event to fire again. The timer's Interval property value is critical, you get the most reproducible update rate by picking 15 or 31 msec (64 or 32 FPS).

You are not always going to get the desired FPS rate, the timer will simply delay or skip a Tick event if the machine gets busy or is too slow or other code on the UI thread needs to run. To make sure that doesn't affect the animation, you must measure actual elapsed time instead of moving the particles by a fixed amount. Either Environment.TickCount, DateTime.UtcNow or Stopwatch are suitable ways to measure true elapsed time.

like image 129
Hans Passant Avatar answered Sep 30 '22 12:09

Hans Passant