Avoid flickering Winforms

3

I am developing a simple game (Snake) in winforms .

I use pictureBox that I use canvas (game board) this canvas is repainted for each execution of a Timer with interval of 100ms.

 g = canvas.CreateGraphics();

    public void dibujar(Graphics g, Color color)
    {
        if (partes.Count() > 0)
        {
            foreach (var act in partes)
            {
                g.FillRectangle(new SolidBrush(color), act.PositionX, act.PositionY, act.ancho, act.ancho);  //Relleno del cuadro y los bordes

            }
        }
        g.FillRectangle(new SolidBrush(color), this.x, this.y, this.ancho, this.ancho);
    }

This is the code that I execute to paint each player, and similar methods for the remaining elements of the game.

Everything works perfectly until there are too many elements and it starts to make some very annoying blinks.

I've tried several " solutions " that I found but none worked .

Edited;

I just realized that the flickers increase when printing the images (only I have implemented this sprite )

    public void FromImageArma(Graphics g, int x, int y)
    {   
        Image imageFile = Image.FromFile("Sprites//cajaMunicion.png");
        Graphics newGraphics = Graphics.FromImage(imageFile);
        // Draw image to screen.
        g.DrawImage(imageFile, new PointF(x, y));
        newGraphics.Dispose();
    }

When I start printing these images (there are usually no more than 10 on the screen), the flicker increases considerably. But even without printing them, if I print many food-type objects, the blinking also begins.

    
asked by Hector Lopez 16.08.2018 в 14:03
source

1 answer

3

After viewing the source code, you have two flicker problems:

The first is that you order the picture box painted white:

        ApiConexion._hub.On("ciclo", (p) =>
        {
            g.Clear(Color.White);
            ...

Even if you have used double buffer in the picturebox, if you order it to be painted white it will be painted white. That is the main flicker.

I have made two changes: The first is to create a custom control that is a double-buffer picturebox. Do not change the property by reflection, this is a bad practice. The correct solution is to use, instead of a picturebox, an instance of the following class:

using System.Windows.Forms;

namespace Snake.Juego.Controles
{
    public class DoubleBufferedPictureBox : PictureBox
    {
        public DoubleBufferedPictureBox() {
            this.DoubleBuffered = true;
            this.SetStyle(ControlStyles.UserPaint | 
              ControlStyles.AllPaintingInWmPaint |
              ControlStyles.ResizeRedraw |
              ControlStyles.ContainerControl |
              ControlStyles.OptimizedDoubleBuffer |
              ControlStyles.SupportsTransparentBackColor, true);
        }
    }
}

I have added this class to a "Controls" folder.

Remember to compile the solution or the toolbox will not detect it.

Next, let's use the double buffer properly.

For this I have to give my picture box final images. If you create a Graphics of the image and order a Clear, the result of that operation is drawn.

So the next thing is to create a new bitmap, create graphics for that bitmap and paint in the bitmap. When we have finished, we replace the image of the picture box with the bitmap:

        ApiConexion._hub.On("ciclo", (p) =>
        {
            Bitmap newImg = new Bitmap(_canvas.Width, _canvas.Height);
            Graphics gNewImg = Graphics.FromImage(newImg);
            gNewImg.Clear(Color.White);
            gNewImg.SmoothingMode = SmoothingMode.AntiAlias;
            gNewImg.InterpolationMode = InterpolationMode.HighQualityBicubic;
            gNewImg.PixelOffsetMode = PixelOffsetMode.HighQuality;

            if (disparos.Count()>0)
            {
                for (int a=0;a<disparos.Count();a++)
                {
                    disparos[a].dibujar(gNewImg);
                    disparos[a].mover();
                }
            }

            if (armasL.Count() > 0)
            {
                for (int d = 0; d < armasL.Count(); d++)
                {
                    draw.getXY(armasL[d].x, armasL[d].y);
                    draw.FromImageArma(gNewImg,armasL[d].x, armasL[d].y); //Imprime el sprite
                }
            }
            for (int it = 0; it < jugadores.Count(); it++)
            {
                ///////////////////////////////////
                if (armasL.Count() > 0)
                {
                    for (int i = 0; i < armasL.Count(); i++)
                    {
                        if (jugadores[it].interseccion(armasL[i]))
                        {
                            armasL.Remove(armasL[i]);
                            jugadores[it].recogerMunicion();
                            if (jugadores[it].idCola == ApiConexion.connection.ConnectionId)
                            {
                                this.Invoke((MethodInvoker)delegate
                                {
                                    refrescarMunicionTask(jugadores[it]);
                                });
                                return;
                            }
                        }
                    }
                }
                ///////////////////////////////////////////


                ParteCola temp = new ParteCola(jugadores[it].x, jugadores[it].y, jugadores[it].color, jugadores[it].idCola);

                lock (jugadores[it])
                {
                    jugadores[it].dibujar(gNewImg, jugadores[it].color);
                    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                    foreach (var act in jugadores)
                    {
                        if (act.partes.Find(x => x.PositionX == temp.PositionX) != null && act.partes.Find(x => x.PositionY == temp.PositionY) != null && act.idCola != jugadores[it].idCola /*&&  filtrar que entre el jug que yo estoy manejando*/ )
                        {
                            ApiConexion._hub.Invoke("pararMov", jugadores[it]);
                        }

                        if (act.x == jugadores[it].x && act.y == jugadores[it].y && act.idCola != jugadores[it].idCola)
                        {
                            ApiConexion._hub.Invoke("pararMov", jugadores[it]);
                            ApiConexion._hub.Invoke("pararMov", act);
                        }
                    }

                    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                    jugadores[it].movimiento();

                    jugadores[it].ChoquePared();

                    for (int i = 0; i < comidas.Count(); i++)
                    {
                        //comidas[i].dibujar(g);
                        draw.getXY(comidas[i].x, comidas[i].y);
                        draw.FromImageManzana(gNewImg, comidas[i].x, comidas[i].y); //Imprime el sprite

                        if (jugadores[it].interseccion(comidas[i]))
                        {
                            if (jugadores[it].partes.Count() > 0)
                            {
                                jugadores[it].meter(jugadores[it].partes.Last().PositionX, jugadores[it].partes.Last().PositionY);
                            }
                            else
                            {
                                jugadores[it].meter(jugadores[it].x, jugadores[it].y);
                            }

                            comidas[i].colocarComidaUno(comidas[i].x, comidas[i].y);
                            comidas.Remove(comidas[i]);
                            jugadores[it].sumarPunto();
                            ApiConexion._hub.Invoke("refrescarPuntuacion");
                        }
                    }
                }
            }
            _canvas.Image = newImg;
        });

This fixes a good part of the flicker. Occasionally I see a flicker when getting food, but I suspect it has nothing to do with the picturebox.

I will pass the source code as soon as I can

    
answered by 17.08.2018 / 11:12
source