2015-12-14 13 views
6

OK, quindi ho svolto alcune ricerche su questo argomento e la maggior parte delle soluzioni che ho trovato sostengono di risolvere il problema, ma sto scoprendo che non stanno funzionando correttamente. Sono nelle prime fasi dell'implementazione di un semplice piccolo motore di particelle, niente di pazzesco lo sto solo facendo per noia. Non ho mai fatto nulla del genere con WinForms, ho certamente con C/C++ ma questa è una novità per me. Quello che segue è il codice che sto usando per disegnare le particelle sullo schermo, il codice della piastra della caldaia per le particelle non è rilevante in quanto funziona bene, sono più curioso del mio ciclo di gioco reale.Flicker nel programma C# WinForms sullo schermo chiaro

Ecco il codice principale per gli aggiornamenti e ridisegna

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

L'edizione che sto funzionando in è che ogni volta che lo schermo è sempre cancellata e aggiornato, si ottiene questo sfarfallio terribile, e non solo, ma a volte non lo farò ogni volta che emetto una particella.

Il modulo è fondamentalmente solo utilizzando le etichette come posizioni invisibili sullo schermo per dire dove visualizzare ogni particella.

In ogni caso, ho già visto questo argomento ma nulla ha risolto nulla, l'implementazione corrente è il minimo sfarfallio/rallentamento ma non risolve il problema.

Qualsiasi aiuto è apprezzato, grazie!

EDIT * Mi sono reso conto che non stavo mai spostando l'oggetto grafico ogni ciclo, così l'ho fatto e non c'è più ritardo ogni volta che faccio clic sul pulsante dell'emettitore, tuttavia il flicker è ancora lì, ho aggiornato il codice di conseguenza.

+0

'Invalidate();' farà pulire l'intera area di controllo, quindi lo sfarfallio. Normalmente si dovrebbe disegnare su un buffer fuori schermo, ma dal momento che si stanno semplicemente disegnando punti, è possibile ricordare le posizioni dei punti precedenti e cancellarli prima di disegnare la nuova posizione. Funziona su un punto. Come ho detto, il buffer esterno è preferito. La velocità della tua animazione fluttuerà male in base a 'Application.Idle'. Si consiglia di prendere in considerazione l'utilizzo di un timer. – MickyD

+0

Interessante, le altre cose che ho letto dicono che il timer non è affidabile, anche se stavo pensando la stessa cosa, potrei dover provare. –

+0

Il 'System.Windows.Forms.Timer' va bene per questo genere di cose, in particolare se si desidera target dire 30 FPS.Il 'Timer' è abbastanza veloce per gestirlo (non stiamo cercando un timer di super precisione). Il problema di averlo seduto su applicazione inattiva invece di un timer, è che se si sposta il mouse, Windows ridurrà effettivamente la quantità di messaggi inattivi di app che io e te probabilmente abbiamo visto nei nostri giorni MFC C++ e seguiamo in C# :) – MickyD

risposta

5

Per liberarsi degli artefatti di pittura visibili è necessario il doppio buffering. In altre parole, trasforma la scena in un buffer posteriore che, quando è pronto, viene rapidamente applicato alla superficie dello schermo in un unico passaggio. Questa è una funzionalità incorporata in Winforms, è sufficiente impostare la proprietà DoubleBuffered su true nel costruttore del modulo. L'utente deve utilizzare per sfruttare l'evento Paint. Sostituire OnPaint() e chiamare RenderParticles (e.Graphics).

È necessario occuparsi dei tempi, in questo momento il thread dell'interfaccia utente sta bruciando il 100% di core e la velocità di animazione dipende completamente dal numero di particelle e dalla velocità della macchina. Invece di Application.Idle, rilascia un timer dalla casella degli strumenti sul modulo. Nel gestore dell'evento Tick, chiama UpdateParticles() e this.Invalidate() per far sparare nuovamente l'evento Paint. Il valore della proprietà Intervallo del timer è fondamentale, si ottiene il tasso di aggiornamento più riproducibile selezionando 15 o 31 msec (64 o 32 FPS).

Non si ottiene sempre la frequenza FPS desiderata, il timer semplicemente ritarderà o salterà un evento Tick se la macchina è occupata o è troppo lenta o se è necessario eseguire un altro codice sul thread dell'interfaccia utente. Per assicurarsi che ciò non influisca sull'animazione, è necessario misurare il tempo trascorso effettivo invece di spostare le particelle di una quantità fissa. Sia Environment.TickCount, DateTime.UtcNow o Cronometro sono modi adatti per misurare il vero tempo trascorso.