Pixelgenaue Kollision

  • VB.NET

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von X-Zat.

    Pixelgenaue Kollision

    Nabend Community!

    Bin dabei ein Spiel zu programmieren und bei der Bewegung und der Kollisionsüberprüfung hakt's... Ja, Google und SuFu hab ich schon geplagt - Erfolglos :(

    Beschreibung:

    Die Bewegung des Spielers (im Moment nur ein Kreis) werden durch Kraft-Angaben durchgeführt: Bei Tastendruck wird eine Sub der Klasse des Spielers ausgeführt, bei der dann ein Double-Wert angegeben wird, wie weit sich der Spieler nun bewegt.
    Und die Kollisionsüberprüfung wird im Moment noch recht simpel gehandhabt, funktioniert aber auch irgendwie nicht... Wenn eine Kollision zustande kommt, wird die Richtung, in der sich der Spieler bewegt, umgekehrt - Irgendwie war's das auch schon und egal wie heftig ich in die Tasten hau, der Spieler bewegt sich 0.


    Verwendete Programmiersprache und IDE

    Visual Basic (Visual Studio 2010 Ultimate)
    .Net Framework 2.0

    Fehlermeldung:

    Keine...

    Bisheriger Code:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. '
    2. ' Dieser Sub ist in Form1 und wird durch einen Timer bearbeitet (Interval = 40 für flüssige 25 FPS)
    3. '
    4. Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    5. Dim MoveNS As Double = 0
    6. Dim MoveWE As Double = 0
    7. If GetAsyncKeyState(Keys.Left) Then
    8. MoveWE -= 0.2
    9. End If
    10. If GetAsyncKeyState(Keys.Right) Then
    11. MoveWE += 0.2
    12. End If
    13. If GetAsyncKeyState(Keys.Up) Then
    14. MoveNS -= 0.2
    15. End If
    16. If GetAsyncKeyState(Keys.Down) Then
    17. MoveNS += 0.2
    18. End If
    19. If GetAsyncKeyState(Keys.Space) Then
    20. Sly.Break(0.3)
    21. End If
    22. Sly.Move(MoveNS, MoveWE)
    23. Me.Invalidate()
    24. End Sub
    25. '
    26. ' Und dieser Sub ist innerhalb der SlyBot-Klasse ("Sly" ist ein Objekt des Typs "SlyBot" - Die Spieler-Klasse)
    27. '
    28. Public Sub Move(ByVal p_ForceNS As Double, ByVal p_ForceWE As Double)
    29. m_ForceNS = Math.Round(m_ForceNS + p_ForceNS * (1 + Math.Abs(m_ForceNS)), 3)
    30. m_ForceWE = Math.Round(m_ForceWE + p_ForceWE * (1 + Math.Abs(m_ForceWE)), 3)
    31. m_Location = New Point(m_Location.X + m_ForceWE, m_Location.Y + m_ForceNS)
    32. If p_ForceNS = 0 Or p_ForceWE = 0 Then Break(0.9)
    33. If CDown() = True Or CUp() = True Then m_ForceNS *= -m_Bounce
    34. If CLeft() = True Or CRight() = True Then m_ForceWE *= -m_Bounce
    35. m_Tail.Add(New Point(m_Location.X + m_Size.Width / 2, m_Location.Y + m_Size.Height / 2))
    36. m_Speed.Add(Math.Abs(ForceWE) + Math.Abs(ForceNS))
    37. If m_Speed.Count >= 500 Then m_Speed.RemoveAt(0)
    38. End Sub
    39. '
    40. ' Diese Funktionen der Klasse SlyBot geben zurück, ob eine Kollision stattgefunden hat (Fenstergröße ist 800x600)
    41. '
    42. Private Function CRight() As Boolean
    43. Return m_Location.X + m_Size.Width >= 800
    44. End Function
    45. Private Function CLeft() As Boolean
    46. Return m_Location.X <= 0
    47. End Function
    48. Private Function CUp() As Boolean
    49. Return m_Location.Y <= 0
    50. End Function
    51. Private Function CDown() As Boolean
    52. Return m_Location.Y + m_Size.Height >= 640
    53. End Function
    54. '
    55. ' Dieser Sub der SlyBot-Klasse wird verwendet, um den Spieler bei fehlendem Tastendruck langsam und bei Tastendruck stark abzubremsen
    56. '
    57. Public Sub Break(ByVal p_Strength As Double)
    58. If Not m_ForceNS = 0 Then
    59. m_ForceNS *= p_Strength
    60. End If
    61. If Not m_ForceWE = 0 Then
    62. m_ForceWE *= p_Strength
    63. End If
    64. End Sub



    Sonstige nützliche Informationen:

    - Die Variablen "m_ForceNS" und "m_ForceWE" geben die aktuelle Geschwindigkeit nach oben/unten (North/South) und links/rechts (West/East) an.
    - Die Variablen "m_Tail" und "m_Speed" sind für Bewegung und Kollision ohne Bedeutung. "m_Tail" speichert die Positionen des Spielers ab und ermöglicht die Anzeige eines "Schweifes" des Spielers auf dem Bildschirm. "m_Speed" ist eine Auflistung der Gesamtgeschwindigkeit und dient der Anzeige der Geschwindigkeit in einem Graph in einer anderen Form.

    Wäre wirklich super, wenn mir jemand helfen könnte, ich habe langsam keine Ahnung mehr, wie ich das umsetzen soll :wacko:

    MfG,
    X-Zat / Momo
    Also ich verstehe es so, dass bei jedem TimerTick deine Move-Variablen erst mal auf Null
    gesetzt werden, dann nützt es auch nichts, wenn du sie um jeweils 0.2 erhöhen willst

    MoveWE += 0.2

    ist dann das gleiche wie

    MoveWE = 0.2

    und somit addierst du nichts und nichts bewegt sich.
    Deine Formeln habe ich jetzt noch nicht eingehend studiert,
    da wäre etwas mehr Erklärung sinnvoll.
    Erstmal zu den Variablen beim Timer:

    Klar, ich kann die direkt auf z.B. 0.2 setzen, jedoch ist dies situationsbedingt (evtl. werden durch spätere Einflüsse wie Mausbewegungen globale Variablen auch beeinflusst, ist momentan noch Teststadium). Das ganze werde ich später bestimmt noch expandieren ;) Du darfst auch nicht vergessen: MoveNS und MoveWE sind nur lokale und damit temporäre Variablen - Sie müssen bei jedem Tick zu Anfang auf 0 stehen!
    Denn MoveNS und MoveWE werden dem Public Sub Move von der Klasse SlyBot übergeben und da den globalen klasseneigenen Variablen angerechnet.

    Und zu den Formeln:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. '
    2. ' Und dieser Sub ist innerhalb der SlyBot-Klasse ("Sly" ist ein Objekt des Typs "SlyBot" - Die Spieler-Klasse)
    3. '
    4. Public Sub Move(ByVal p_ForceNS As Double, ByVal p_ForceWE As Double)
    5. 'Hier werden die Kalsseneigenen Kraft-Variablen m_ForceNS und -WE um das
    6. 'Produkt der übergebenen Kraft und eines Wertes > 1 erhöht. Damit erhalte ich
    7. 'eine schön schnelle Beschleunigung (nicht linear!)
    8. m_ForceNS = Math.Round(m_ForceNS + p_ForceNS * (1 + Math.Abs(m_ForceNS)), 3)
    9. m_ForceWE = Math.Round(m_ForceWE + p_ForceWE * (1 + Math.Abs(m_ForceWE)), 3)
    10. 'Der Location des Spielers werden die NS- und WE-KRäfte hinzugefügt (hier also die "Bewegung")
    11. m_Location = New Point(m_Location.X + m_ForceWE, m_Location.Y + m_ForceNS)
    12. 'Wenn eine der Kräfte = 0 ist soll abgebremst werden
    13. If p_ForceNS = 0 Or p_ForceWE = 0 Then Break(0.9)
    14. 'Hier die Reaktion auf die Kollisionsabfragen:
    15. 'Wenn positives Ergebnis -> Kraft wird umgekehrt und vermindert/vergrößert um den Faktor
    16. 'm_Bounce (soll nachher angeben, wie sehr der Spieler bei Kollision an Geschwindigkeit verliert)
    17. If CDown() = True Or CUp() = True Then m_ForceNS *= -m_Bounce
    18. If CLeft() = True Or CRight() = True Then m_ForceWE *= -m_Bounce
    19. 'Hier werden noch die Listen zum Verlauf und der Geschwindigkeit befüllt/erweitert
    20. m_Tail.Add(New Point(m_Location.X + m_Size.Width / 2, m_Location.Y + m_Size.Height / 2))
    21. m_Speed.Add(Math.Abs(ForceWE) + Math.Abs(ForceNS))
    22. If m_Speed.Count >= 500 Then m_Speed.RemoveAt(0)
    23. End Sub
    24. '
    25. ' Diese Funktionen der Klasse SlyBot geben zurück, ob eine Kollision stattgefunden hat (Fenstergröße ist 800x600)
    26. '
    27. Private Function CRight() As Boolean
    28. 'Befindet sich der Spieler rechts vom eigentlichem Fenster/am rechten Fensterrand?
    29. Return m_Location.X + m_Size.Width >= 800
    30. End Function
    31. Private Function CLeft() As Boolean
    32. 'Spieler ist am linken Rand
    33. Return m_Location.X <= 0
    34. End Function
    35. Private Function CUp() As Boolean
    36. 'Spieler ist am oberen Rand
    37. Return m_Location.Y <= 0
    38. End Function
    39. Private Function CDown() As Boolean
    40. 'Spieler ist am unteren Rand
    41. Return m_Location.Y + m_Size.Height >= 640
    42. End Function
    43. '
    44. ' Dieser Sub der SlyBot-Klasse wird verwendet, um den Spieler bei fehlendem Tastendruck langsam und bei Tastendruck stark abzubremsen
    45. '
    46. Public Sub Break(ByVal p_Strength As Double)
    47. 'Wenn die NS-Kraft nicht 0 ist
    48. If Not m_ForceNS = 0 Then
    49. 'Verringere die NS-Kraft um die angegebene Stärke (z.B. Kraft: 5.0, Stärke: 0.9 -> Kraft nach dem Veringern: 4.5)
    50. m_ForceNS *= p_Strength
    51. End If
    52. 'Dasselbe mit der horizontalen Kraft
    53. If Not m_ForceWE = 0 Then
    54. m_ForceWE *= p_Strength
    55. End If
    56. End Sub


    Wenn es sonst noch Verständnisprobleme gibt oder der Codeausschnitt nicht ausreichen sollte, hätte ich wohl noch den Rest parat ;).

    MfG,
    X-Zat / Momo
    m_Bounce kann Null sein, darf es aber nicht: Multiplikation zweier Werte von denen einer 0 ist, ist sinnlos oder?
    Momentan habe ich den Wert auf 0.9 gesetzt, also wird die Geschwindigkeit bei Aufprall nur um ein 10tel reduziert...

    Wäre m_Bounce jetzt 0 wäre natürlich auch jede weitere Bewegung für die Katz, da die Kraft der Bewegung bei Aufprall auf Null gesetzt werden würde... Ich habe nur leider keine Ahnung, woran's bei meinem System hakt... Oder wie ich das System verbessern kann...

    MfG,
    X-Zat / Momo
    @SystemUnknow:
    Wow, das sieht mal interessant aus... Mal schauen, ob's mich weiterbringt... Ansonsten werd ich wohl mein System neu aufbauen müssen.

    Außerdem ist mir eingefallen, wo das Problem sein könnte:

    Angenommen, der Spieler berührt nun eine Wand perfekt, so prallt er ab. Wenn er aber auch nur einen Pixel zu weit "in die Wand" geht (kommt vor, wenn die Kraft der Bewegung zu hoch ist und er sich mehrere Pixel weit bewegt), merkt er, dass er zu weit ist und dreht sich um - Dazu kommt, dass bei dem umdrehen durch den Bounce-Effekt die Geschwindigkeit sinkt und er evtl. beim nächsten Schritt immernoch im ungültigen Bereich ist. Dadurch kommt er nie wieder da raus...
    Also müsste ich ihn sich Pixel für Pixel bewegen lassen...

    ... ?(

    - Ok, erschlagt mich und nennt mich Bob, ich Vollpfosten hab (wahrscheinlich) die Lösung auf mein Problem beim schreiben oben gefunden - Super, ich setzt das ganze mal in Pixel-Schritte um... Ich bin ein Held :thumbup:

    MfG,
    X-Zat / Momo

    //Edit:

    Hab's neu eingebaut... Funktioniert besser, aber immernoch fehlerhaft...

    Das Gute: Der Spieler bleibt nun innerhalb des gültigen Bereichs.
    Das Schlachte: a) Der Spieler prallt teilweise weit vor einer Kollision ab... b) Fährt er langsam an einem Rand entlang kann er außerhalb des gültigen Bereichs...

    Der Code schaut nun so aus:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Sub Move(ByVal p_ForceNS As Double, ByVal p_ForceWE As Double)
    2. m_ForceNS = Math.Round(m_ForceNS + p_ForceNS * (1 + Math.Abs(m_ForceNS)), 3)
    3. m_ForceWE = Math.Round(m_ForceWE + p_ForceWE * (1 + Math.Abs(m_ForceWE)), 3)
    4. 'm_Location = New Point(m_Location.X + m_ForceWE, m_Location.Y + m_ForceNS)
    5. For i = 0 To Math.Abs(m_ForceNS)
    6. If m_ForceNS >= 1 Then m_Location = New Point(m_Location.X, m_Location.Y + 1)
    7. If m_ForceNS <= -1 Then m_Location = New Point(m_Location.X, m_Location.Y - 1)
    8. If CDown() = True Or CUp() = True Then m_ForceNS *= -m_Bounce
    9. Next
    10. For i = 0 To Math.Abs(m_ForceWE)
    11. If m_ForceWE >= 1 Then m_Location = New Point(m_Location.X + 1, m_Location.Y)
    12. If m_ForceWE <= -1 Then m_Location = New Point(m_Location.X - 1, m_Location.Y)
    13. If CLeft() = True Or CRight() = True Then m_ForceWE *= -m_Bounce
    14. Next
    15. If p_ForceNS = 0 Or p_ForceWE = 0 Then Break(0.9)
    16. 'If CDown() = True Or CUp() = True Then m_ForceNS *= -m_Bounce
    17. 'If CLeft() = True Or CRight() = True Then m_ForceWE *= -m_Bounce
    18. m_Tail.Add(New Point(m_Location.X + m_Size.Width / 2, m_Location.Y + m_Size.Height / 2))
    19. m_Speed.Add(Math.Abs(ForceWE) + Math.Abs(ForceNS))
    20. If m_Speed.Count >= 500 Then m_Speed.RemoveAt(0)
    21. End Sub

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „X-Zat“ ()

    Ne, bewegen tut sich schon die ganze Zeit was, nur nicht so, wie es sollte ;).

    Also, ich hab' den Code nochmal angepasst und nun hab ich nur noch das Problem, dass

    Ok, hab alle Probleme soweit gelöst^^. Funzt 1a ohne Probleme - Ich bin einfach ein Pfosten... Hätte ich auch früher drauf kommen können...
    Hier der fertige Code der Bewegungs-Sub:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Sub Move(ByVal p_ForceNS As Double, ByVal p_ForceWE As Double)
    2. m_ForceNS = Math.Round(m_ForceNS + p_ForceNS * (1 + Math.Abs(m_ForceNS)), 3)
    3. m_ForceWE = Math.Round(m_ForceWE + p_ForceWE * (1 + Math.Abs(m_ForceWE)), 3)
    4. 'Vertikale Kraft Pixel fuer Pixel umsetzen...
    5. For i = 0 To Math.Abs(m_ForceNS)
    6. If m_ForceNS >= 1 Then m_Location = New Point(m_Location.X, m_Location.Y + 1)
    7. If m_ForceNS <= -1 Then m_Location = New Point(m_Location.X, m_Location.Y - 1)
    8. If CDown() = True Then
    9. m_ForceNS *= -m_Bounce
    10. m_Location = New Point(m_Location.X, m_Location.Y - 1)
    11. Exit For
    12. End If
    13. If CUp() = True Then
    14. m_ForceNS *= -m_Bounce
    15. m_Location = New Point(m_Location.X, m_Location.Y + 1)
    16. Exit For
    17. End If
    18. Next
    19. 'Horizontale Kraft Pixel fuer Pixel umsetzen...
    20. For i = 0 To Math.Abs(m_ForceWE)
    21. If m_ForceWE >= 1 Then m_Location = New Point(m_Location.X + 1, m_Location.Y)
    22. If m_ForceWE <= -1 Then m_Location = New Point(m_Location.X - 1, m_Location.Y)
    23. If CLeft() = True Then
    24. m_ForceWE *= -m_Bounce
    25. m_Location = New Point(m_Location.X + 1, m_Location.Y)
    26. Exit For
    27. End If
    28. If CRight() = True Then
    29. m_ForceWE *= -m_Bounce
    30. m_Location = New Point(m_Location.X - 1, m_Location.Y)
    31. Exit For
    32. End If
    33. Next
    34. m_Tail.Add(New Point(m_Location.X + m_Size.Width / 2, m_Location.Y + m_Size.Height / 2))
    35. If p_ForceNS = 0 Or p_ForceWE = 0 Then Break(0.9)
    36. m_Speed.Add(Math.Abs(ForceWE) + Math.Abs(ForceNS))
    37. If m_Speed.Count >= 500 Then m_Speed.RemoveAt(0)
    38. If m_Tail.Count >= 500 Then m_Tail.RemoveAt(0)
    39. End Sub
    Bilder
    • SlyBot.jpg

      185,3 kB, 814×679, 157 mal angesehen