Collision Detection bei Kugeln (oder Kreisen)

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von Bartosz.

    Collision Detection bei Kugeln (oder Kreisen)

    Hi all, ich hoffe, ihr hattet schöne Weihnachtstage.

    Ich habe eine Klasse
    Kugel, die die wichtigsten physikalischen Eigenschaften einer normalen Kugel enthält.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Windows
    2. Public Class Kugel
    3. Public velocity As Vector
    4. ''' <summary>
    5. ''' m [g] = ρ [g/cm^3] * V [cm^3] mit ρ = 2.6989
    6. ''' </summary>
    7. Public mass As Double
    8. Public Property Startposition As Vector
    9. ''' <summary>
    10. ''' Impuls
    11. ''' </summary>
    12. Public momentum As Vector = New Vector(0, 0)
    13. Public Farbe As Color
    14. ''' <summary>
    15. ''' [cm]
    16. ''' </summary>
    17. Public Durchmesser As Double
    18. Public Property akt_Position As Vector
    19. Public Property steht_zwar__aber_nicht_auf_Startposition As Boolean = False
    20. Public Property steht As Boolean = True
    21. Public Property Span As TimeSpan
    22. Public Property DateBeginn As Date
    23. Public Sub New(ByVal posX As Double, ByVal posY As Double, ByVal R As Byte, ByVal G As Byte, ByVal B As Byte, ByVal Du As Double)
    24. Me.Startposition = New Vector(posX, posY)
    25. Me.Farbe = Color.FromArgb(R, G, B)
    26. Me.Durchmesser = Du
    27. Me.mass = 4.0 / 3.0 * Math.PI * Math.Pow((Du / 2.0), 3) * 2.6989
    28. End Sub
    29. End Class


    Von dieser Klasse gibt es mehrere Instanzen in der
    Public alleKugelnListe As New List(Of Kugel). Im Form_MainLoad- und Button_start_sub werden die Instanzen initialisiert.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub Form_Main_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. Me.BackColor = Color.FromArgb(141, 161, 148)
    3. Button_Start.BackColor = Color.FromArgb(195, 219, 204)
    4. Button_Stopp.BackColor = Color.FromArgb(195, 219, 204)
    5. Me.Location = New Drawing.Point(0, CInt(Math.Round(Screen.PrimaryScreen.WorkingArea.Height / 2.0 - Me.Size.Height / 2.0, 0)))
    6. alleKugelnListe.Add(New Kugel(15, 15, 0, 229, 255, 30.0))
    7. alleKugelnListe.Add(New Kugel(1275.0 / 2.0, 15, 8, 0, 255, 30.0))
    8. alleKugelnListe.Add(New Kugel(1275.0 / 2.0, 360, 255, 0, 230, 30.0))
    9. alleKugelnListe.Add(New Kugel(1275.0 / 3.0, 500, 60, 242, 5, 30.0))
    10. alleKugelnListe.Add(New Kugel(1275.0 / 4.0, 600, 0, 0, 0, 50.0))
    11. End Sub
    12. Private Async Sub Button_Start_Click(sender As Object, e As EventArgs) Handles Button_Start.Click
    13. Await Task.Run(Sub() alle_Kugeln_initialisieren())
    14. End Sub
    15. Private Sub alle_Kugeln_initialisieren()
    16. alleKugelnListe(0).velocity = New Vector(110.0, 0.0)
    17. alleKugelnListe(0).steht = False
    18. alleKugelnListe(0).akt_Position = alleKugelnListe(0).Startposition
    19. alleKugelnListe(0).DateBeginn = Date.Now
    20. alleKugelnListe(1).velocity = New Vector(0.0, 0.0)
    21. alleKugelnListe(1).steht = True
    22. alleKugelnListe(1).akt_Position = alleKugelnListe(1).Startposition
    23. alleKugelnListe(2).velocity = New Vector(0.0, 0.0)
    24. alleKugelnListe(2).steht = True
    25. alleKugelnListe(2).akt_Position = alleKugelnListe(2).Startposition
    26. alleKugelnListe(3).velocity = New Vector(-120.0, 0.0)
    27. alleKugelnListe(3).steht = False
    28. alleKugelnListe(3).akt_Position = alleKugelnListe(3).Startposition
    29. alleKugelnListe(3).DateBeginn = Date.Now
    30. alleKugelnListe(4).velocity = New Vector(-130.0, -130.0)
    31. alleKugelnListe(4).steht = False
    32. alleKugelnListe(4).akt_Position = alleKugelnListe(4).Startposition
    33. alleKugelnListe(4).DateBeginn = Date.Now
    34. bereit = True
    35. Me.Invoke(Sub() Timer1.Start())
    36. Me.Invoke(Sub() Timer2.Start())
    37. End Sub


    Nun gibt es einen Timer_Tick, der regelmäßig

    VB.NET-Quellcode

    1. Private Async Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    2. Await Task.Run(Sub() PictureBox1.Invalidate())
    3. End Sub

    aufruft, sodass PictureBox1_Paint aufgerufen wird. Hierin werden ständig alle Eigenschaften berechnet, unter anderem der Geschwindigkeitsvektor und der zurückgelegte Weg. bereit ist nur ein Notbhelf, um dafür zu sorgen, dass er beim Programmstart (Formaufbau) nicht das alles ausführt.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. If bereit AndAlso alleKugelnListe.Count > 0 Then
    2. For i As Integer = 0 To alleKugelnListe.Count - 1 Step 1
    3. '...
    4. '...
    5. '...

    Hierin soll es nun eine Kollisionserkennung geben. Wie kann ich eine Funktion schreiben, die prüft, ob 2 Kugeln sich treffen? Das Problem ist, dass zur Laufzeit nicht immer klar ist, wie viele Rechtecke es zur Zeit gibt, und es muss außerdem egal sein, aus welcher Richtung sie sich treffen. Sobald sich 2 Kugeln treffen, muss ich wissen, welchen beiden Kugeln (oder deren Um-Rechtecke) es sind. Und ich bräuchte den genauen Berührungspunkt, um mit dem Skalarprodukt berechnen zu können, wohin beide Kugeln abdriften.
    en.wikipedia.org/wiki/Momentum


    PS: Ich brauche nicht das Abprallen an den 4 Wänden. Das habe ich schon.
    Bilder
    • 1.jpg

      47,19 kB, 1.437×767, 56 mal angesehen
    • 2.jpg

      48,87 kB, 1.441×766, 56 mal angesehen
    • 3.jpg

      45,98 kB, 1.443×766, 53 mal angesehen
    • 4.jpg

      49,46 kB, 1.439×772, 51 mal angesehen
    Ein Task.Run in einem Timer, das die PictureBox "invalidiert"? Ein bisschen zu viel Overhead, findest du nicht?
    (Ist doch illegal, weil threadübergreifend ein Control-Element aktualisiert wird?)

    Ferner rate ich dir Namen von Eigenschaften besser zu wählen (soweit ich weiß gehört camelCase, oder "abc_def_ghi" nicht zu .NET-Konvention respektive Properties).

    Zurück zu deiner Frage:

    Du könntest vielleicht event-basiert vorgehen: In deinen Kugeln ein Event deklarieren (z.B. OnCollision), die dann jeweils bei kollidierenden Kugeln (mit entsprechenden Argumenten) aufgerufen wird.
    Ferner bedingt dein Szenario einen Aufwand O(n^2), das heißt es werden zwei for-Schleifen benötigt.
    Du musst selbstverständlich in der For-Schleife den Fall ausschließen, dass eine Kollisionsprüfung auf die momentan zu untersuchenden Kugel erfolgt.

    Grüße.
    _
    Und Gott alleine weiß alles am allerbesten und besser.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „φConst“ ()

    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!
    @RodFromGermany Wow, das ist klasse. Wenn ich mir die 2 Threads durchlese und den Code anschaue, dann sehe ich, dass dort sehr viel Arbeit drin steckt. Ich finde es trotzdem erstaunlich, wie „einfach“ der Code gehalten wurde, wenn du verstehst, was ich meine. Ich setz diesen Thread erstmal auf erledigt, da ich mit deiner Solution lernen werde.

    @φConst Das mit dem
    Task.Run in einem Timer, das die PictureBox "invalidiert"
    entstammt einer Lösung hier im Forum, die mir mal gezeigt wurde (ich weiß aber nicht mehr, wo). Bis jetzt gab's nie Probleme. @all Kann da jemand etwas zu sagen?
    Ferner rate ich dir Namen von Eigenschaften besser zu wählen
    Ja, das auf jeden Fall. Ich möchte dazu sagen, dass ich gestern Nachmittag angefangen habe und ich zuerst dachte, dass es nur 2 Fälle gibt: 1) Kugel rollt frei und 2) Kugel kollidiert. Dann bemerkte ich, dass es viel mehr Fäle gibt, die behandelt werden müssen. Und da ging das Experimentieren los.