Entwickler-Ecke
Multimedia / Grafik - Panel flackert trotz DoubleBuffered
mvollmer - Do 24.02.11 17:37
Titel: Panel flackert trotz DoubleBuffered
Guten Tag,
ich bin grade dabei ein Tetris zu programmieren.
Das spielfeld ist ein Panel. Bekanntlicher weise, muss man ja DoubleBuffered auf true setzten.
Doch das reicht immer noch nicht aus und es flackert immer noch.
Zusätzlich habe ich diese Styles gesetzt
C#-Quelltext
1:
| this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer ,true); |
Hier mal mein Quelltext :
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| public class MyPanel : System.Windows.Forms.Panel { public MyPanel() { this.DoubleBuffered = true; } }
public partial class MainForm : Form { public MainForm() { GameField = new MyPanel(); InitializeComponent(); this.DoubleBuffered = true; this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer ,true); this.UpdateStyles(); timer1.Interval = 156; } |
Die Aktualisierungsrate des Panels beträgt die Interval von timer1
Gibt es eventuell eine weitere Lösung?
Mit freundlichen Grüßen
mvollmer
jaenicke - Do 24.02.11 18:14
Ich vermute, dass du erstens direkt auf das Panel einzeln zeichnest und zweitens den kompletten Hintergrund zuerst löschst?
Durch das Löschen flackert es dann kurz. Wenn du alles zuerst zusammenbaust und dann komplett auf das Panel kopierst, wird es vermutlich weniger flackern.
Th69 - Do 24.02.11 18:20
Hallo jaenicke,
durch das DoubleBuffered (OptimizedDoubleBuffer) wird ja intern schon eine Bitmap verwendet, die dann direkt 'geblitted' wird.
jaenicke - Do 24.02.11 18:33
Das bringt aber nicht viel, wenn man zuerst die ganze Zeichenfläche löscht und die Elemente dann einzeln draufzeichnet. Denn das werden dann offenbar auch mehrere Kopiervorgänge. Erst ab Vista nimmt auch dann das Flackern durch das interne Zwischenspeichern von Vista/7 ab.
Wenn man natürlich nur selektiv Teile der Fläche löscht, kann das schon reichen, aber wenn man alles löscht, hat es bei mir trotz DoubleBuffered geflackert, insbesondere unter XP.
mvollmer - Fr 25.02.11 09:11
Hallo Th69,
Hallo jaenicke,
danke erstmal für eure Beteiligung :)
Zitat: |
Gibt es denn einen Unterschied zu "DoubleBuffered=false"? |
Habs grade mal ausprobiert. Macht keinen unterschied. Daraus lässt sich ja erkennen, das es schonmal damit nichts zu tun hat.
Zitat: |
P.S: Wie bist du gerade auf 156 gekommen (Bedenke, daß ein Windows-Timer nur eine Genauigkeit von 14-15ms hat). |
140ms zum Zeichnen + 16 ms ungenauigkeit.
Zitat: |
Ich vermute, dass du erstens direkt auf das Panel einzeln zeichnest und zweitens den kompletten Hintergrund zuerst löschst? |
Nein, ich erstelle eine neue Bitmap, zeichne dort alles drauf und zum Schluss setzte ich per BackgroundImage die Bitmap.
Ich poste mal meine Zeichenprozedure, vielleicht findet ihr etwas, was dazu führen könnte.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
| public void drawPlayingField() { Bitmap pf = new Bitmap(_SizeX * bitmapSize, _SizeY * bitmapSize); Graphics g = Graphics.FromImage(pf); Point p = new Point(); Image imgEmpty = Image.FromFile("Texture/empty.jpg"); for (int i = 0; i < _SizeY; i++) { for (int k = 0; k < _SizeX; k++) { p.X = bitmapSize * k; p.Y = bitmapSize * i; switch (PlayField[k, i].state) { case Field.FieldState.Emtpy: drawField(g, p, imgEmpty); break; case Field.FieldState.Blocked: break; case Field.FieldState.Brick: drawField(g, p, PlayField[k, i].brick.texture); break; default: break; } } } field.BackgroundImage = pf; }
private void drawField(Graphics g, Point p, Image img) { Pen GrayPen = new Pen(Color.Gray, 1); Rectangle rect = new Rectangle(p.X, p.Y, bitmapSize, bitmapSize); g.DrawImageUnscaledAndClipped(img, rect); g.DrawRectangle(GrayPen, rect); } |
mvollmer - Fr 25.02.11 12:14
Guten Tag,
ich weiß nicht warum. Aber als Spielfeld hab ich ein Panel genommen. Aufjedenfall hab ich das Panel jetzt durch eine PictureBox ersetzt und alles klappt wunderbar :)
VampireSilence - Sa 25.06.11 12:38
Das kann ich dir sagen. In dem Moment, wo du das Bild mit
C#-Quelltext
1:
| field.BackgroundImage = pf; |
änderst, veränderst du nicht nur das aktuell dargestellte Bild, sondern du veränderst auch den Quellverweis von field.BackgroundImage -> auf pf.
Ein Beispiel: Würdest du pf jetzt wieder ändern, würdest du diese Veränderung sofort sehen, weil BackgroundImage nun pf als Quelle verwendet und ständig aktuell hält. Eine Zuweisung ist also auch nur ein einziges Mal nötig.
The Better Way:
1. Nur ein Verweis (am besten direkt in der construct()-Methode, unter InitializeComponent())
2.a) Entweder du clonst dein Bild einfach in den Objektverweis hinein (einmaliges initialisieren des BackgroundImage-Bitmaps erforderlich), mit
C#-Quelltext
1:
| field.BackgroundImage = (Bitmap)pf.Clone(); |
(hierbei ist es aber wiederrum ein wildes rumgecaste, das Zeit kostet) oder noch besser:
2.b) Du erstellst dir deinen eigenen Buffer und zeichnest diesen nur noch (opaque!) über den aktuellen Frame, in etwa so:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| namespace AnyNS(musst du noch anpassen) { public partial class frmMain(oder wie immer deine Form heisst) : Form { Bitmap _Buffer, _Frame; Graphics _GFX, _Translator;
public frmMain() { InitializeComponent(); _Buffer = new Bitmap(höhe, breite), _Frame = new Bitmap(höhe, breite); _GFX = Graphics.FromImage(_Buffer), _Translator = Graphics.FromImage(_Frame); field.BackgroundImage = Frame; } } |
Du erstellt also einen Buffer, den du beschreibst. Dann einen Frame, der immer das aktuell angezeige Bild repräsentiert und wichtig: Keines der Objekte wird ständig komplett neu erstellt, das kostet nämlich Zeit (Zeit, in der der Bildschirm kurzzeitig weiß ist !). Wenn der Frame fertig ist, Drawst du ihn mit dem Translator ganz einfach in den Frame und das geht rasent schnell und erzeugt keinen einzigen Whiteflash:
C#-Quelltext
1: 2: 3:
| field.BackgroundImage = pf; _Translator.DrawImage(_Buffer, new Rectangle(Point.Empty, _Buffer.Size), new Rectangle(Point.Empty, _Frame.Size), GraphicsUnit.Pixel); |
Durch die Rectangle->Rectangle-Methode kommst du im übrigen niemals in die Verlegenheit von überstehenden Kanten etc., weil die Bildabmessungen automatisch aneinander angeglichen werden. Trotzdem sollten die natürlich gleich groß sein, sonst sieht das Bild später verzerrt aus. ;)
mfg
- VampireSilence
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!