DrawImage flackert?

  • VB.NET

Es gibt 30 Antworten in diesem Thema. Der letzte Beitrag () ist von nafets3646.

    Ja du siehst es doch in der GIF.
    Ich komme mit dem Spieler nicht bis an den Rand der Form, sondern irgendwo ist eine Grenze gezogen die ich ja nicht hin gemacht habe..

    Code bis jetzt:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Int32) As Short
    3. Dim g As Graphics = Me.CreateGraphics
    4. Sub Render()
    5. Dim g As Graphics = Me.CreateGraphics
    6. paintPlayer(g)
    7. End Sub
    8. Private Sub paintPlayer(ByVal g As Graphics)
    9. Dim playerSprite As New Bitmap("C:\Unten.png")
    10. g.DrawImage(playerSprite, XPOS, YPOS, 30, 86)
    11. End Sub
    12. Dim XPOS As Integer
    13. Dim YPOS As Integer
    14. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    15. Label3.Text = XPOS
    16. Label4.Text = YPOS
    17. Dim hotkey1 As Boolean
    18. hotkey1 = GetAsyncKeyState(Keys.A)
    19. If hotkey1 = True Then
    20. XPOS = XPOS - 3
    21. Refresh()
    22. paintPlayer(g)
    23. End If
    24. Dim hotkey2 As Boolean
    25. hotkey2 = GetAsyncKeyState(Keys.D)
    26. If hotkey2 = True Then
    27. XPOS = XPOS + 3
    28. Refresh()
    29. paintPlayer(g)
    30. End If
    31. Dim hotkey3 As Boolean
    32. hotkey3 = GetAsyncKeyState(Keys.W)
    33. If hotkey3 = True Then
    34. YPOS = YPOS - 3
    35. Refresh()
    36. paintPlayer(g)
    37. End If
    38. Dim hotkey4 As Boolean
    39. hotkey4 = GetAsyncKeyState(Keys.S)
    40. If hotkey4 = True Then
    41. YPOS = YPOS + 3
    42. Refresh()
    43. paintPlayer(g)
    44. End If
    45. End Sub
    46. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    47. Me.DoubleBuffered = True
    48. SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.UserPaint, True)
    49. XPOS = 191
    50. YPOS = 131
    51. End Sub
    52. End Class


    Wie kann ich denn das dauernd neue Laden des Bildes umgehen?

    Mfg. TGS
    Du brauchst eine ordentliche Klassenstruktur. Wenn z.B. die Spielfigur in einer eigenen Klasse ist, dann kannst du dort alle Werte wie Position und aktuelles Bild speichern. Wenn sich der Zustand ändern soll veränderst du nur die Werte innerhalb der Klasse und rufst dann Invalidate auf. Dadurch wird das Paint-Event ausgelöst, in dem du dann alle Objekte auf einmal mit ihrem derzeitigen Status neuzeichnest. Das ist die einzige richtige Vorgehensweise in GDI eine Engine zu erstellen.
    Hier ist mal ein kleines Beispiel, dass mit Doublebuffer nicht flackert. Kopier das mal in ein neues Projekt und guck obs klappt.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. WithEvents Timer1 As New Timer With {.Enabled = True, .Interval = 2}
    2. Dim counter As Single = 0
    3. Private Sub Form1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
    4. Dim g As Graphics = e.Graphics
    5. With g
    6. .InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    7. .PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
    8. .SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    9. End With
    10. Dim radiusX As Integer = Me.Width \ 2 - 30
    11. Dim radiusY As Integer = Me.Height \ 2 - 30
    12. Dim x As Single = CSng(Math.Cos(counter) * radiusX) + Me.Width \ 2
    13. Dim y As Single = CSng(Math.Sin(counter) * radiusY) + Me.Height \ 2 - 25
    14. g.FillEllipse(Brushes.Black, x, y, 15, 15)
    15. End Sub
    16. Private Sub Timer1_Tick(sender As Object, e As System.EventArgs) Handles Timer1.Tick
    17. counter += 0.03F
    18. If counter = 359 Then counter = 0
    19. Me.Invalidate()
    20. End Sub
    21. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    22. Me.DoubleBuffered = True
    23. End Sub
    Danke sehr, hat funktioniert.

    Hier mein Code:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Int32) As Short
    3. WithEvents Timer1 As New Timer With {.Enabled = True, .Interval = 2}
    4. Dim counter As Single = 0
    5. Dim XPOS As Single
    6. Dim YPOS As Single
    7. Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
    8. Dim g As Graphics = e.Graphics
    9. With g
    10. .InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    11. .PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
    12. .SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    13. End With
    14. Dim playerSprite As New Bitmap("C:\Unten.png")
    15. g.DrawImage(playerSprite, XPOS, YPOS, 30, 86)
    16. End Sub
    17. Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    18. counter += 0.03F
    19. If counter = 359 Then counter = 0
    20. Me.Invalidate()
    21. Dim hotkey1 As Boolean
    22. hotkey1 = GetAsyncKeyState(Keys.A)
    23. If hotkey1 = True Then
    24. XPOS = XPOS - 3
    25. End If
    26. Dim hotkey2 As Boolean
    27. hotkey2 = GetAsyncKeyState(Keys.D)
    28. If hotkey2 = True Then
    29. XPOS = XPOS + 3
    30. End If
    31. Dim hotkey3 As Boolean
    32. hotkey3 = GetAsyncKeyState(Keys.W)
    33. If hotkey3 = True Then
    34. YPOS = YPOS - 3
    35. End If
    36. Dim hotkey4 As Boolean
    37. hotkey4 = GetAsyncKeyState(Keys.S)
    38. If hotkey4 = True Then
    39. YPOS = YPOS + 3
    40. End If
    41. End Sub
    42. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    43. Me.DoubleBuffered = True
    44. End Sub
    45. End Class



    Mfg. TGS :)
    Ich hab deinen Code mal ein bisschen verbessert, unter anderem hab ich die Tastenabfrage verkürzt und OPTION STRICT ON gemacht. Außerdem habe ich das Laden des Bildes verschoben, damit es nicht immer wieder aufs Neue geladen wird. Des weiteren habe ich noch das Zeichnen in die OnPaint-Sub gemacht und das Invalidaten an das Ende der Timer-Sub verschoben:

    VB.NET-Quellcode

    1. Option Strict On 'Das Allerwichtigste, sollte in jedem Programm stehen
    2. Public Class Form1
    3. Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Int32) As Short
    4. WithEvents Timer1 As New Timer With {.Enabled = True, .Interval = 2}
    5. Dim counter As Single = 0
    6. Dim XPOS As Single
    7. Dim YPOS As Single
    8. Dim playerSprite As New Bitmap("C:\Unten.png") 'Das Laden des Bildes von der Festplatte hierhin verschieben
    9. Protected Overrides Sub OnPaint(e As PaintEventArgs) 'OnPaint zu Overriden ist noch einen minimalen Ticken schneller als das Event abzuwarten ;)
    10. With e.Graphics 'Du musst kein Graphics-Objekt deklarieren, wenn du schon eins hast
    11. '.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic 'Die Modes kannst du eigentlich weglassen, da ich denke, dass deine Spielfigur schon die richtige Größe hat,
    12. '.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality 'ansonsten kannst du sie auch wieder auskommentieren bzw. nicht so hoch stellen. Dies sollte die Performance deutlich erhöhen.
    13. '.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    14. .DrawImage(playerSprite, XPOS, YPOS, 30, 86) 'Warum nicht gleich hier rein? :D
    15. End With
    16. MyBase.OnPaint(e)
    17. End Sub
    18. Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    19. counter += 0.03F
    20. If counter = 359 Then counter = 0
    21. Dim XPOSOld As Single = XPOS
    22. Dim YPOSOld As Single = YPOS
    23. If CBool(GetAsyncKeyState(Keys.A)) Then XPOS = XPOS - 3 'kürzer und schneller, außerdem Option Strict On
    24. If CBool(GetAsyncKeyState(Keys.D)) Then XPOS = XPOS + 3
    25. If CBool(GetAsyncKeyState(Keys.W)) Then YPOS = YPOS - 3
    26. If CBool(GetAsyncKeyState(Keys.S)) Then YPOS = YPOS + 3
    27. If XPOS = XPOSOld AndAlso YPOS = YPOSOld Then Me.Invalidate() 'Invalidaten ans Ende verschoben und mit If-Abfrage versehen, da ich denke, dass du erst zeichnen willst, wenn die Werte von der Tastatur abgefragt wurden und auch nur, wenn sich irgendetwas geändert hat
    28. End Sub
    29. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    30. Me.DoubleBuffered = True
    31. End Sub
    32. End Class


    //EDIT: Hab noch die Empfehlungen von ErfinderDesRades umgesetzt, allerdings habe ich das mit der Region beim invalidaten nicht ersetzt. Probiers aus :).

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nafets3646“ ()

    für DrawImage braucht man glaub keinen der Modes (InterpolationMode, .PixelOffsetMode, .SmoothingMode) zu manipulieren.
    Wenn keine Taste gedrückt ist, sollteman auch kein .Invalidate() aufrufen.
    Die Performance lässt sich ca. 50-fach steigern, wenn man bei Invalidate einschränkt, welches Rechteck zu neuzeichnen ist.

    Ich kanns nicht ausprobieren, weil ich das Bildle nicht hab, aber hat der DoppelPuffer eine merkliche Auswirkung?
    Hab noch eine Frage.

    Mit der Picturebox gings so, dass sie nicht mehr aus der Form kam:

    VB.NET-Quellcode

    1. Dim ScreenSize As Size = Me.Size
    2. If PictureBox1.Left <= 0 Then
    3. PictureBox1.Left = 0
    4. ElseIf PictureBox1.Left + PictureBox1.Width >= ScreenSize.Width Then
    5. PictureBox1.Left = ScreenSize.Width - PictureBox1.Width
    6. End If
    7. If PictureBox1.Top <= 0 Then
    8. PictureBox1.Top = 0
    9. ElseIf PictureBox1.Top + PictureBox1.Height >= ScreenSize.Height Then
    10. PictureBox1.Top = ScreenSize.Height - PictureBox1.Height
    11. End If



    Doch wie geht das nun mit GDI (Mit dem Code oben)?

    Wäre nett, wenn mir da jemand helfen könnte :)

    Danke, und mfg.
    TGS :)
    Das geht dann so:

    VB.NET-Quellcode

    1. Dim MovingRectangle As New Rectangle(New Point(10, 10), New Size(100, 100)) 'Das Rectangle, in dem sich die Figur bewegen kann
    2. Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    3. counter += 0.03F
    4. If counter = 359 Then counter = 0
    5. Dim XPOSOld As Single = XPOS
    6. Dim YPOSOld As Single = YPOS
    7. If CBool(GetAsyncKeyState(Keys.A)) AndAlso XPOS - 3 >= MovingRectangle.X Then XPOS -= 3 'einfach noch ne zweite Bedingung, welche prüft, ob die Figur aus dem Rectangle rauslaufen würde
    8. If CBool(GetAsyncKeyState(Keys.D)) AndAlso XPOS + 3 <= MovingRectangle.X + MovingRectangle.Width - 30 Then XPOS += 3 'Die -30 steht für die Breite deiner Spielfigur
    9. If CBool(GetAsyncKeyState(Keys.W)) AndAlso YPOS - 3 >= MovingRectangle.Y Then YPOS -= 3
    10. If CBool(GetAsyncKeyState(Keys.S)) AndAlso YPOS + 3 <= MovingRectangle.Y + MovingRectangle.Height - 80 Then YPOS += 3 'Die -80 steht für die Höhe deiner Spielfigur
    11. If XPOS = XPOSOld AndAlso YPOS = YPOSOld Then Me.Invalidate() 'Invalidaten ans Ende verschoben und mit If-Abfrage versehen, da ich denke, dass du erst zeichnen willst, wenn die Werte von der Tastatur abgefragt wurden und auch nur, wenn sich irgendetwas geändert hat
    12. End Sub