verzögerte keyboard detection?

  • VB.NET

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von Plexian.

    verzögerte keyboard detection?

    Hallo, ich mag ein kleines Achtung die Kurve Spiel Programmieren - nur zur Übung.
    Mein Problem ist, dass die Keyboard detection irgendwie verzögert abläuft.
    Oder anders gesagt: wenn ich lenke passiert es erst eine halbe Sekunde (etwa) später.
    Hat wer eine Idee an was das liegen könnte?
    Hier ist der Quellcode: (es gibt nur eine picturebox und ein label auf der Form1)

    Quellcode

    1. Public Class Form1
    2. Dim g As Graphics
    3. Dim x, y, dir, speed As Double
    4. Dim moved As Boolean
    5. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    6. moved = False
    7. If (dir > 360) Then dir -= 360
    8. If (dir < 0) Then dir += 360
    9. x += speed * Math.Sin(dir)
    10. y += speed * Math.Cos(dir)
    11. lblStats.Text = "x: " + Convert.ToInt16(x).ToString + vbCrLf +
    12. "y: " + Convert.ToInt16(y).ToString + vbCrLf +
    13. "dir: " + Convert.ToInt16(dir).ToString + vbCrLf +
    14. "Ticks: " + Timer1.Interval.ToString
    15. g.FillPie(Brushes.Aqua, Convert.ToInt16(x) - 4, Convert.ToInt16(y) - 4, 8, 8, 0, 360)
    16. End Sub
    17. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    18. g = picbox.CreateGraphics()
    19. Randomize()
    20. lblStats.Text = "x: " + vbCrLf + "y: " + vbCrLf + "dir: " + vbCrLf + "Ticks: "
    21. End Sub
    22. Private Sub Player_Move(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    23. Select Case e.KeyCode
    24. Case Keys.Left
    25. If (moved = False) Then
    26. dir += speed * 0.055
    27. moved = True
    28. End If
    29. Case Keys.Right
    30. If (moved = False) Then
    31. dir -= speed * 0.055
    32. moved = True
    33. End If
    34. Case Keys.P
    35. Timer1.Enabled = Not Timer1.Enabled
    36. Case Keys.Enter
    37. x = 170
    38. y = 170
    39. moved = False
    40. speed = 1.2
    41. dir = Rnd() * 360
    42. Timer1.Enabled = True
    43. picbox.Refresh()
    44. g.FillPie(Brushes.Aqua, Convert.ToInt16(x) - 4, Convert.ToInt16(y) - 4, 8, 8, 0, 360)
    45. Case Keys.Escape
    46. Close()
    47. End Select
    48. End Sub
    49. End Class
    In welcher Frequenz läuft der Timer ?

    Btw, CreateGraphics sollte nicht genutzt werden. Verschiebe alles (!) aus der Timer_Tick Methode in eine Methode, die das PicBox.Paint-Event abbonniert. Im Timer-Tick steht, dann nur PicBox.Invalidate(rect).
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
    Danke für die schnelle Antwort. Leider habe ich jetzt nichts davon einbauen können :-/
    Der Timer ist auf 10ms geschaltet - an dem sollte es also eher nicht liegen
    was kann ich alternativ zu picturebox.createGraphics() verwenden?
    picbox invalidate nehme ich nicht, da ich ja will das die Kurve immer länger wird.

    Dr_Gre schrieb:

    was kann ich alternativ zu picturebox.createGraphics() verwenden?

    Die e.Graphics des Paint-Event
    Dein Spiel ist eine ownerDrawing-Anwendung.
    allerdings ist ownerdrawing recht vielfältig. guggemol OwnerDrawing
    wie du siehst: Es gibt durchaus Patentrezepte, aber Für viele Anwendungsfälle gibts halt viel Patentrezepte.
    Nur eine Grundregel: Kein control.CreateGraphics verwenden - immer nur das Graphics, was die painteventargs liefern

    Dr_Gre schrieb:

    picbox invalidate nehme ich nicht
    wohl aber

    Dr_Gre schrieb:

    VB.NET-Quellcode

    1. picbox.Refresh()
    das ist in diesem Kontext das selbe.
    Vielleicht bescheibst Du mal verbal, was passieren soll, wenn welche Taste gedrückt wird.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi
    KeyDown ist das Problem. Es reagiert beim Drücken der Taste und nach einer gewissen Zeit in regelmäßigem Intervall (ist beides in der Systemkonfiguration einstellbar) bis zum KeyUp.
    Speichere lieber im KeyDown in eine boolsche Variable oder ein Enum, in welche Richtungen eine Bewegung geschehen soll und lösche das bei KeyUp wieder. Im Timer wendest du diese Bewegungen dann an.
    Speichere übrigens die Routen der Spieler z.B. in eine List(Of Point) und zeichne diese dann.

    Viele Grüße
    ~blaze~
    @~blaze~ Du meinst AutoRepeat.
    @Dr_Gre Schmeiß den Timer raus und arbeite ausschließlich im KeyDown-Event:

    VB.NET-Quellcode

    1. Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    2. Me.Label1.Text = e.KeyCode.ToString ' Tun, was zu tun ist
    3. e.Handled = True ' Autorepeat unterdrücken
    4. End Sub
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Öh, kann sein, dass das so heißt.
    Aber wie genau stellst du dir das ohne irgendeine Form von Timer vor? Das Zeichnen der Oberfläche braucht ja auch einen Timer, um eine Invalidierung durchzuführen, das Verhalten bei Autorepeat ist nicht geeignet, um ein sinnvolles Spiel zu implementieren.

    Viele Grüße
    ~blaze~
    Hallo, war jetzt ein paar Tage nicht wirklich online.
    Danke für die vielen Antworten!!
    Der entscheidende Punkt war die Antwort von ~blaze~ - Habe jetzt jeweils einen boolean ob links bzw rechts gedrückt ist.
    Nun läuft so alles einwandfrei - welchen Vorteil hätte ich nicht picbox.createGraphics zu verwenden?
    Und welchen Vorteil bringt es das ganze als List(Of Point) zu speichern und alles einzeln zu drawen jeden step?
    Danke nochmals an Alle!!

    Grüße,
    Dr_Gre
    gib deiner picBox mal Dockstyle.Fill.
    Dann starte das Programm und spiel damit herum.
    Dann maximiere das Fenster - ich hoffe, dadurch wird auch deine picBox vergrößert.
    Spiele in dem nun größer gewordenen Zeichenbereich herum und wundere dich.

    Dr_Gre schrieb:

    ich mag ein kleines Achtung die Kurve Spiel Programmieren - nur zur Übung.
    wenn übung, dann übe, wie mans richtig macht.
    Sonst gewöhnst du dir nur Gruseligkeiten an, und wirst sie auch so reproduzieren, wenn mal wirklich was mit Grafik zu machen ist.
    Haste recht!
    Ich habe es jetzt etwas umgeschrieben und die list of point sowie das ownerDrawing eingebaut.
    Es gibt jetzt keine PictureBox mehr. Hier ist der Code - kann man da noch aws verbessern?
    Danke für alle Tips und Tricks!!

    [edit] Beim ausprobieren ist mir folgendes aufgefallen: Resizing, maximieren funktionieren jetzt einwandfrei mit der Grafik. Das Problem ist jetzt die GEschwindigkeit - ab etwa 2000 Punkten in der List of Points wird das ganze richtig langsam :-/

    Quellcode

    1. Public Class Form1
    2. Dim x, y, dir, speed As Double
    3. Dim leftDown, rightDown As Boolean
    4. Dim curve As List(Of Point)
    5. Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    6. Dim p As Point
    7. For i As Integer = 0 To curve.Count - 1
    8. p = curve.Item(i)
    9. e.Graphics.FillPie(Brushes.Aqua, Convert.ToInt16(p.X) - 4, Convert.ToInt16(p.Y) - 4, 8, 8, 0, 360)
    10. Next
    11. End Sub
    12. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    13. If leftDown = True Then dir += 0.05
    14. If rightDown = True Then dir -= 0.05
    15. If (dir > 360) Then dir -= 360
    16. If (dir < 0) Then dir += 360
    17. x += speed * Math.Sin(dir)
    18. y += speed * Math.Cos(dir)
    19. curve.Add(New Point(x, y))
    20. MyBase.Text = "Curve X " +
    21. "x : " + Convert.ToInt16(x).ToString +
    22. ", y: " + Convert.ToInt16(y).ToString +
    23. ", dir: " + Convert.ToInt16(dir).ToString
    24. MyBase.Invalidate()
    25. End Sub
    26. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    27. Randomize()
    28. x = MyBase.Width / 2
    29. y = MyBase.Height / 2 - 20
    30. curve = New List(Of Point)
    31. leftDown = False
    32. rightDown = False
    33. speed = 1.2
    34. dir = Rnd() * 360
    35. End Sub
    36. Private Sub Player_Move(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    37. Select Case e.KeyCode
    38. Case Keys.Left
    39. leftDown = True
    40. Case Keys.Right
    41. rightDown = True
    42. Case Keys.P
    43. Timer1.Enabled = Not Timer1.Enabled
    44. Case Keys.Enter
    45. x = MyBase.Width / 2
    46. y = MyBase.Height / 2 - 20
    47. leftDown = False
    48. rightDown = False
    49. speed = 1.2
    50. dir = Rnd() * 360
    51. Timer1.Enabled = True
    52. For i As Integer = 0 To curve.Count - 1
    53. curve.RemoveAt(0)
    54. Next
    55. Case Keys.Escape
    56. Close()
    57. End Select
    58. End Sub
    59. Private Sub Release(sender As Object, e As KeyEventArgs) Handles MyBase.KeyUp
    60. Select Case e.KeyCode
    61. Case Keys.Left
    62. leftDown = False
    63. Case Keys.Right
    64. rightDown = False
    65. End Select
    66. End Sub
    67. End Class

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

    jo - GDI+ ist auch einfach langsam.
    Ich versteh aber auch dein Code nicht, mit den ganzen FillPies - tust du da 2000 KuchenDiagramme generieren?
    Und den Timer hast du - lass mich raten - auf 10ms eingestellt?

    Also um bewegliche Szenen (Welten) darzustellen taugt GDI nicht. Ownerdrawing ist darauf optimiert, kleine Figuren auf einem unbewegten Hintergrund zu animieren. Und die Animation sollte auf User-Eingabe hin erfolgen, nicht unbedingt mit einem hochgetakteten Timer.

    Wenn dein Kurve-Spiel verlangt, dass bei jeder Bewegung die ganze Fläche neu zu rendern ist, dann ist GDI+ einfach das falsche MIttel.

    GDI+ wäre gut geeignet für sowas wie Tetris, oder Snake - evtl. sogar ein einfaches Moorhuhn.
    timer ist auf 10 ms - also 100 steps pro sekunde - too much? :D
    und ja, ich generiere fillpies als einfache methode einen kreis zu zeichnen.
    Habs grad auch einfach mit einem image import versucht:
    e.Graphics.DrawImage(point_img, curve.Item(i).X - 4, curve.Item(i).Y - 4)
    das ist aber genauso schlecht :-/
    Und wenn ich ein Flüssiges Spielfeld will sollte der TImer nicht wirklich auf mehr als 15ms eingestellt werden oder?
    Ja der Code ist zu verbessern, und zwar mehr als du denken magst:
    1. Schenk dir das Convert.ToInt16(), vollkommen unnötig
    2. Schmeiß Randomize und Rnd() raus und guck dir die System.Random-Klasse an. Erstelle eine (!!) globale Instanz davon, dann mit rnd.Next() die Zahl holen.
    3. Wie EDR sagte, erzähl mal wie hoch dein Timer getaktet ist. A) Langen 33ms, das entsprächen 30FPS (1000ms / 30FPS= 33ms) und b) kann ein Timer eh kaum schneller.
    4. Und hier das wichtigste. Du generierst im Timer-Tick einen, genau einen neuen Punkt. Invalidieren tust du aber die ganze PB. Es langt doch, wenn du nur das Rechteck um den neuen Punkt invalidierst. also PB.Invalidate(New Rectangle(x - 4, y - 4, 8, 8)) oder so.
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
    Danke nochmals für die vielen Antworten!
    Das verändern des TImers hat alles verbessert - steht jetzt auf 30ms und alles läuft super.
    ohne convert.toInt16() kann ich das bild nicht drawen - aus cos() bzw sin() bekomme ich ja einen double den ich dann in einen int verwandeln muss - oder geht das anders auch?
    Jetzt sieht der code so aus: (auf der Form ist nichts mehr außer der schwarze background und ein Timer mimt 30ms)

    Quellcode

    1. Public Class Form1
    2. Dim x, y, dir, speed As Double
    3. Dim is_drawing As Integer
    4. Dim leftDown, rightDown As Boolean
    5. Dim curve As List(Of Point)
    6. Dim point_img As Image
    7. Dim rnd1 As New Random
    8. Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    9. For i As Integer = 0 To curve.Count - 1
    10. e.Graphics.DrawImage(point_img, curve.Item(i).X - 4, curve.Item(i).Y - 4)
    11. Next
    12. e.Graphics.DrawImage(point_img, Convert.ToInt16(x) - 4, Convert.ToInt16(y) - 4) 'fall is_drawing gerade off
    13. End Sub
    14. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    15. If leftDown = True Then dir += 0.035 * speed
    16. If rightDown = True Then dir -= 0.035 * speed
    17. x += speed * Math.Sin(dir)
    18. y += speed * Math.Cos(dir)
    19. If x <= 1 Or y <= 1 Or x >= MyBase.Width - 16 Or y >= MyBase.Height - 40 Then
    20. Timer1.Enabled = False
    21. MessageBox.Show("You drove out of the window!")
    22. New_Game()
    23. End If
    24. is_drawing += 1
    25. If is_drawing < 40 * 3.7 Then curve.Add(New Point(x, y))
    26. If is_drawing > 40 * 4 Then is_drawing = 0
    27. MyBase.Text = "Curve X " +
    28. "x : " + Convert.ToInt16(x).ToString +
    29. ", y: " + Convert.ToInt16(y).ToString +
    30. ", dir: " + Convert.ToInt16(dir).ToString +
    31. ", Point count: " + Convert.ToInt16(curve.Count).ToString
    32. MyBase.Invalidate(New Rectangle(x - 10, y - 10, x + 10, y + 10))
    33. End Sub
    34. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    35. rnd1 = New Random
    36. curve = New List(Of Point)
    37. point_img = Image.FromFile("C:\Users\Gregor\Documents\Visual Studio 2015\Projects\Curve X\Curve X\point_pic.png")
    38. End Sub
    39. Private Sub Player_Move(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    40. Select Case e.KeyCode
    41. Case Keys.Left
    42. leftDown = True
    43. Case Keys.Right
    44. rightDown = True
    45. Case Keys.P
    46. Timer1.Enabled = Not Timer1.Enabled
    47. Case Keys.Enter
    48. New_Game()
    49. Case Keys.Escape
    50. Close()
    51. End Select
    52. End Sub
    53. Private Sub Release(sender As Object, e As KeyEventArgs) Handles MyBase.KeyUp
    54. Select Case e.KeyCode
    55. Case Keys.Left
    56. leftDown = False
    57. Case Keys.Right
    58. rightDown = False
    59. End Select
    60. End Sub
    61. Private Sub New_Game()
    62. x = MyBase.Width / 2
    63. y = MyBase.Height / 2 - 20
    64. leftDown = False
    65. rightDown = False
    66. speed = 2.9
    67. dir = rnd1.NextDouble * 6
    68. is_drawing = 0
    69. Timer1.Enabled = True
    70. MyBase.Invalidate()
    71. For i As Integer = 0 To curve.Count - 1
    72. curve.RemoveAt(0)
    73. Next
    74. End Sub
    75. End Class
    Es wird, es wird.
    - Du hast recht, ich nahm an x und y kämen von einem Punkt nicht als normale Double Variable. Aber was hindert dich daran, x und y direkt als Integer zu deklarieren, und beim zuweisen dann direkt in Integer zu konvertieren ? --> ​x += CInt(speed * Math.Sin(dir)) Spart ne Meeeeenge converts.
    - Desweiteren, schau dir mal genau den Rectangle-Konstruktor auf MSDN an. Die letzten beiden Parameter stehen nicht für die untere rechte Ecke des REchtecks ;)
    - Booleans mit True zu vergleichen ist unnötig, es langt zu schreiben ​if (bool) anstatt ​if (bool == true)
    - Zu den Convert.ToInt16-Benutzungen: curve.Count ist ein Integer wie dir die IDE sagen wird, keine Konvertierung in ein Short (=Int16) von Nöten.
    - Last but not least, es gibt eine List.Clear()-Methode.
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais