Grafik verschwindet

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Wipf1000.

    Grafik verschwindet

    Hallo

    Ich hab folgendes Problem...
    Ich will immer wenn die leertaste gedrückt wird eine grafik zeichnen...
    Dass funktioniert anfangs auch gut aber sobald ich mich dann bewege wird diese wieder entfernt!
    Gibt es eine möglichkeit dass die grafik dauerhaft bleibt?

    Hier mal der code den ich verwende:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Integer) As Integer
    3. Dim taste As String
    4. Dim adobjekt As Boolean = False
    5. Dim objekt As String = "standard.png"
    6. Dim playerlocation As Point
    7. Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
    8. If GetAsyncKeyState(Keys.Left) = -32767 Then
    9. playerlocation.X -= 10
    10. ElseIf GetAsyncKeyState(Keys.Right) = -32767 Then
    11. playerlocation.X += 10
    12. ElseIf GetAsyncKeyState(Keys.Up) = -32767 Then
    13. playerlocation.Y -= 10
    14. ElseIf GetAsyncKeyState(Keys.Down) = -32767 Then
    15. playerlocation.Y += 10
    16. ElseIf GetAsyncKeyState(Keys.Space) = -32767 Then
    17. adobjekt = True
    18. End If
    19. Me.Refresh()
    20. Me.Text = adobjekt.ToString
    21. End Sub
    22. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    23. playerlocation.X = 10
    24. playerlocation.Y = 10
    25. End Sub
    26. Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
    27. Dim g As Graphics = e.Graphics
    28. g.DrawImage(Image.FromFile("img/player.png"), playerlocation)
    29. If adobjekt = True Then
    30. g.DrawImage(Image.FromFile("img/objekts/" & objekt), playerlocation.X + 32, playerlocation.Y)
    31. adobjekt = False
    32. End If
    33. End Sub
    34. End Class

    Also erst mal, bevor wir Dein Problem genauer betrachten, baue bitte was um:
    1. ist GetAsyncKeyState unnötig:

    VB.NET-Quellcode

    1. Protected Overrides OnKeyDown(e As KeyEventArgs)
    2. Select Case e.KeyCode
    3. Case Keys.Left
    4. '...
    5. Case Keys.Space
    6. '...
    7. End Select
    8. End Sub


    2. Lade die Bilder einmal beim Start des Programmes in Variablen und verwende diese anschließend in der Paint-Methode. Das Laden der Bilder dauert seine Zeit und in der Paint-Methode sollte man generell so wenig wie möglich machen.

    3. Me.Refresh ist unnötig. Dadurch erzwingst Du das sofortige Neuzeichnen. Verwende Me.Invalidate. Dann kann das System selbst entscheiden, wann genau es neuzeichnen sollte.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    GetAsyncKeyState hab ich raus genommen...


    Niko Ortner schrieb:

    2. Lade die Bilder einmal beim Start des Programmes in Variablen und verwende diese anschließend in der Paint-Methode. Das Laden der Bilder dauert seine Zeit und in der Paint-Methode sollte man generell so wenig wie möglich machen.
    Was genau ist damit jetzt gemeint? bzw wie mach ich dass?


    me.refresh hab ich jetzt durch invalidate ersetzt...

    VB.NET-Quellcode

    1. Class FormMitSinnvollemNamen
    2. Dim PlayerImage As Bitmap
    3. Dim ObjectImage As Bitmap
    4. Public Sub New()
    5. InitializeComponent()
    6. PlayerImage = New Bitmap(Pfad)
    7. ObjectImage = New Bitmap(Pfad)
    8. End Sub
    9. Overrides OnPaint(...)
    10. e.Graphics.DrawImage(PlayerImage, PlayerLocation)
    11. End Sub
    12. End Class

    Dadurch wird das Bild nur einmal von der Platte gelesen und dann bleibt's im Arbeitsspeicher.
    Übrigens müssen Bilder mit einem Aufruf an Dispose verworfen werden. Das heißt, wenn Du das machst:

    VB.NET-Quellcode

    1. e.Graphics.DrawImage(New Bitmap("img/player.png"), playerlocation)

    Dann wird immer wieder eine neue Bitmap-Instanz im Arbeitsspeicher abgelegt. Und diese Instanzen verschwinden nicht, wenn Dispose nicht aufgerufen wird. Irgendwann geht dem Programm der Arbeitsspeicher aus.

    Da würde sich auch eine eigene Klasse anbieten, die die Bilder verwaltet:

    VB.NET-Quellcode

    1. Public Class BitmapResources
    2. Private Shared _PlayerImage As Bitmap
    3. Public Shared ReadOnly Property PlayerImage As Bitmap
    4. Get
    5. If _PlayerImage Is Nothing Then
    6. _PlayerImage = New Bitmap(Pfad)
    7. End If
    8. Return _PlayerImage
    9. End Get
    10. End Property
    11. End Class
    12. 'Oder so:
    13. Public Class BitmapResources
    14. Private Shared _PlayerImage As Bitmap
    15. Public Shared ReadOnly Property PlayerImage As Bitmap
    16. Get
    17. Return _PlayerImage
    18. End Get
    19. End Property
    20. Public Shared Sub Load()
    21. _PlayerImage = New Bitmap(Pfad)
    22. End Sub
    23. End Class


    Dadurch hast Du die Verwaltung der Bilder aus dem übrigen Code raus und es wird aufgeräumter.
    Nochwas: Verwende nicht Image.FromFile() sondern New Bitmap(). Das ist wesentlich schneller.
    [VB 2010] GDI+ Bitmap Vergrößern -> Bitmap wird abgeschnitten
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Niko Ortner schrieb:


    Könntest Du bitte den Code posten, wie Du ihn jetzt hast?


    also der jetzige code sieht genau so aus:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim adobjekt As Boolean = False
    3. Dim playerlocation As Point
    4. Dim playerimage As Bitmap
    5. Dim objekt As Bitmap
    6. Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)
    7. Select Case e.KeyCode
    8. Case Keys.Left
    9. playerlocation.X -= 10
    10. Case Keys.Right
    11. playerlocation.X += 10
    12. Case Keys.Up
    13. playerlocation.Y -= 10
    14. Case Keys.Down
    15. playerlocation.Y += 10
    16. Case Keys.Space
    17. adobjekt = True
    18. End Select
    19. Me.Invalidate()
    20. End Sub
    21. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    22. playerlocation.X = 10
    23. playerlocation.Y = 10
    24. InitializeComponent()
    25. playerimage = New Bitmap("img/player.png")
    26. objekt = New Bitmap("img/objekts/standard.png")
    27. End Sub
    28. Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
    29. Dim g As Graphics = e.Graphics
    30. g.DrawImage(playerimage, playerlocation)
    31. playerimage = New Bitmap("img/player.png")
    32. If adobjekt = True Then
    33. g.DrawImage(objekt, playerlocation.X + 32, playerlocation.Y, Me.Width - Me.Width - 32, Me.Height - Me.Height - 64)
    34. adobjekt = False
    35. End If
    36. End Sub
    37. End Class
    Da gibt's noch ein paar Sachen, die mir nicht einleuchten.

    Bei Zeile 42 lädst Du erneut ein Bild. Das ist unnötig, denn Du lädst es ja schon im Konstruktor.

    Bei Zeile 46, was soll Me.Width - Me.Width - 32, Me.Height - Me.Height - 64 bedeuten?
    Du rechnest zum Beispiel 100 - 100 - 32, was in -32 resultiert. Das bedeutet übrigens, dass das Bild umgedreht wird. Also was davor links war ist jetzt rechts.

    Und es ist jetzt auch klar, warum das gezeichnete wieder verschwindet.

    Zuerst wird per Druck auf die Leertaste adobjekt auf True gesetzt und es wird neu gezeichnet. In der Zeichenroutine wird jetzt adobjekt geprüft. Es ist true, deshalb wird Zeile 46 ausgeführt. Aber jetzt hast Du adobject = False in diesem Block stehen. Das bedeutet, adobject ist ab sofort wieder False. Wenn jetzt nochmal neu gezeichnet wird, wird das Bild nicht mehr gezeichnet. Du müsstest nochmal Leertaste drücken, damit es nochmal ein einziges Mal gezeichnet wird. Und dann verschwindet es wieder.

    Was hast Du denn da überhaupt vor?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Zeile 42 gehört da nicht hin... werd ich gleich rauswerfen genauso wie zeile 46 da hab ich grad was ausprobiert (denkfehler)
    Qas ich vor hab ist dass man ein bild hin und her bewegt (dass funktikniert ja auch) und mit der leertaste immer neben dem bild ein weiteres plaziert (da ist dass problem)
    Ich hab nur noch nicht oft sowas gemacht und mit controls will ich garnicht erst anfangen...
    Iergendwie muss dass doch gehen oder?
    Dann musst Du eine Liste von Positionen erstellen, an denen das Bild gezeichnet werden soll.
    Das sieht dann in etwa so aus:

    VB.NET-Quellcode

    1. Dim CurrentPosition As Point
    2. Dim SavedPositions As New List(Of Point)
    3. Public Sub New()
    4. InitializeComponent()
    5. 'Startposition festlegen
    6. 'Bilder laden
    7. End Sub
    8. Overrides OnKeyDown(...)
    9. Select Case e.KeyCode
    10. Case Keys.Up
    11. CurrentPosition.Y -= ...
    12. Case Down
    13. '...
    14. Case Space
    15. SavedPositions.Add(CurrentPosition) 'Dadurch wird die aktuelle Position der Liste hinzugefügt.
    16. End Select
    17. Me.Invalidate()
    18. End Sub
    19. Overrides OnPaint(...)
    20. e.Graphics.DrawImage(..., CurrentPosition)
    21. For Each i In SavedPositions
    22. e.Graphics.DrawImage(..., i)
    23. Next
    24. End Sub
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils