Autor |
Beitrag |
mvollmer
Beiträge: 42
Erhaltene Danke: 3
Delphi (Seattle), C (MPLAB X)
|
Verfasst: Do 24.02.11 17:37
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
|
|
Th69
Beiträge: 4782
Erhaltene Danke: 1055
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Do 24.02.11 18:12
Hallo,
durch "DoubleBuffered=true" werden schon die internen Styles gesetzt (laut Reflector):
C#-Quelltext 1:
| this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, value); |
Gibt es denn einen Unterschied zu "DoubleBuffered=false"?
Ein kleines Flackern kannst du bei Bewegungen mit WinForms nicht vermeiden (habe ich bei meinem Rennspiel FastDriver auch), jedoch sollten fixe Objekte (z.B. Texte) flimmerfrei dargestellt werden.
Evtl. kannst du dir noch Anregungen aus Flackernde Controls vermeiden / Schnelles, flackerfreies Zeichnen holen...
P.S: Wie bist du gerade auf 156 gekommen (Bedenke, daß ein Windows-Timer nur eine Genauigkeit von 14-15ms hat).
Zuletzt bearbeitet von Th69 am Do 24.02.11 18:17, insgesamt 1-mal bearbeitet
|
|
jaenicke
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 4782
Erhaltene Danke: 1055
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: 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
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 42
Erhaltene Danke: 3
Delphi (Seattle), C (MPLAB X)
|
Verfasst: 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.
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
Beiträge: 42
Erhaltene Danke: 3
Delphi (Seattle), C (MPLAB X)
|
Verfasst: 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
Beiträge: 109
Erhaltene Danke: 5
C# (VS 2008 Express), PHP/MySQL, Windows XP
|
Verfasst: 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
|
|
|